

import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.*;
import javax.swing.*;

// import java.text.*;
import com.genlogic.*;

//////////////////////////////////////////////////////////////////////////
public class GlgElectricCircuit extends GlgJBean implements ActionListener
{
   static final long serialVersionUID = 0;

   //////////////////////////////////////////////////////////////////////////
   // The main demo class
   //////////////////////////////////////////////////////////////////////////

   // Main class parameters.
   boolean IsReady = false;
   static boolean StandAlone = false;

   // If true, the resource path is used to animate resources of the drawing.
   // If false, stored resource ID is used to set resource directly with 
   // null path using the Extended API. 
   // Alternatively, tags may be used instead of resources.
     //
   final boolean USE_RESOURCE_PATH = false;

   Timer timer = null;
   final int UpdateInterval = 500;    // milliceconds
   boolean UpdateData = true;

   // Array of resources to update, queried from the drawing.
   GlgObject ResourceList;

   // Keeps info of a resource to update
   class SimulationResource
   {
      GlgObject object;
      int type;
      double range;
      String resource_path;
   }

   //////////////////////////////////////////////////////////////////////////
   public GlgElectricCircuit()
   {
      super();
   }

   //////////////////////////////////////////////////////////////////////////
   // Stops the update thread
   //////////////////////////////////////////////////////////////////////////
   synchronized public void stop()
   {
      StopUpdate();

      IsReady = false;
      super.stop();
   }

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

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

   //////////////////////////////////////////////////////////////////////////
   public void ToggleUpdates()
   {
      UpdateData = !UpdateData;
   }

   //////////////////////////////////////////////////////////////////////////
   // Starts the update thread
   //////////////////////////////////////////////////////////////////////////
   synchronized public void ReadyCallback()
   {
      if( GetJavaLog() )
        PrintToJavaConsole( "Debug: Ready\n" );

      super.ReadyCallback();

      InitializeSimulation();

      StartUpdate();
      IsReady = true;      
   }

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

   public static void Main( String arg[] )
   {
      String filename = "electric_circuit.g";

      class DemoQuit extends WindowAdapter
      {
         public void windowClosing( WindowEvent e ) { System.exit( 0 ); }
      } 

      int arg_length = Array.getLength( arg );
      int i = 0;

      // Parse options
      while( i < arg_length )
      {
         if( arg[ i ].equals( "-drawing" ) )
         {
            ++i;
            if( i >= arg_length )
              error( "Missing filename after -drawing option", true );
            filename = arg[i];
         }
      }

      GlgObject.Init();

      Frame frame = new Frame();
      frame.setResizable( true );
      frame.setSize( 800, 600 );
      frame.addWindowListener( new DemoQuit() );

      GlgElectricCircuit circuit = new GlgElectricCircuit();
      GlgElectricCircuit.StandAlone = true;

      frame.add( circuit );
      frame.show();

      // Setting the viewport triggers the ReadyCallback which starts
      // the update thread.
      //
      circuit.SetViewportExt( GlgObject.LoadWidget( filename, GlgObject.FILE ) );
   }

   //////////////////////////////////////////////////////////////////////////
   // Updates display with simulated data.
   //////////////////////////////////////////////////////////////////////////
   void UpdateCircuit()
   {
      if( ResourceList == null )
        return;

      int size = ResourceList.GetSize();
      for( int i=0; i<size; ++i )
      {
         SimulationResource resource = (SimulationResource)
           ResourceList.GetElement( i );

         if( resource.type != GlgObject.D ) 
           continue;      // Update only resources of D type
             
         double value = resource.range * Math.random();
         if( USE_RESOURCE_PATH )
           // Use resource path.
           SetDResource( resource.resource_path, value );
         else
           // Use stored resource ID with null path to set the resource 
             // directly using the Extended API.
           resource.object.SetDResource( null, value );
         Update();
      }      
   }

   //////////////////////////////////////////////////////////////////////////
   // This callback is invoked when user selects some object in the drawing
   // with the mouse. In this program, it's used to blink the moving object
   // (named "CatchMe") if user selects it with the mouse.
   //////////////////////////////////////////////////////////////////////////
   public void SelectCallback( GlgObject viewport, Object[] name_array,
                              int button )
   {
      super.SelectCallback( viewport, name_array, button );

      // Handle mouse selection here
   }

