/*
 * TableApplication.java    1.0 97/08/28
 *
 * Copyright (c) 1997 Netscape Communications Corporation
 *
 * Netscape grants you a non-exclusive, royalty free, license to use,
 * modify and redistribute this software in source and binary code form,
 * provided that i) this copyright notice and license appear on all copies of
 * the software; and ii) Licensee does not utilize the software in a manner
 * which is disparaging to Netscape.
 *
 * This software is provided "AS IS," without a warranty of any kind.
 * See the CDK License Agreement for additional terms and conditions.
 * created: 2/97 cls
 */

package netscape.peas;

import netscape.application.*;


import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import netscape.palomar.widget.layout.*;

public class TableApplication extends Application implements Target, Row, Table {

    public void init() {

        super.init();
        createViews();

  	}   // constructor



  	private void createViews() {

        // create a grid view
        moGridView = new GridView( this );

         // create a scroll group to stick it in
	    mainRootView().setLayoutManager(new MarginLayout(0,0,0,0));

        ScrollGroup scrollGroup = new ScrollGroup();
        scrollGroup.setHasVertScrollBar(true);
        scrollGroup.setVertScrollBarDisplay(ScrollGroup.AS_NEEDED_DISPLAY);
        scrollGroup.setBuffered(true);

        scrollGroup.setContentView(moGridView);

        mainRootView().addSubview(scrollGroup);
	    mainRootView().layoutView(0,0);
	    moGridView.layoutView(0,0);

  	}

    private void clearView( boolean bDraw ) {
        moGridView.removeAll();
        if ( bDraw ) {
            mainRootView().draw();
        }
    }


    private void removeViewRow( int iRow ) {

        moGridView.removeRowAt( iRow );
        moGridView.sizeTo();
        mainRootView().draw();

    }


    private void setViewColumnNames( String[] aColumnNames, int[] aColumnWidths, boolean bDraw ) {
        //System.out.println( "TA.setViewColumnNames + " );
        moGridView.setColumnInfo( aColumnNames.length, aColumnNames, aColumnWidths );

        //System.out.println( "TA.setViewColumnNames A " );

        moGridView.sizeTo();
        //System.out.println( "TA.setViewColumnNames B " );
        mainRootView().layoutView( 0, 0 );
        //System.out.println( "TA.setViewColumnNames C " );

        if ( bDraw ) {
        //System.out.println( "TA.setViewColumnNames D " );
            mainRootView().draw();
        }
        //System.out.println( "TA.setViewColumnNames - " );

    }

    private void addRowToView( Object[] aValues, boolean bDraw ) {

         moGridView.addRow( aValues );
         moGridView.sizeTo();
         if ( bDraw ) {
            mainRootView().draw();
         }
    }

    private void drawView() {
        mainRootView().draw();
    }


/*
    private void addRows() {
        Object[] aValues = { "George", "333 Elm Street", "555-3453" };
        for ( int i = 1; i <100; i++ ) {
            aValues[1] = i + " Elm Street";
            moGridView.addRow( aValues );
        }
        moGridView.sizeTo();
    }
*/


    public void setViewCurrentRow( int iNewCurrentRow ) {

        moGridView.setCurrentRow( iNewCurrentRow );
    }


	public void performCommand(String command, Object data)
    {
        //System.out.println( "performCommand + " + command );
		if (command.equals("exit")) {
			System.exit(0);
		} else if ( "change".equals( command ) ) {

		    // get row and column from data's position in gridview
            int iIndex = moGridView.getIndex( data );
            int iNumColumns = moTableSupport.getNumColumns();
            int iRow = iIndex / iNumColumns;
            int iColumn = iIndex % iNumColumns;

            String sNewValue = ((TextField)data).stringValue();
            setTableValue( iRow, iColumn, sNewValue, null );

        } else if ( "startFocus".equals( command ) ) {

		    // get row and column from data's position in gridview
            int iIndex = moGridView.getIndex( data );
            int iNumColumns = moTableSupport.getNumColumns();
            int iRow = iIndex / iNumColumns;
            int iColumn = iIndex % iNumColumns;

            doSetCurrentRowNumber( iRow, true, false, null );
            setViewCurrentRow( iRow );


        }
    }

