

import java.awt.event.*;
import java.util.Calendar;
import java.util.Date;
import javax.swing.*;
import com.genlogic.*;

//////////////////////////////////////////////////////////////////////////
public class GlgGraphCrossWire extends GlgJBean implements ActionListener
{
   //////////////////////////////////////////////////////////////////////////
   // The main demo class
   //////////////////////////////////////////////////////////////////////////
   static final long serialVersionUID = 0;

   boolean
     IsReady = false,
     ShowYPosition = true;
   Timer timer = null;
   int
     UpdateInterval = 100,   // msec
     NumSamples = 100,
     SelectedSampleIndex = -1;
   Calendar calendar = Calendar.getInstance();
   long last_update;
   double XMin, XMax, YMax, YMin;
   GlgPoint
     point,
     world_point;
   GlgObject
     Viewport,
     NormalColor,
     SelectedColor;

   //////////////////////////////////////////////////////////////////////////
   public GlgGraphCrossWire()
   {
      super();

      // Set TraceHRef to activate the trace callback.
      SetTraceHRef( true );
   }

   //////////////////////////////////////////////////////////////////////////
   // Initializes simulation and starts updates.
   //////////////////////////////////////////////////////////////////////////
   public void ReadyCallback()
   {
      if( GetJavaLog() )
        PrintToJavaConsole( "Debug: Ready\n" );

      super.ReadyCallback();

      point = new GlgPoint();
      world_point = new GlgPoint();
      Viewport = GetViewport();

      // Set the initial number of samples, etc.
      SetDResource( "DataGroup/Factor", (double)NumSamples );
      SetSResource( "XLabelGroup/XLabel/String", "" ); // Initial value
      SetDResource( "XLabelGroup/Factor", 10. );       // Num labels and ticks 
      SetDResource( "XLabelGroup/MinorFactor", 10. );  // Num minor ticks
      SetDResource( "DataGroup/ScrollType", 0. );

      // Set the template's color to Local to allow coloring individual data
      // samples, store the color.
      SetDResource( "DataGroup/Marker/FillColor/Global", 0. );

      // Query colors to use for selection
      NormalColor = Viewport.GetResourceObject( "DataGroup/Marker/FillColor" );
      SelectedColor = Viewport.GetResourceObject( "Selection/FillColor" );

      // Make the level line and info display invisible
      SetDResource( "LevelObjectGroup/Visibility", 0. );
      SetDResource( "InfoDisplay/Visibility", 0. );
      SetDResource( "YFeedback/Visibility", ShowYPosition ? 1. : 0. );

      Update();

      XMin = Viewport.GetGResource( "LLPoint" ).x;
      XMax = Viewport.GetGResource( "LRPoint" ).x;
      YMax = Viewport.GetGResource( "ULPoint" ).y;
      YMin = Viewport.GetGResource( "LRPoint" ).y;

      if( timer == null )
      {
         timer = new Timer( UpdateInterval / 2, this );
         timer.setRepeats( true );
         timer.start();
      }

      IsReady = true;
   }

   //////////////////////////////////////////////////////////////////////////
   // For use as a stand-alone java demo
   //////////////////////////////////////////////////////////////////////////
   public static void main( final String arg[] )
   {
      SwingUtilities.
        invokeLater( new Runnable(){ public void run() { Main( arg ); } } );
   }

   //////////////////////////////////////////////////////////////////////////
   public static void Main( final String arg[] )
   {
      class DemoQuit extends WindowAdapter
      {
         public void windowClosing( WindowEvent e ) { System.exit( 0 ); }
      } 

      JFrame frame = new JFrame();

      frame.setResizable( true );
      frame.setSize( 600, 450 );
      frame.setLocation( 20, 20 );

      GlgGraphCrossWire graph = new GlgGraphCrossWire();

      frame.getContentPane().add( graph );

      frame.addWindowListener( new DemoQuit() );
      frame.setVisible( true );

      // Setting the viewport triggers the ReadyCallback which starts
        // updates.
      graph.SetViewport( GlgObject.LoadWidget( "line1_1.glg",
                                              GlgObject.FILE ) );
   }