   //////////////////////////////////////////////////////////////////////////
   // This callback is invoked when user interacts with input objects in GLG
   // drawing. 
   //////////////////////////////////////////////////////////////////////////
   public void InputCallback( GlgObject viewport, GlgObject message_obj )
   {
      String
        origin,
        format,
        action;

      super.InputCallback( viewport, message_obj );

      origin = message_obj.GetSResource( "Origin" );
      format = message_obj.GetSResource( "Format" );
      action = message_obj.GetSResource( "Action" );

      // Handle window closing if run stand-alone
      if( format.equals( "Window" ) && action.equals( "DeleteWindow" ) )
        System.exit( 0 );

      // Handle user input here
      if( format.equals( "Button" ) )
      {
         if( action.equals( "Activate" ) )
         {
            if( origin.equals( "Resources" ) )
              PrintResources();
            else if( origin.equals( "ToggleUpdates" ) )
              ToggleUpdates();
         }
      }
   }

   //////////////////////////////////////////////////////////////////////////
   // Prints resources of the drawing to use for animation (resources whose
   // name starts with the "#" character.
   //////////////////////////////////////////////////////////////////////////
   void PrintResources()
   {
      if( ResourceList == null )
      {
         System.out.println( "Found no resources to update!" );
         return;
      }

      System.out.println( "Resource list for updates: resource_path type" );
      
      int size = ResourceList.GetSize();
      for( int i=0; i<size; ++i )
      {
         SimulationResource resource = (SimulationResource)
           ResourceList.GetElement( i );

         String data_type_str;
         switch( resource.type )
         {
          case GlgObject.D: data_type_str = "d"; break;
          case GlgObject.S: data_type_str = "s"; break;
          case GlgObject.G: data_type_str = "g"; break;
          default:
            error( "Invalid resource type.", false ); 
            continue;
         }
      
         System.out.println( resource.resource_path + " " + data_type_str );
      }

      System.out.println( "Resource list: Done." );
   }

   //////////////////////////////////////////////////////////////////////////
   // Creates a list of resources to animate defined in the drawing.
   // Queries the drawing to include all resources of interest that are 
   // marked by having "#" as the first character of their names. 
   // Alternatively, tags may be used instead of resources.
   //////////////////////////////////////////////////////////////////////////
   GlgObject GetResourceList( GlgObject obj, String res_path, GlgObject list )
   {
      // Using only named resources in this example, no aliases.
      GlgObject res_list = obj.CreateResourceList( true, false, false );
      if( res_list == null )
        return list;
     
      int size = res_list.GetSize();
      for( int i=0; i<size; ++i )
      {
         GlgObject object = (GlgObject) res_list.GetElement( i );

         String name = object.GetSResource( "Name" );
         if( !name.startsWith( "#" ) )
           // We are interested only in resources that start with #
           continue;

         // Accumulate resource path.
         String new_path;
         if( res_path == null )
           new_path = name;
         else
           new_path = res_path + "/" + name;

         int object_type = object.GetDResource( "Type" ).intValue();

         // Data or attribute object: add to the list of resources to animate.
         if( object_type == GlgObject.DATA || 
            object_type == GlgObject.ATTRIBUTE )
         {
            SimulationResource resource = new SimulationResource();
            resource.object = object;
            resource.type = object.GetDResource( "DataType" ).intValue();
            resource.resource_path = new_path; 
            
            // Set range for animating the resource.
            // State resources may have ON (1) and OFF (0) values - 
            // use 1.3 as a range to simulate. Use range=100 for the rest 
            // of resources.
              //
            if( name.equals( "#State" ) )
              resource.range = 1.3;
            else
              resource.range = 1000.;
            
            // Create a list of does not yet exist.
            if( list == null )
              list = new GlgDynArray( GlgObject.GLG_OBJECT, 0, 0 );
            
            list.AddObjectToBottom( resource );
         }

         double has_resources = 
           object.GetDResource( "HasResources" ).intValue();

         // If object's HasResources=ON, recursively traverse all resources
           // inside it.
         if( has_resources == 1. )
           list = GetResourceList( object, new_path, list );
      }
      return list;
   }

   //////////////////////////////////////////////////////////////////////////
   // Creates a list of resources to animate.
   //////////////////////////////////////////////////////////////////////////
   void InitializeSimulation()
   {
      ResourceList = GetResourceList( GetViewportExt(), null, null );
      if( ResourceList == null )
        error( "No resources to animate.", false );
   }

   //////////////////////////////////////////////////////////////////////////
   static void error( String string, boolean quit )
   {
      System.out.println( string );

      // Thread.currentThread().dumpStack();

      if( quit )
        System.exit( 0 );
   }

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