    /**
     *  This method is called by this TableApplication, as well as others to
     *  set our "model".  It is "overridden" here so we can update our
     *  view when other actions (e.g. events) cause our model to be
     *  changed for us by TableSupport.
     *
     */
    public void setTableValue( int iRow, int iColumn, Object oNewValue, NotifyList oNotify ) {

        moTableSupport.setTableValue( iRow, iColumn, oNewValue, oNotify );

        if ( null != oNotify ) {

            // !! we're assuming that if oNotify is null then we changed it ourselves, and do not need to update... !!
            if ( moTableSupport.isValidRowNumber( iRow ) && moTableSupport.isValidColumnNumber( iColumn ) ) {
                int iIndex = iRow*getNumColumns() + iColumn;
                TextField oTextField = moGridView.getField( iIndex );
                oTextField.setStringValue( oNewValue.toString() );
                oTextField.draw();
            }
        }
    }

    //-----------------------------------------------------------------------------------------------------------
    // GetInterface interface

    /**
     * Return an object which implements this specified (fully qualified) interface name.
     * If this interface is not implemented, return null.
     */
    public Object getInterface( String sInterfaceName ) {
        return moTableSupport.getInterface( sInterfaceName );
    }

    //-----------------------------------------------------------------------------------
    // Row interface

    public void setColumnNames( String[] aColumnNames, int[] aColumnWidths, NotifyList oNotify ) {
        moTableSupport.setColumnNames( aColumnNames, aColumnWidths, oNotify );
    }

    // end of Row interface
    //-----------------------------------------------------------------------------------


    //-----------------------------------------------------------------------------------------------------------
    // RowProvider interface

	/**
	 * Return the number of columns.
	 */
	public int getNumColumns() {
	    return moTableSupport.getNumColumns();
    }

    /**
     * Return an array of column names
     */
    public String[] getColumnNames() {
	    return moTableSupport.getColumnNames();
    }

    /**
     * Return an array of column widths
     */
    public int[] getColumnWidths() {
        return moTableSupport.getColumnWidths();
    }

    /**
     * Given a column's index (0..NumColumns), return its name
     */
    public String getColumnName( int iColumnNumber ) {
	    return moTableSupport.getColumnName( iColumnNumber );
    }

    public Object[] getColumnValues() {
        return moTableSupport.getColumnValues();
    }

    /**
     * Given a column name, get its value.  This is a
     * "Dynamic-getter" method.  Unlike "normal" setter
     * methods ("void getFoo();"), dynamic-getters can be used
     * for properties whose names are known at compile time.
     */
    public Object getValueByName( String sColumnName ) {
	    return moTableSupport.getValueByName( sColumnName );
    }

    /**
     * Another flavor of "Dynamic-getter" method.
     * See @getValueByName.
     */
	public Object getValueByNumber( int iColumnNumber ) {
	    return moTableSupport.getValueByNumber( iColumnNumber );
    }

    /**
     * Row data providers implement addPropertyChangeListener and publish
     * it in the beaninfo as their OnPropertyChange "add listner" method.
     *
     * Technically, it does not need to be a member of this interface, but
     * hey, why not?
     */
    public void addPropertyChangeListener( PropertyChangeListener x ) {
	    moTableSupport.addPropertyChangeListener( x );
    }

    /**
     * Your basic remove listener method
     */
    public void removePropertyChangeListener( PropertyChangeListener x ) {
       moTableSupport.removePropertyChangeListener( x );
    }

    /**
     * Helper to fire property change.  Delegates to PropertyChangeSuppport member.
     */
    public void firePropertyChange(String propertyName,	Object oldValue, Object newValue) {
        moTableSupport.firePropertyChange( propertyName, oldValue, newValue );
    }