   //////////////////////////////////////////////////////////////////////////
   // Updates the graph with random data
   //////////////////////////////////////////////////////////////////////////
   void UpdateGraphProc()
   {
      if( !IsReady || !isVisible() )
        return;

      long curr_time = System.currentTimeMillis();
      if( curr_time < last_update + UpdateInterval )
        return;

      last_update = curr_time;
      calendar.setTime( new Date( curr_time ) );

      String time_string = GetTimeString( calendar );

      // Push next data value
      SetDResource( "DataGroup/EntryPoint", Math.random() );
      
      // Store the time stamp with the datasample for selection.
      SetSResource( "DataGroup/TimeEntryPoint", time_string );

      // Push next label
      SetSResource( "XLabelGroup/EntryPoint", time_string );

      UpdateSelectionDisplay( SelectedSampleIndex );

      Update();
   }

   //////////////////////////////////////////////////////////////////////////
   public void TraceCallback( GlgObject viewport, GlgTraceData trace_info )
   {
      if( !IsReady || trace_info.viewport != Viewport )
        return;

      GlgPoint point = new GlgPoint();

      int event_type = trace_info.event.getID();
      switch( event_type )
      {
       case MouseEvent.MOUSE_MOVED:
         point.x = (double) (double) ((MouseEvent)trace_info.event).getX();
         point.y = (double) (double) ((MouseEvent)trace_info.event).getY();
         break;
        
       default: return;
      }      

      Viewport.ScreenToWorld( true, point, world_point );
      
      double x_rel_value = VALUE_TO_RELATIVE( XMin, XMax, world_point.x );
      double y_rel_value = VALUE_TO_RELATIVE( YMin, YMax, world_point.y );
      
      boolean out = false;
      if( x_rel_value < 0. )
      {
         x_rel_value = 0.;
         out = true;
      }
      else if( x_rel_value > 1. )
      {
         x_rel_value = 1.;
         out = true;
      }

      if( y_rel_value < 0. )
      {
         y_rel_value = 0.;
         out = true;
      }
      else if( y_rel_value > 1. )
      {
         y_rel_value = 1.;
         out = true;
      }
      
      Viewport.SetDResource( "XPosition", x_rel_value );
      if( ShowYPosition )
        Viewport.SetDResource( "YPosition", y_rel_value );
      if( out )
      {
         Viewport.SetDResource( "InfoDisplay/Visibility", 0. );       
         SelectSample( SelectedSampleIndex, false );   // Unselect
         Viewport.Update();	   
         return;
      }

      Viewport.SetDResource( "InfoDisplay/Visibility", 1. );

      int sample_index = (int) 
        RELATIVE_TO_NEW_RANGE( 0., NumSamples - 0.01, x_rel_value );

      UpdateSelectionDisplay( sample_index );
      Viewport.Update();
   }

   //////////////////////////////////////////////////////////////////////////
   void UpdateSelectionDisplay( int sample_index )
   {
      SelectSample( SelectedSampleIndex, false );   // Unselect old
      SelectSample( sample_index, true );           // Select new

      if( sample_index == -1 )
        return;

      double sel_value = 
        GetDResource( "DataGroup/Points/DataSample" + 
                     sample_index + "/Value" );

      String sel_time =
        GetSResource( "DataGroup/Markers/Marker" + 
                     sample_index + "/Time" );

      SetDResource( "InfoDisplay/Value", sel_value );
      SetSResource( "InfoDisplay/Time", sel_time );
   }

   //////////////////////////////////////////////////////////////////////////
   void SelectSample( int index, boolean select )
   {
      SelectedSampleIndex = index;

      if( index == -1 )
         return;

      String res_name = "DataGroup/Markers/Marker" + index + "/FillColor";
      if( select )
        Viewport.SetResourceFromObject( res_name, SelectedColor );
      else
        Viewport.SetResourceFromObject( res_name, NormalColor );
   }

