//==================================================================================================
// S i m u l a t i o n                                                                         Java
// S i m u l a t o r F r a m e
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ Library. This library is free software; you can redistribute it
// and/or modify it under the terms of the GNU Library General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
// the GNU Library General Public License for more details (http://www.gnu.org).

/*DESCRIPTION*/
/* This module provides a class to represent a frame where a simulator can display components.
   It is the simulator that controls the frame, and through this frame the user can also give
   orders to the simulator. */

// Package //---------------------------------------------------------------------------------------
package bpp.simulation;

// Importation //-----------------------------------------------------------------------------------
import bpp.graphic.*;
import bpp.standard.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.BoxLayout;
import javax.swing.JSplitPane;

// S i m u l a t o r F r a m e  Class //------------------------------------------------------------
/*CLASS SimulatorFrame */
/* Represents a frame where a simulator can display components. As the simulator controls the
   frame, the interface of this class reveals lots of details about the frame structure. To use
   this class, the user only needs to build an object, then the simulation loads automatically.
   <B>Except for the constructor, all the methods of this class should not be invoked directly by
   the user.</B> It is the simulator that call them. */
public class SimulatorFrame extends bpp.graphic.Frame implements ActionListener,Runnable {
 //-------------------------------------------------------------------------------Symbolic Constants
 protected static String CLOSE_ACTION = "close_action";
 protected static String PAUSE_ACTION = "pause_action";
 protected static String RESET_ACTION = "reset_action";
 protected static String START_ACTION = "start_action";
 //---------------------------------------------------------------------------------------Attributes
 protected Button atClose; // Button to close the frame.
 protected Button atPause; // Button to pause the simulation.
 protected Button atReset; // Button to reset the simulation.
 protected Button atStart; // Button to start the simulation.

 protected boolean atClosing;   // Indicates if the frame is closing.
 protected boolean atPaused;    // Indicates if the simulator is paused.
 protected boolean atRestarted; // Indicates if the simulator is restarted.
 protected boolean atStopped;   // Indicates if the simulator is stopped.

 protected IntegerIndicator atClock;        // Component to display the current time of the
                                            // simulation.
 protected IntegerIndicator atMeasureRange; // Component to display the time range for statistical
                                            // measures.
 protected IntegerIndicator atReplication;  // Component to display the number of the current
                                            // replication.
 protected ScrollBar        atSpeed;        // Component to display the simulation speed.
 protected StringIndicator  atStatus;       // Component to display the status of the simulator.
 protected ScrollBar        atTextSize;     // Component to display the size of the font used in
                                            // the frame.

 protected TextArea        atConsole;             // Console where messages are displayed.
 protected TextArea        atInspector;           // Inspector where properties of a visual
                                                  // component are displayed.
 protected VisualComponent atInspectedComponent;  // Visual component that is actually inspected.
 protected VisualComponent atSelectedComponent;   // Visual component that is actually selected.
 protected Container       atSimulationPanel;     // Panel where the simulation is displayed.
 protected ScrollPane      atSimulationScroll;    // Scroll panel that contains the simulation
                                                  // panel.

 protected String atExperimentFile; // File describing the simulation experiment.
 protected Font   atMonospacedFont; // Font used for the console and the inspector.
 protected Thread atTimeKeeper;     // Thread that maintains the applet running.
 protected String atTitle;          // Title of the frame.
 //-----------------------------------------------------------------------------------Native Methods
 private static native void startModule(SimulatorFrame agFrame,OutputStream agStream,
                                        String agExperimentFile);