    public void addRowChangeListener( RowChangeListener oRowReceiver ) {
       moTableSupport.addRowChangeListener( oRowReceiver );

    }

    public void removeRowChangeListener( RowChangeListener oRowReceiver ) {
       moTableSupport.removeRowChangeListener( oRowReceiver );

    }

    public void fireRowChange( int iChangeType, String sColumnAffected, Object oOldValue, Object oNewValue, RowProvider oRowProvider, NotifyList oNotify ) {
        moTableSupport.fireRowChange( iChangeType, sColumnAffected, null, null, oRowProvider, oNotify );
    }




    // end of RowProvider interface
    //-----------------------------------------------------------------------------------------------------------



    //-----------------------------------------------------------------------------------------------------------
    // RowReceiver interface

    /**
     * This method is typically called when we have been "wired" as
     * a listener to another component's bound property
     */
	public void propertyChange( PropertyChangeEvent oEvent ) {
        moTableSupport.propertyChange( oEvent );
    }

    /**
     * Twister rowChange event. For us it means: "column info has changed".
     */
     public void rowChange( RowChangeEvent oEvent ) {
        moTableSupport.rowChange( oEvent );
     }


    /**
     * This method is called when column names or other inforation has changed.
     *
     * We are given a RowProvider into which we can call to get new column info.
     *
     *
     **/
    public boolean initializeColumnInfo( RowProvider oRowProvider ) {

        // let TableSupport handle the model part...
        if ( moTableSupport.initializeColumnInfo( oRowProvider ) ) {

            int iNumColumns = getNumColumns();
            String[] aColumnNames = getColumnNames();
            int[] aColumnWidths = getColumnWidths();


            //------------------------ take care of "view" part -----------//
            clearView( false );
            setViewColumnNames( aColumnNames, aColumnWidths, true );
            return true;
        }
        return false;

    } // initializeColumnInfo




    /**
     * Given a column's name, set its value to be this new value.
     * This is a "dymanic-setter". See @RowReceiver.getValueByName
     */
    public void setValueByName( String sColumnName, Object oNewValue) {
        setValueByName( sColumnName, oNewValue, null );
    }

    public void setValueByName( String sColumnName, Object oNewValue, NotifyList oNotify ) {
        moTableSupport.setValueByName( sColumnName, oNewValue, oNotify );

    } // setValueByName



    /**
     * Different flavor "dynamic-setter".  See @setValueByName.
     */
    public void setValueByNumber( int iColumnNumber, Object oNewValue, NotifyList oNotify ) {
        moTableSupport.setValueByNumber( iColumnNumber, oNewValue, oNotify );
    }

    // end of RowReceiver interface
    //-----------------------------------------------------------------------------------------------




    //-----------------------------------------------------------------------------------------------
    // Table interface

    //------------
    // table  provider
    public RowProvider getCurrentRowValues() {
        return moTableSupport.getCurrentRowValues();
    }

    public Table getTable() {
	    return this;
    }

    public void addTableChangeListener( TableChangeListener oListener ) {
	    moTableSupport.addTableChangeListener( oListener );
    }

    public void removeTableChangeListener( TableChangeListener oListener ) {
	    moTableSupport.removeTableChangeListener( oListener );
    }

    public void fireTableChange( int iChangeType, int iRowAffected, int iColumnAffected, Object oNewValue, TableProvider oTableProvider, NotifyList oNotify ) {
        moTableSupport.fireTableChange( iChangeType, iRowAffected, iColumnAffected, oNewValue, oTableProvider, oNotify );
    }

    public Object[] getNonCurrentRowValues( int iRowNumber ) {
        return moTableSupport.getNonCurrentRowValues( iRowNumber );
    }


    public void updateCurrentRow() {
        moTableSupport.updateCurrentRow();
    }

    public void doUpdateRow( Object[] aColumnNames, Object[] aNewValues, Object[] aOldValues ) {
        moTableSupport.doUpdateRow( aColumnNames, aNewValues, aOldValues );
    }


    public boolean isCurrentRowChanged() {
        return moTableSupport.isCurrentRowChanged();

    }



