/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.diirt.javafx.tools;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

/**
 * Allows the user to select some form of data (e.g. a Gaussian wave or a sine
 * wave) and graphs it.
 * 
 * @author mjchao
 */
abstract public class BaseGraphApp extends Application {
    
    final private DataSelectionPanel pnlData = new DataSelectionPanel();
    private BaseGraphView pnlGraph;
    final private MessagePanel pnlError = new MessagePanel();
    
    abstract public BaseGraphView getGraphView();
    
    @Override
    public void start( Stage stage ) throws Exception {
	this.pnlGraph = getGraphView();
	
	BorderPane mainPanel = new BorderPane();
	    mainPanel.setTop( this.pnlData );
	    mainPanel.setCenter( this.pnlGraph );
	    mainPanel.setBottom( this.pnlError );
	    
	//allow components to shrink
	this.pnlData.setMinSize( 0 , 0 );
	this.pnlGraph.setMinSize( 0 , 0 );
	this.pnlError.setMinSize( 0 , 0 );
	
	
	
	Scene scene = new Scene( mainPanel );
	stage.setTitle( "diirt - BaseGraphApp" );
	stage.setScene( scene );
	stage.setWidth( 500 );
	stage.setHeight( 500 );
	stage.show();
    }
    
    /**
     * Reconnects to the data source with whatever data formula
     * is currently in use.
     */
    public void reconnect() {
	this.pnlData.reselect();
    }
    
    /**
     * Reconnects to the data source using the given data formula.
     * 
     * @param dataFormula the data formula to use
     */
    public void reconnect( String dataFormula ) {
	this.pnlGraph.reconnect( dataFormula );
    }
    
    /**
     * Adds the given data formulae to the list of acceptable data formulae
     * for this graphing application.
     * 
     * @param formulae the formulae to add
     */
    public void addDataFormulae( String... formulae ) {
	for ( String s : formulae ) {
	    DataFormula f = DataFormulaFactory.fromFormula( this.pnlGraph , s );
	    this.pnlData.addDataFormulae( f );
	}
    }
    
    /**
     * Adds the given data formulae to the list of acceptable data formulae
     * for this graphing application.
     * 
     * @param formulae the formula objects to add
     */
    void addDataFormulae( DataFormula... formulae ) {
	this.pnlData.addDataFormulae( formulae );
    }
    
    /**
     * Panel allowing the user to select some data to graph.
     */
    private class DataSelectionPanel extends BorderPane {
	
	/**
	 * the allowed data formulae are stored here and the user can
	 * select from this list
	 */
	final private ComboBox cboSelectData = new ComboBox();
	
	public DataSelectionPanel() {
	    this.setCenter( this.cboSelectData );
	    
	    //allow the combo box to stretch out and fill panel completely
	    this.cboSelectData.setMaxSize( Double.MAX_VALUE , Double.MAX_VALUE );
	    this.cboSelectData.setEditable( true );
	    
	    //allow the user to select no data
	    DataFormula defaultData = new DataFormula( "[Select]" ) {
		
		@Override
		public void onSelected() {
		    BaseGraphApp.this.reconnect( null );
		}
	    };
	    addDataFormulae( defaultData );
	    this.cboSelectData.setValue( defaultData );
	    
	    //watches for when the user selects a new data formula
	    this.cboSelectData.valueProperty().addListener( new ChangeListener< Object >() {

		@Override
		public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
		    if ( newValue instanceof DataFormula ) {
			((DataFormula) newValue).onSelected();
		    }
		    else if ( newValue instanceof String ) {
			DataFormulaFactory.fromFormula( pnlGraph , (String) newValue ).onSelected();
		    }
		}
		
	    });
	}
	
	/**
	 * Adds the given data formulae objects to the list of acceptable
	 * data formulae.
	 * 
	 * @param f the data formula objects to add
	 */
	final public void addDataFormulae( DataFormula... f ) {
	    this.cboSelectData.getItems().addAll( f );
	    
	    //we want the default data formula to not be [Select], so
	    //we update it to be the first formula in the list
	    if ( this.cboSelectData.getItems().size() >= 2 ) {
		this.cboSelectData.setValue( this.cboSelectData.getItems().get( 1 ) );
	    }
	}
	
	/**
	 * Reselects the currently selected data formula and reconnects to the 
	 * data source using this data formula.
	 */
	public void reselect() {
	    ((DataFormula)this.cboSelectData.getValue()).onSelected();
	}
    }
    
    /**
     * Panel for displaying any messages that may be received from the data
     * source.
     */
    private class MessagePanel extends BorderPane {
	
    }
}

@@ -715,16 +715,759 @@
 phs it.%0A
+ * %3Cp%3E%0A * To produce a custom graph app, e.g. a BubbleGraphApp, you first extend the%0A * BaseGraphApp. Inside this new class, you must create the BaseGraphView object%0A * templated with the correct Graph2DRendererUpdate for your graph type.%0A * Furthermore, you must define how to create expressions to be sent to%0A * PVManager for data. Inside the BaseGraphView object, you will also find a%0A * Graph2DExpression object which represents the data to be plotted on the graph.%0A * You can send updates to this if you choose to.%0A * %3Cp%3E%0A * The BaseGraphView object is created, so you can define the getGraphView()%0A * method. Now, everything should be all set up. Just override start(), %0A * add your data formulae, and your graph app should be complete.%0A
  * %0A * @

Document how to use BaseGraphApp