import com.mathwizards.*;
import com.mathwizards.remote.*;
import graph.*;

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.net.URL;
import java.rmi.*;
import java.rmi.server.*;

public class LinearFMClientApplet extends Applet implements com.mathwizards.remote.Client, ActionListener , AdjustmentListener {

  com.mathwizards.remote.Engine server = null;
  GenericRemoteClient generic_remote_client = new GenericRemoteClient(this);

  Object server_flag = new Object();

  TextArea  console = new TextArea();

  G2Dint time_g = new G2Dint();
  G2Dint freq_g = new G2Dint();

  Panel sb_panel = new Panel();
  Panel plot_panel = new Panel();

  Scrollbar freq_sb  = new Scrollbar( Scrollbar.HORIZONTAL, 0 , 5 , 0 , 100 );
  Scrollbar mod_sb   = new Scrollbar( Scrollbar.HORIZONTAL, 0 , 5 , 0 , 100 );
  Scrollbar specz_sb = new Scrollbar( Scrollbar.HORIZONTAL, 0 , 5 , 0 , 100 );

  // Checkbox on_demand_cb = new Checkbox( "plot on demand" , true );
  double min_freq =  1.0;
  double max_freq = 10.0;
  double freq = min_freq;
  double range_freq = max_freq - min_freq;

  double min_mod =  0.0;
  double max_mod = 10.0;
  double mod = min_mod;
  double range_mod = max_mod - min_mod;

  double min_specz =  10.0;
  double max_specz = 200.0;
  double specz = min_specz;
  double range_specz = max_specz - min_specz;

  Label freq_l  = new Label( "Frequency" );
  Label mod_l   = new Label( "Modulation" );
  Label specz_l = new Label( "Spectral Zoom" );

  java.text.DecimalFormat decimal_format = 
     new java.text.DecimalFormat("###0.##");

  Label freq_n_l  = new Label( "(" + Double.MAX_VALUE + ")" , Label.RIGHT );
  Label mod_n_l   = new Label( "(" + Double.MAX_VALUE + ")" , Label.RIGHT );
  Label specz_n_l = new Label( "(" + Double.MIN_VALUE + ")" , Label.RIGHT );

  Button plot_b = new Button( "Plot" );

  public void init() {

    Dimension applet_size = getSize();
    int max_plot_size = 
      (int)(Math.min( applet_size.width / 2 , applet_size.height ) * 0.45);

    time_g.setBackground( Color.black );
    freq_g.setBackground( Color.black );

    Dimension d = new Dimension( max_plot_size , max_plot_size );
    time_g.setSize( d );
    freq_g.setSize( d );

    GridBagLayout gbl = new GridBagLayout();
    setLayout( gbl );

    plot_panel.setLayout( new GridBagLayout() );
    sb_panel.setLayout( new GridBagLayout() );

    GridBagConstraints gbc = new GridBagConstraints();

    /* add the plots panel */
    gbc.gridx = 0;
    gbc.gridy = 0;
    gbc.weightx = 1.0;
    gbc.weighty = 1.0;
    gbc.gridwidth = GridBagConstraints.REMAINDER;
    gbc.fill = GridBagConstraints.BOTH;
    add( plot_panel , gbc );

    /* add the scroll-bar panel */
    gbc.gridy = 1;
    gbc.weightx = 0.0;
    gbc.weighty = 0.0;
    gbc.fill = GridBagConstraints.HORIZONTAL;
    add( sb_panel , gbc );

    /* add the plot button */
    gbc.gridy = 2;
    gbc.fill = GridBagConstraints.NONE;
    gbc.gridwidth = 1;
    plot_b.addActionListener( this );
    add( plot_b , gbc );

    /* add the console */
    gbc.gridy = 3;
    gbc.fill = GridBagConstraints.REMAINDER;
    add( console , gbc );

    gbc.gridx = 0;    
    gbc.gridy = 0;
    gbc.insets = new Insets( 2 , 2 , 2 , 2 );
    gbc.fill = GridBagConstraints.BOTH;
    gbc.weightx = 1.0;
    gbc.weighty = 1.0;
    plot_panel.add( time_g , gbc );

    gbc.gridx = 1;
    gbc.gridy = 0;
    plot_panel.add( freq_g , gbc );

    gbc.gridx = 0;
    gbc.gridy = 0;
    gbc.insets = new Insets( 3 , 3 , 3 , 3 );
    gbc.weightx = 1.0;
    gbc.weighty = 0.0;
    gbc.fill = GridBagConstraints.HORIZONTAL;
    sb_panel.add( freq_sb , gbc );
    freq_sb.addAdjustmentListener( this );

    gbc.gridx = 0;
    gbc.gridy = 1;
    sb_panel.add( mod_sb , gbc );
    mod_sb.addAdjustmentListener( this );

    gbc.gridx = 0;
    gbc.gridy = 2;
    sb_panel.add( specz_sb , gbc );
    specz_sb.addAdjustmentListener( this );

    gbc.gridx = 1;
    gbc.gridy = 0;
    gbc.weightx = 0.1;
    sb_panel.add( freq_n_l , gbc );

    gbc.gridx = 1;
    gbc.gridy = 1;
    sb_panel.add( mod_n_l , gbc );

    gbc.gridx = 1;
    gbc.gridy = 2;
    sb_panel.add( specz_n_l , gbc );

    gbc.gridx = 2;
    gbc.gridy = 0;
    sb_panel.add( freq_l , gbc );

    gbc.gridx = 2;
    gbc.gridy = 1;
    sb_panel.add( mod_l , gbc );

    gbc.gridx = 2;
    gbc.gridy = 2;
    sb_panel.add( specz_l , gbc );

    double frac;

    freq_sb.setValue ( 100 / 3 );
    mod_sb.setValue  ( 100 / 3 );
    specz_sb.setValue( 100 / 2 );

    frac = getSBFrac( mod_sb );
    mod = min_mod + frac * range_mod;
    mod_n_l.setText( "(" + decimal_format.format(mod) + ")" );

    frac = getSBFrac( freq_sb );
    freq = min_freq + frac * range_freq;
    freq_n_l.setText( "(" + decimal_format.format(freq) + ")" );

    frac = getSBFrac( specz_sb );
    specz = min_specz + frac * range_specz;
    specz_n_l.setText( "(" + decimal_format.format(specz) + ")" );

  }