   //////////////////////////////////////////////////////////////////////////
   double RELATIVE_TO_NEW_RANGE( double low, double high, double rel_value )
   {
      return ( (low) + ((high) - (low)) * rel_value );
   }

   //////////////////////////////////////////////////////////////////////////
   double VALUE_TO_RELATIVE( double low, double high, double value )
   {
      return ( high - low != 0. ? ((value) - (low)) / ((high) - (low)) : 0. );
   }

   //////////////////////////////////////////////////////////////////////////
   String GetTimeString( Calendar calendar )
   {
      int hour = calendar.get( Calendar.HOUR );
      int minute = calendar.get( Calendar.MINUTE );
      int second = calendar.get( Calendar.SECOND );

      return
        "" + hour + ( minute < 10 ? ":0" : ":" ) + minute + 
          ( second < 10 ? ":0" : ":" ) + second;
   }

   //////////////////////////////////////////////////////////////////////////
   public void ToggleShowY()
   {
      ShowYPosition = ( ShowYPosition ? false : true );

      SetDResource( "YPosition", 0. );
      SetDResource( "YFeedback/Visibility", ShowYPosition ? 1. : 0 );
   }

   //////////////////////////////////////////////////////////////////////////
   public void ToggleScroll()
   {      
      double ScrollType = GetDResource( "DataGroup/ScrollType" );
      SetDResource( "DataGroup/ScrollType", (double) 
                    ( ScrollType == (double) GlgObject.WRAPPED ?
                      GlgObject.SCROLLED : GlgObject.WRAPPED ) );
      Update();
   }

   //////////////////////////////////////////////////////////////////////////
   public void ToggleSpeed()
   {      
      UpdateInterval = ( UpdateInterval == 1000 ? 100 : 1000 );
      timer.setDelay( UpdateInterval / 2 );
   }

   //////////////////////////////////////////////////////////////////////////
   // Stops updates
   //////////////////////////////////////////////////////////////////////////
   public void stop()
   {
      if( timer != null )
      {
         timer.stop();
         timer = null;
      }

      IsReady = false;
      super.stop();
   }

   //////////////////////////////////////////////////////////////////////////
   // ActionListener method to use the bean as update timer's ActionListener.
   //////////////////////////////////////////////////////////////////////////
   public void actionPerformed( ActionEvent e )
   {
      if( timer != null )
        UpdateGraphProc();
   }

   //////////////////////////////////////////////////////////////////////////
   // Inner class for a Runnable interface.
   // Provides an interface between JavaScript and Java: invokes applet's 
   // methods in a synchronous way as required by Swing.
   //////////////////////////////////////////////////////////////////////////
   class GlgBeanRunnable implements Runnable
   {
      GlgGraphCrossWire bean;
      String request_name;
      int value;

      public GlgBeanRunnable( GlgGraphCrossWire bean_p, 
                             String request_name_p, int value_p )
      {
         bean = bean_p;
         request_name = request_name_p;
         value = value_p;
      }

      public void run()
      {
         if( request_name.equals( "ToggleSpeed" ) )
           bean.ToggleSpeed();
         else if( request_name.equals( "ToggleScroll" ) )
           bean.ToggleScroll();
         else if( request_name.equals( "ToggleShowY" ) )
           bean.ToggleShowY();
         else
           PrintToJavaConsole( "Invalid request name: " + 
                              request_name + "\n" );
      }
   }

   //////////////////////////////////////////////////////////////////////////
   // Provides an interface between JavaScript and Java: invokes applet's 
   // methods in a synchronous way as required by Swing.
   //////////////////////////////////////////////////////////////////////////
   public void SendRequest( String request_name, int value )
   {
      GlgBeanRunnable runnable = 
        new GlgBeanRunnable( this, request_name, value );

      SwingUtilities.invokeLater( runnable );
   }
}