 private static native void terminateModule();
 //--------------------------------------------------------------------------------------Constructor
 /*METHOD SimulatorFrame */
 /* Builds a simulator frame (with a given title) and starts a simulation according to a given
    experiment file. It is also possible to indicate if the frame is the master of the
    application. */
 public SimulatorFrame(String agTitle,String agExperimentFile,boolean agMaster) throws Exception {
  super(agTitle,agMaster,true);

  JSplitPane lcBottomPanel;
  Panel      lcButtonPanel;
  Panel      lcControlPanel;
  ScrollPane lcControlScroll;
  Panel      lcIndicatorPanel;
  Panel      lcLeftPanel;
  JSplitPane lcMainPanel;
  String     lcModuleFile;
  JSplitPane lcTopPanel;

  atClosing=false;
  atTitle=agTitle;

  // Font //
  atMonospacedFont=new Font("Monospaced",Font.PLAIN,12);

  // Main Layout //
  setLayout(new BorderLayout());
  lcMainPanel=new JSplitPane(JSplitPane.VERTICAL_SPLIT);
  lcTopPanel=new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
  lcBottomPanel=new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);

  add(lcMainPanel);
  lcMainPanel.setTopComponent(lcTopPanel);
  lcMainPanel.setBottomComponent(lcBottomPanel);
  lcMainPanel.setDividerSize(1);
  lcMainPanel.setResizeWeight(1);
  lcMainPanel.setContinuousLayout(true);