    //-------------------------------------------------
    // table receiver

	public void tableChange( TableChangeEvent oEvent ) {

        //System.out.println( "TAn.TableChange( ChangeType = "+oEvent.getChangeType()+" ) " );

        switch ( oEvent.getChangeType() ) {

            case TableChangeEvent.ROW_DELETED: {
                moTableSupport.tableChange( oEvent );

                // update our view
                removeViewRow( oEvent.getRowAffected() );
            }


            case TableChangeEvent.NON_CURRENT_ROW_CHANGED:
                break;

            case TableChangeEvent.ROW_INSERTED: {
                // first delegate to get model up to date
                moTableSupport.tableChange( oEvent );
                // now update our view

                Object[] aValues = oEvent.getTableProvider().getNonCurrentRowValues( oEvent.getRowAffected() );
                addRowToView( aValues, true );
                break;
            }

            case TableChangeEvent.ROWS_INSERTED: {
                // first delegate to get model up to date
                moTableSupport.tableChange( oEvent );

                // now add rows to view
                TableProvider oTableProvider = oEvent.getTableProvider();
                for ( int iRow = oEvent.getRowAffected(); iRow < oTableProvider.getNumRowsFetched(); iRow++ ) {
                    Object[] aValues = moTableSupport.getNonCurrentRowValues( iRow );
                    addRowToView( aValues, false );
                }
                drawView();
                break;
            }

            case TableChangeEvent.ALL_CHANGED:
                moTableSupport.tableChange( oEvent );
                break;

            case TableChangeEvent.NUM_FETCHED:
                moTableSupport.tableChange( oEvent );
                break;

            case TableChangeEvent.CURRENT_ROW_NUMBER_CHANGED:
                moTableSupport.tableChange( oEvent );
                break;

            case TableChangeEvent.CURRENT_ROW_CHANGED:
                moTableSupport.tableChange( oEvent );
                break;

            case TableChangeEvent.COLUMN_INFO_CHANGED:
                //System.out.println( "COLUMN_INFO_CHANGED + ..." );
                TableProvider oTableProvider = (TableProvider)oEvent.getTableProvider();

                int iNumColumns = oTableProvider.getNumColumns();
                //System.out.println( "NumColumns = " + iNumColumns );

                int[] aColumnWidths = oTableProvider.getColumnWidths();
                //System.out.println( "aColumnWidths = " + aColumnWidths );

                String[] aColumnNames = oTableProvider.getColumnNames();
                //System.out.println( "aColumnNames = " + aColumnNames );

                //System.out.println( "c" );


                for ( int i = 0; i < iNumColumns; i++ ) {
                    if ( null != aColumnNames ) {
                        //System.out.println( "aColumnNames["+i+"] = " + aColumnNames[i] );
                    }
                    if ( null != aColumnWidths ) {
                        //System.out.println( "aColumnWidths["+i+"] = " + aColumnWidths[i] );
                    }
                }
                //System.out.println( "d" );
                moTableSupport.tableChange( oEvent );
                //System.out.println( "COLUMN_INFO_CHANGED - ..." );

                break;

            case TableChangeEvent.TABLE_EMPTY:
                moTableSupport.tableChange( oEvent );
                break;

            default: {
                moTableSupport.tableChange( oEvent );
                break;
            }
        }
        //System.out.println( "TAn.TableChange  - ( ChangeType = " + oEvent.getChangeType() );

	}


    //----------------------
    // TableInfo


    /**
     * Getter method for this cursor's "query" property.
     */
	public String getQuery() {
	    return moTableSupport.getQuery();
	}

    /**
     * Setter method for this cursor's "query" property.
     */
	public void setQuery( String sNewQuery ) {
	    moTableSupport.setQuery( sNewQuery );
    }


    /**
     * Setter method for this cursor's "bufferSize" property.
     * this specifies the number of rows fetched when the query
     * is executed, and on successive fetchMoreRows() calls.
     *
     * A buffer size of 0 means that all rows are fetched before
     * control is returned from executeQuery.
     */
	public int getFetchBufferSize() {
	    return moTableSupport.getFetchBufferSize();
    }


