
import byucc.jhdl.base.Browser;
import byucc.jhdl.base.HWSystem;
import byucc.jhdl.base.Wire;
import byucc.jhdl.base.WireValueException;
import byucc.jhdl.base.SimulatorCallback;
import byucc.jhdl.util.cli.CLInterpreter;

/** This class takes care of communications to the MonitorViewerPanel
 * for the MonitorBroker
 * @author Anthony L. Slade */
public class ViewIntermediary implements Browser, ButtonListener,
					 SimulatorCallback {

  /**
   * @param system the system with which this will be registered as a
   * SimulatorCallback 
   * @param interp the CLInterpreter to use to parse CLI commands */
  public ViewIntermediary( HWSystem system,
			   CLInterpreter interp,
			   MonitorViewerPanel panel,
			   int[] toCount,
			   int[] toBlock ) {
    system.addSimulatorCallback( this );
    _cli = interp;
    _view = panel;

    // obtain references of the wires from which the information for
    // the viewer will be extracted
    counts = new Wire[ toCount.length ];
    for ( int ci = 0; ci < toCount.length; ++ci )
      counts[ci] = (Wire)
	system.findNamed("Monitor/NetworkMonitor/ToCount/Counter"
			 +ci+"/count");
    blocks = new Wire[ toBlock.length ];
    for ( int bi = 0; bi < toBlock.length; ++bi )
      blocks[bi] = (Wire)
	system.findNamed("Monitor/NetworkMonitor/ToBlock/Counter"
			      +bi+"/count");
    countHit = (Wire)
      system.findNamed("Monitor/NetworkMonitor/ToCount/hit");
    blockHit = (Wire)
      system.findNamed("Monitor/NetworkMonitor/ToBlock/hit");
  }

  /** Tells the MonitorViewerPanel button how to be labeled.  This is
   * part of the ButtonListener interface
   * @return the button label */
  public String getActionString() {
    return "Clear Circuit Counters";
  }
  /** MonitorViewerPanel calls this method when the button is pressed.
   *  This is part of the ButtonListener interface.
   * @param command the command associated with the button */
  public void buttonPressed(String command) {
    _cli.parseCommand(MonitorBroker.COMMAND_CLEAR_COUNTERS);
  }

  /** SimulatorCallback method called when system is reset.  This
   * method causes the viewer to be reset. */
  public void simulatorReset() {
    _view.resetValues();
    _view.repaint();
    hitDetected = false;
  }
  /** SimulatorCallback method called on every clock edge.  This
   * method checks to see if the circuit has detected any hits.  If it
   * has, then a variable is set that indicates the viewer needs to be
   * updated. */
  public void simulatorUpdate( int cycle, int step ) {
    if ( 1 == step )
      return;
    try {
      if ( 1 == blockHit.get(this)
	   || 1 == countHit.get(this) )
	hitDetected = true;
    } catch ( WireValueException wve ) {
      System.err.println("Caught a WireValueException:"+wve.getMessage());
      wve.printStackTrace();
      System.exit(1);
    }
    // every so often on long simulation runs, update graph
    if ( 0 == cycle%UPDATE_FREQUENCY )
      refreshGraph( false );
  }
  /** SimulatorCallback method called at end of simulation runs.  This
   * will refresh the viewer if a hit has been detected. */
  public void simulatorRefresh( int cycle, int step ) {
    if ( 0 != step )
      refreshGraph( true );
  }

  /** If a hit has been detected recently, queries counters for values
   * and updates the graph, including calling for a repaint.
   * @param forceUpdate if true, then the viewer will be refreshed
   * whether the hitDetected field has been set or not. */
  private void refreshGraph( boolean forceUpdate ) {
    if ( hitDetected || forceUpdate ) {
      try {
	int total = 0;
	for ( int ci = 0; ci < counts.length; ++ci ) {
	  int hits = counts[ci].get(this);
	  total += hits;
	  _view.setGraphValue( ci, hits, false );
	}
	_view.setCountTotal( total );
	//System.err.println("Total Counts="+total);
	total = 0;
	for ( int bi = 0; bi < blocks.length; ++bi ) {
	  int hits = blocks[bi].get(this);
	  total += hits;
	  _view.setGraphValue( bi, hits, true );
	}
	_view.setBlockTotal( total );
	//System.err.println("Total Blocks="+total);
      } catch ( WireValueException wve ) {
	System.err.println("Caught a WireValueException:"+wve.getMessage());
	wve.printStackTrace();
	System.exit(1);
      }
      _view.repaint();
      hitDetected = false;
    }
  }

  /** A reference to the command line interpreter */
  private CLInterpreter _cli;
  /** A reference to the viewer */
  private MonitorViewerPanel _view;
  /** Used to remember if a hit has been detected during simulation */
  private boolean hitDetected;
  /** The wires of the hit counter outputs */
  private Wire[] counts;
  /** The wires of the hit counter outputs for blocks */
  private Wire[] blocks;
  /** The hit indicator for hits to count */
  private Wire countHit;
  /** The hit indicator for hits to count and block */
  private Wire blockHit;

  /** The maximum number of cycles between updates to the graph */
  private static final int UPDATE_FREQUENCY = 40;
}