  // Top Panel //
  atSimulationScroll=new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS) {
   public Dimension getMinimumSize() { return (new Dimension(0,0)); } };

  lcLeftPanel=new Panel(new BorderLayout()) {
   public Dimension getMinimumSize() { return (new Dimension(0,0)); } };

  lcTopPanel.setRightComponent(atSimulationScroll);
  lcTopPanel.setLeftComponent(lcLeftPanel);
  lcTopPanel.setDividerSize(1);
  lcTopPanel.setResizeWeight(0);
  lcTopPanel.setContinuousLayout(true);

  // Bottom Panel //
  atConsole=new TextArea(null,10,80) {
   public Dimension getMinimumSize() { return (new Dimension(0,0)); } };

  atInspector=new TextArea(null,6,30) {
   public Dimension getMinimumSize() { return (new Dimension(0,0)); } };

  lcBottomPanel.setLeftComponent(atInspector);
  lcBottomPanel.setRightComponent(atConsole);
  lcBottomPanel.setDividerSize(1);
  lcBottomPanel.setResizeWeight(0);
  lcBottomPanel.setContinuousLayout(true);

  // Simulation Scroll //
  atSimulationPanel=new Panel(null);
  atSimulationScroll.add(atSimulationPanel);
  atSimulationPanel.setSize(2000,2000);
  atSimulationScroll.setSize(600,500);

  atSimulationPanel.addMouseListener(new MouseAdapter() {
   public void mouseClicked(MouseEvent agEvent) { setInspectedComponent(null); }
  });

  // Left Panel //
  lcIndicatorPanel=new Panel();
  lcIndicatorPanel.setLayout(new BoxLayout(lcIndicatorPanel,BoxLayout.Y_AXIS));
  lcControlScroll=new ScrollPane();
  lcLeftPanel.add(lcIndicatorPanel,BorderLayout.NORTH);
  lcLeftPanel.add(lcControlScroll,BorderLayout.CENTER);

  // Control Scroll //
  lcControlPanel=new Panel();
  lcControlScroll.setSize(140,200);
  lcControlScroll.add(new LayoutShell(lcControlPanel));

  // Indicator Panel //
  atClock=new IntegerIndicator(0,0,"Clock");
  atMeasureRange=new IntegerIndicator(0,0,"Measure Range");
  atReplication=new IntegerIndicator(0,1,"Replication");
  atStatus=new StringIndicator("Initializing...","Status");

  lcIndicatorPanel.add(new Panel());
  lcIndicatorPanel.add(new Label(agTitle,Label.CENTER));
  lcIndicatorPanel.add(new Panel());
  lcIndicatorPanel.add(new LayoutShell(atClock));
  lcIndicatorPanel.add(new LayoutShell(atMeasureRange));
  lcIndicatorPanel.add(new LayoutShell(atReplication));
  lcIndicatorPanel.add(new LayoutShell(atStatus));
  lcIndicatorPanel.add(new Panel());

  // Control Panel //
  lcButtonPanel=new Panel(new GridLayout(2,2,2,2));
  atSpeed=new ScrollBar(50,100,"Speed");
  atTextSize=new ScrollBar(4,7,"Text Size");

  atTextSize.addAdjustmentListener(new AdjustmentListener() {
   public void adjustmentValueChanged(AdjustmentEvent agEvent) {
    atMonospacedFont=new Font("Monospaced",Font.PLAIN,atTextSize.value()+8);
    atConsole.setFont(atMonospacedFont);
    atInspector.setFont(atMonospacedFont);
   }
  });

  lcControlPanel.setLayout(new BoxLayout(lcControlPanel,BoxLayout.Y_AXIS));
  lcControlPanel.add(new Panel());
  lcControlPanel.add(new LayoutShell(lcButtonPanel));
  lcControlPanel.add(new Panel());
  lcControlPanel.add(new Panel());
  lcControlPanel.add(atSpeed);
  lcControlPanel.add(new Panel());
  lcControlPanel.add(atTextSize);

  // Button Panel //
  atStart=new Button("Start");
  atStart.setActionCommand(START_ACTION);
  atStart.addActionListener(this);

  atPause=new Button("Pause");
  atPause.setActionCommand(PAUSE_ACTION);
  atPause.addActionListener(this);

  atReset=new Button("Reset");
  atReset.setActionCommand(RESET_ACTION);
  atReset.addActionListener(this);

  atClose=new Button("Close");
  atClose.setActionCommand(CLOSE_ACTION);
  atClose.addActionListener(this);

  lcButtonPanel.add(atStart);
  lcButtonPanel.add(atPause);
  lcButtonPanel.add(atReset);
  lcButtonPanel.add(atClose);

  // Console //
  atConsole.setEditable(false);
  atConsole.setFont(atMonospacedFont);

  // Inspector //
  atInspector.setEditable(false);
  atInspector.setFont(atMonospacedFont);
  setInspectedComponent(null);

  // Frame Resizing //
  setVisible(false);
  pack();

  Dimension lcFrameSize  = getSize();
  Dimension lcScreenSize = Toolkit.getDefaultToolkit().getScreenSize();

  setLocation((int)(lcScreenSize.getWidth()-lcFrameSize.getWidth())/2,
              (int)(lcScreenSize.getHeight()-lcFrameSize.getHeight())/2);

  setVisible(true);
  pack();

  // Simulation Experiment Reading //
  if (agMaster) {
   lcModuleFile=retrieveModuleFile(agExperimentFile);

   if (lcModuleFile.equals("")) {
    atConsole.append("[!] The experiment file isn't valid.\n");
    return;
   }

   atConsole.append("[>] Simulation Module '"+lcModuleFile+"' Loading...\n\n");
   System.loadLibrary("bpp_calendar");
   System.loadLibrary(lcModuleFile);

   atExperimentFile=agExperimentFile;

   atTimeKeeper=new Thread(this);
   atTimeKeeper.start();
  }
 }
 //-------------------------------------------------------------------------------RetrieveModuleFile
 protected String retrieveModuleFile(String agProjectFile) throws Exception {
  InputTextFile lcFile   = new InputTextFile(agProjectFile);
  String        lcString = "";

  while (!lcFile.isEnded() && !lcString.equals("module")) lcString=lcFile.readString();

  if (!lcFile.isEnded()) {
   lcString=lcFile.readString();

   if (lcString.equals("=")) lcString=lcFile.readString();
   else lcString="";
  }
  else lcString="";

  return (lcString);
 }
 //------------------------------------------------------------------------------------------Closing
 /*METHOD SimulatorFrame */ /* Indicates if the frame is closing. */
 public boolean closing() { return (atClosing); }
 //-------------------------------------------------------------------------------------------Paused
 /*METHOD SimulatorFrame */ /* Indicates if the simulator is paused. */
 public boolean paused() { return (atPaused); }
 //----------------------------------------------------------------------------------------Restarted
 /*METHOD SimulatorFrame */ /* Indicates if the simulator is restarted. */
 public boolean restarted() { return (atRestarted); }
 //------------------------------------------------------------------------------------------Stopped
 /*METHOD SimulatorFrame */ /* Indicates if the simulator is stopped. */
 public boolean stopped() { return (atStopped); }
 //--------------------------------------------------------------------------------------------Clock
 /*METHOD SimulatorFrame */ /* Returns the current time of the simulation. */
 public int clock() { return (atClock.value()); }
 //--------------------------------------------------------------------------------------Replication
 /*METHOD SimulatorFrame */ /* Returns the number of the current replication. */
 public int replication() { return (atReplication.value()); }
 //--------------------------------------------------------------------------------------------Speed
 /*METHOD SimulatorFrame */ /* Returns the speed of the simulation. */
 public int speed() { return (atSpeed.value()); }
 //-------------------------------------------------------------------------------------------Status
 /*METHOD SimulatorFrame */ /* Returns a string indicating the status of the simulator. */
 public String status() { return (atStatus.value()); }
 //------------------------------------------------------------------------------------------Console
 /*METHOD SimulatorFrame */ /* Returns the console where messages are displayed in the frame. */
 public TextArea console() { return (atConsole); }
 //-------------------------------------------------------------------------------InspectedComponent
 /*METHOD SimulatorFrame */ /* Returns the component that is actually inspected. */
 public VisualComponent inspectedComponent() { return (atInspectedComponent); }
 //--------------------------------------------------------------------------------SelectedComponent
 /*METHOD SimulatorFrame */ /* Returns the component that is actually selected. */
 public VisualComponent selectedComponent() { return (atSelectedComponent); }
 //----------------------------------------------------------------------------------------SetPaused
 /*METHOD SimulatorFrame */ /* Sets whether the simulator is paused. */
 public void setPaused(boolean agPaused) { atPaused=agPaused; }
 //-------------------------------------------------------------------------------------SetRestarted
 /*METHOD SimulatorFrame */ /* Sets whether the simulator is restarted. */
 public void setRestarted(boolean agRestarted) { atRestarted=agRestarted; }
 //---------------------------------------------------------------------------------------SetStopped
 /*METHOD SimulatorFrame */ /* Sets whether the simulator is stopped. */
 public void setStopped(boolean agStopped) { atStopped=agStopped; }
 //-----------------------------------------------------------------------------------------SetClock
 /*METHOD SimulatorFrame */ /* Sets the current time of the simulation. */
 public void setClock(int agClock) { atClock.setValue(agClock); }
 //----------------------------------------------------------------------------------SetRunningRange
 /*METHOD SimulatorFrame */ /* Sets the time range of the simulation. */
 public void setRunningRange(int agStart,int agEnd) {
  atClock.setValue(agStart);
  atClock.setMaximum(agEnd);
 }
 //----------------------------------------------------------------------------------SetMeasureRange
 /*METHOD SimulatorFrame */ /* Sets the time range for statistical measures. */
 public void setMeasureRange(int agStart,int agEnd) {
  atMeasureRange.setValue(agStart);
  atMeasureRange.setMaximum(agEnd);
 }
 //-----------------------------------------------------------------------------------SetReplication
 /*METHOD SimulatorFrame */ /* Sets the number of the current replication. */
 public void setReplication(int agReplication) { atReplication.setValue(agReplication); }
 //---------------------------------------------------------------------------------SetNbReplication
 /*METHOD SimulatorFrame */ /* Sets the number of replications in the experiment. */
 public void setNbReplication(int agNbReplication)
 { atReplication.setMaximum(agNbReplication); }
 //-----------------------------------------------------------------------------------------SetSpeed
 /*METHOD SimulatorFrame */ /* Sets the speed of the simulation. */
 public void setSpeed(int agSpeed) { atSpeed.setValue(agSpeed); }
 //----------------------------------------------------------------------------------------SetStatus
 /*METHOD SimulatorFrame */ /* Sets the string indicating the status of the simulator. */
 public void setStatus(String agStatus) { atStatus.setValue(agStatus); }
 //--------------------------------------------------------------------------------------SetTextSize
 /*METHOD SimulatorFrame */ /* Sets the size of the font used in the frame. */
 public void setTextSize(int agTextSize) { atTextSize.setValue(agTextSize); }
 //----------------------------------------------------------------------------SetInspectedComponent
 /*METHOD SimulatorFrame */
 /* Sets the component that is actually inspected, i.e. whose properties are displayed in the
    inspector. */
 public void setInspectedComponent(VisualComponent agComponent) {
  atInspectedComponent=agComponent;
  updateInspector();
 }
 //-----------------------------------------------------------------------------SetSelectedComponent
 /*METHOD SimulatorFrame */
 /* Sets the component that is actually selected, i.e. whose inside view is displayed in the
    frame. */
 public void setSelectedComponent(VisualComponent agComponent) {
  atSimulationScroll.setVisible(false);
  if (atSelectedComponent!=null) atSimulationPanel.remove(atSelectedComponent.inside());
  atSelectedComponent=agComponent;
  atSimulationPanel.add(atSelectedComponent.inside());
  atSimulationScroll.setScrollPosition(0,0);
  atSimulationScroll.setVisible(true);
 }
 //--------------------------------------------------------------------------------------SetSubtitle
 /*METHOD SimulatorFrame */ /* Sets the subtitle of the frame. */
 public void setSubtitle(String agSubtitle) { super.setTitle(atTitle+"   -   "+agSubtitle); }
 //----------------------------------------------------------------------------------UpdateInspector
 /*METHOD SimulatorFrame */ /* Updates the properties displayed in the inspector. */
 public void updateInspector() {
  if (atInspectedComponent==null)
   atInspector.setText("Click on an object\nto see its properties here...");
  else atInspector.setText(atInspectedComponent.properties());
 }
 //---------------------------------------------------------------------------------------------Main
 /*METHOD SimulatorFrame */
 /* Starts a simulation according to the experiment file given as argument. */
 public static void main(String agParameterS[]) {
  try { new SimulatorFrame("B + +  S i m u l a t o r",agParameterS[0],true); }
  catch (Exception agException) { System.out.println("[!] "+agException.getMessage()); }
 }
 //----------------------------------------------------------------------------------------------Run
 /*METHOD SimulatorFrame */ /* Runs the experiment. */
 public void run() {
  atStopped=false;
  atPaused=true;
  atClosing=false;
  atRestarted=false;

  startModule(this,new TextAreaStream(atConsole),atExperimentFile);
  terminateModule();
  if (atMaster) super.windowClosing(null);
 }
 //----------------------------------------------------------------------------------ActionPerformed
 /*METHOD SimulatorFrame */ /* Is invoked when a registered action is performed on the frame. */
 public void actionPerformed(ActionEvent agEvent) {
  String lcAction = agEvent.getActionCommand();

  if (lcAction.equals(START_ACTION)) atPaused=false;
  else if (lcAction.equals(PAUSE_ACTION)) atPaused=true;
  else if (lcAction.equals(RESET_ACTION)) {
   atStopped=true;
   atRestarted=true;
  }
  else if (lcAction.equals(CLOSE_ACTION)) windowClosing(null);
 }
 //------------------------------------------------------------------------------------WindowClosing
 /*METHOD SimulatorFrame */ /* Is invoked when the window is closing. */
 public void windowClosing(java.awt.event.WindowEvent agEvent) {
  setStatus("Closing...");
  atStopped=true;
  atClosing=true;
 }
}

// End //-------------------------------------------------------------------------------------------