    /**
     * Getter method for this cursor's "bufferSize" property.
     * this specifies the number of rows fetched when the query
     * is executed, and on successive fetchMoreRows() calls.
     */
	public void setFetchBufferSize( int iNewBufferSize ) {
	    moTableSupport.setFetchBufferSize( iNewBufferSize );
	}


	/**
	 * Evalutate the query, and fetch the appropriate
	 * amount of rows, based on the "bufferSize" property.
	 * If not all rows have been fetched, the rowsPending
	 * property will have a "true" value.  The numRowsFetched
	 * property will equal the number of rows satisfying the query
	 * only if the numRowsFetched property is "false".
	 */
	public void executeQuery() {
	    moTableSupport.executeQuery();
	}


	public void deleteRow( int iRowNumber, boolean bNotify ) {
	    moTableSupport.deleteRow( iRowNumber, bNotify );
    }

	public void deleteRow() {
	    moTableSupport.deleteRow();
    }

	public void addRow() {
	    moTableSupport.addRow();
    }



    /**
     * Getter method for "NumRowsFetched" property.  This
     * is the total number of rows satisfying the query if
     * and only if the "rowsPending" property is true.
     */
	public int getNumRowsFetched() {
	    return moTableSupport.getNumRowsFetched();
	}


    /**
     * Getter for the booelan "rowsPending" property.
     */
	public boolean getRowsPending() {
	    return moTableSupport.getRowsPending();
    }


    /**
     * Get more rows, if "rowsPending" property  is true, otherwise does nothing.
     */
    public void fetchMoreRows() {
        moTableSupport.fetchMoreRows();
    }


    /**
     * getCurrentRowNumber allows clients of this Table to get the
     * value of its its "currentRowNumber" property.  It ought to be
     * exposed in the beaninfo as the "read" method for this property.
     */
	public int getCurrentRowNumber() {
	    return moTableSupport.getCurrentRowNumber();
	}


    /**
     * setCurrentRowNumber allows clients of this Table adjust
     * its "currentRowNumber" property.  It ought to be exposed in
     * the beaninfo as the "write" method for this property.
     */
	public boolean setCurrentRowNumber( int iNewRowNumber ) {
       return moTableSupport.setCurrentRowNumber( iNewRowNumber );
    }


    /**
     * override to set our view, unless value *came* from view
     */
	public boolean doSetCurrentRowNumber( int iNewRowNumber, boolean bNotify, boolean bUpdateView, NotifyList oNotifyList ) {
	    //System.out.println( "TableApplication.doSetCurrentRowNumber row, notify, update, list = " + iNewRowNumber + " / " + bNotify + " / " + bUpdateView + " / " + oNotifyList );
	    if ( moTableSupport.doSetCurrentRowNumber( iNewRowNumber, bNotify, bUpdateView, oNotifyList ) ) {
	        if ( bUpdateView ) {
	            setViewCurrentRow( iNewRowNumber );
	        }
	        return true;
	    }
	    return false;
    }

    /**
     * Change the position of this cursor to the
     * next row.  Return false if there is none.
     */
	public boolean nextRow() {
       return moTableSupport.nextRow();
    }

    /**
     * Change the position of this cursor to the
     * next row.  Return false if there is none.
     */
	public boolean previousRow() {
       return moTableSupport.previousRow();
    }


    //
    //-----------------------------------------------------------------------------------------------

   /*
   private boolean isValidRowNumber( int iRowNumber ) {
        return ( 0 <= iRowNumber && iRowNumber < getNumRowsFetched() );
    }
*/




    //------------------------------------------------------------------------------
    // Member variables


    private TableSupport moTableSupport = new TableSupport( this, false );


    // table data support stuff
    //int miNumRows = 0;
    //private int miCurrentRowNumber = 0;
    //private Object maValues[][] = null;




    GridView moGridView = null;

 } //TableApplication