  private void Compute() {

    String cmd = "[ t , s , f , h ] = linearfm( " +
      freq + " , " + mod + " , " + specz + ")";

    try {

      server.eval( generic_remote_client , cmd );

      synchronized( server_flag ) {
	try {
	  server_flag.wait(60000L); // wait for a minute
	}
	catch ( Exception e ) {
	  e.printStackTrace();
	}
      }

      DoubleTensor t = server.getDoubleVar( generic_remote_client , "t" );
      DoubleTensor s = server.getDoubleVar( generic_remote_client , "s" );
      DoubleTensor f = server.getDoubleVar( generic_remote_client , "f" );
      DoubleTensor h = server.getDoubleVar( generic_remote_client , "h" );

      double[] data_s = new double[ t.data.length * 2 ];
      double[] data_h = new double[ f.data.length * 2 ];

      int j = 0;
      int i = 0;
      for ( i = 0 ; i < t.data.length * 2 ; i = i + 2 ) {
	data_s[i] = t.data[j];
	data_s[i+1] = s.data[j];
	j++;
      }

      j = 0;
      for ( i = 0 ; i < f.data.length * 2 ; i = i + 2 ) {
	data_h[i] = f.data[j];
	data_h[i+1] = h.data[j];
	j++;
      }

      time_g.detachDataSets();
      freq_g.detachDataSets();

      DataSet ds_s = new DataSet( data_s , t.data.length );
      DataSet ds_h = new DataSet( data_h , f.data.length );

      ds_s.linecolor = Color.yellow;
      ds_h.linecolor = Color.red;

      time_g.attachDataSet( ds_s );
      freq_g.attachDataSet( ds_h );

      time_g.repaint();
      freq_g.repaint();

    }
    catch ( VariableNotFoundException e ) { }
    catch ( TypeException e ) { }
    catch ( Exception e ) {
      System.out.println( e.toString() );
    }
  }

  boolean isConnected() {
    return generic_remote_client.isConnected();
  }

  public com.mathwizards.remote.Engine connect( URL base ) {
    return generic_remote_client.connect( base );
  }

  public void actionPerformed( ActionEvent e ) {

    Object source = e.getSource();

    if ( source != plot_b )
      return;

    if ( !isConnected() )
      server = connect( getDocumentBase() );
      
    if ( !isConnected() )
      return;

    Compute();

  }

  public void disconnect() {
    generic_remote_client.disconnect();
  }

  public void timeout( double minutes ) {
    System.out.println("applet time out by server after " +
		       minutes + " minutes of inactivity");
    server = null;
  }

  public void parseError( String error ) throws RemoteException {}
  public void varChanged( String var ) throws RemoteException {}
  public void displayResult( String str ) throws RemoteException {}

  public void quit() throws RemoteException {
    disconnect();
  }

  public void evalDone( String script , int remainingScripts ) throws RemoteException {
    synchronized (server_flag) {
      server_flag.notify();
    }
  }

  public void adjustmentValueChanged( AdjustmentEvent ae ) {

    Object source = ae.getSource();
    int type = ae.getAdjustmentType();
    double frac = 0.0;

    if ( source == mod_sb ) {
      
      frac = getSBFrac( mod_sb );
      mod = min_mod + frac * range_mod;
      mod_n_l.setText( "(" + decimal_format.format(mod) + ")" );

    } else if ( source == freq_sb ) {

      frac = getSBFrac( freq_sb );
      freq = min_freq + frac * range_freq;
      freq_n_l.setText( "(" + decimal_format.format(freq) + ")" );

    } else if ( source == specz_sb ) {

      frac = getSBFrac( specz_sb );
      specz = min_specz + frac * range_specz;
      specz_n_l.setText( "(" + decimal_format.format(specz) + ")" );

    }
  }

  private double getSBFrac( Scrollbar sb ) {
    return (double)sb.getValue() / 
      (double)( sb.getMaximum() - sb.getMinimum() );
  }

  public void ack() {}

  public void printStatusMessage( String str ) {
    console.append( str );
  }

}
