
import byucc.jhdl.apps.Viewers.cvt.cvtFrame;
import byucc.jhdl.apps.Stimulator.Stimulator;
import byucc.jhdl.base.Browser;
import byucc.jhdl.base.CellInterface;
import byucc.jhdl.base.HWSystem;
import byucc.jhdl.base.Node;
import byucc.jhdl.base.TestBench;
import byucc.jhdl.base.SimulatorCallback;
import byucc.jhdl.base.Wire;
import byucc.jhdl.base.WireValueException;
import byucc.jhdl.Logic.Logic;
import java.awt.BorderLayout;
import java.awt.Color;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

/** This is a basic test of the NetworkMonitor circuit.  This
 * TestBench simply creates an instance of the NetworkMonitor design
 * within CVT and also creates a GraphCanvas linked to it to give a
 * custom visualization of the functionality of the circuit.
 * @author Anthony L. Slade */
public class TestMonitor extends Logic
  implements TestBench, SimulatorCallback, Browser {

  /** The set width of a packet */
  public static final int PACKETWIDTH = 5;
  /** The low-order index of the address portion of the packets */
  public static final int ADDRESS_LO = 0;
  /** The high-order index of the address portion of the packets */
  public static final int ADDRESS_HI = 4;
  /** The pre-defined width of a counter */
  public static final int COUNTERWIDTH = 16;

  /** Reads in values from the command line to set the parameters of
   * the HitCounterArray to be tested.  Also creates a GraphCanvas
   * view of the HitCounterArray counts. */
  public static void main( String[] args ) {
    if ( 1 > args.length )
      showUsage(1);

    // parse user input
    ArrayList toCountList = new ArrayList(),
      toBlockList = new ArrayList();
    ArrayList list = toCountList;
    for ( int vi = 0; vi < args.length; ++vi ) {
      if ( "-".equals(args[vi]) ) {
	if ( list == toBlockList ) // 2nd "-" found :(
	  showUsage(1);
	else
	  list = toBlockList;
      } else
	list.add(args[vi]);
    }
    // fill in the int arrays with parsed integer values
    int[] toCount = new int[ toCountList.size() ];
    for ( int ind = 0; ind < toCountList.size(); ++ind )
      try {
	toCount[ind] = Integer.parseInt( (String)toCountList.get(ind) );
      } catch ( NumberFormatException nfe ) {
	showUsage(1);
      }
    int[] toBlock = new int[ toBlockList.size() ];
    for ( int ind = 0; ind < toBlockList.size(); ++ind )
      try {
	toBlock[ind] = Integer.parseInt( (String)toBlockList.get(ind) );
      } catch ( NumberFormatException nfe ) {
	showUsage(1);
      }

    // build the TestBench and the design circuit
    TestMonitor thc
      = new TestMonitor( new HWSystem(),
			 toCount,
			 toBlock );
    // put the design in a CVT frame
    new cvtFrame(thc);
    // build a custom view of the HitCounterArray counts
    GraphCanvas gc = thc.createGraphView();
    JLabel status = thc.createStatusLabel();
    JLabel l1 = new JLabel("NetworkMonitor Test");
    JPanel panel = new JPanel(new BorderLayout());
    panel.add(gc,BorderLayout.CENTER);
    panel.add(l1,BorderLayout.NORTH);
    panel.add(status,BorderLayout.SOUTH);
    JFrame graphFrame = new JFrame("Test NetworkMonitor");
    graphFrame.getContentPane().add(panel);
    graphFrame.setLocation(250,250);
    graphFrame.pack(); graphFrame.show();
  }

  /** Prints out the usage for the user to learn what the command line
   * should be
   * @param exitValue the return exit value for when the program
   * exits */
  public static void showUsage(int exitValue) {
    System.err.println( "TestMonitor usage:" );
    System.err.println( "  java TestMonitor "
			+"<count_value>+ - <block_value>+" );
    System.exit( exitValue );
  }

  /** Builds a TestHitCounters TestBench
   * @param hitValue the constant values to be embedded in the
   * HitCounter equals comparators
   * @param width the width of the input wire
   * @param countWidth the width of the HitCounter output wire */
  public TestMonitor( Node parent, int[] toCount, int[] toBlock ) {
    super( parent,"TestMonitor" );
    new NetworkMonitor( this,
			toCount, toBlock,
			packetsIn = wire( PACKETWIDTH, "packetsIn"),
			ADDRESS_LO, ADDRESS_HI,
			restart = wire( 1, "restart" ),
			packetsOut = wire( PACKETWIDTH, "packetsOut"),
			COUNTERWIDTH );
    Stimulator stim = new Stimulator( this );
    stim.addWire( packetsIn );
    stim.addWire( restart );
    // obtain references to wires that we want to watch
    counts = new Wire[ toCount.length ];
    for ( int ci = 0; ci < toCount.length; ++ci )
      counts[ci] = (Wire)
	getSystem().findNamed("TestMonitor/NetworkMonitor/ToCount/Counter"
			      +ci+"/count");
    blocks = new Wire[ toBlock.length ];
    for ( int bi = 0; bi < toBlock.length; ++bi )
      blocks[bi] = (Wire)
	getSystem().findNamed("TestMonitor/NetworkMonitor/ToBlock/Counter"
			      +bi+"/count");
    countHit = (Wire)
      getSystem().findNamed("TestMonitor/NetworkMonitor/ToCount/hit");
    blockHit = (Wire)
      getSystem().findNamed("TestMonitor/NetworkMonitor/ToBlock/hit");
  }

  // SimulatorCallback interface methods
  /** Resets the values of the GraphCanvas */
  public void simulatorReset() {
    graph.resetValues();
    graph.repaint();
    hitDetected = false;
  }
  /** Updates the GraphCanvas if new hits have been detected */
  public void simulatorUpdate(int cycle, int step) {
    if ( 1 == step )
      return;
    try {
      if ( 1 == blockHit.get((Browser)this) ) {
	_status.setText("A hit to block has been detected!");
	hitDetected = true;
      } else if ( 1 == countHit.get((Browser)this) ) {
	_status.setText("A hit to count has been detected!");
	hitDetected = true;
      } else {
	_status.setText("                                 ");
      }
    } 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);
  }
  /** Simply tells the GraphCanvas to repaint */
  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 */
  private void refreshGraph( boolean forceUpdate ) {
    if ( hitDetected || forceUpdate ) {
      int numToCount = counts.length;
      try {
	for ( int ci = 0; ci < numToCount; ++ci )
	  graph.updateValue(ci,counts[ci].get((Browser)this));
	for ( int bi = 0; bi < blocks.length; ++bi )
	  graph.updateValue(numToCount+bi,blocks[bi].get((Browser)this));
      } catch ( WireValueException wve ) {
	System.err.println("Caught a WireValueException:"+wve.getMessage());
	wve.printStackTrace();
	System.exit(1);
      }
      graph.repaint();
      hitDetected = false;
    }
  }

  /** Creates a new GraphCanvas linked with this TestBench */
  GraphCanvas createGraphView() {
    String[] labels = new String[ counts.length + blocks.length ];
    int[] values = new int[ counts.length + blocks.length ];
    Color[] colors = new Color[ counts.length + blocks.length ];
    for ( int ind = 0; ind < counts.length; ++ind ) {
      labels[ind] = "To Count "+ind;
      values[ind] = 0;
      int red = 10, green = 10, blue = 127;
      //System.err.println("Creating a color with "+red+","+green+","+blue);
      colors[ind] = new Color(red,
			      green,
			      blue,50);
    }
    for ( int ind = 0; ind < blocks.length; ++ind ) {
      labels[ind+counts.length] = "To Block "+ind;
      values[ind+counts.length] = 0;
      int red = 127, green = 10, blue = 10;
      //System.err.println("Creating a color with "+red+","+green+","+blue);
      colors[ind+counts.length] = new Color(red,
					    green,
					    blue,50);
    }
    graph = new GraphCanvas( labels,
			     values,
			     colors,
			     "Number of hits");
    getSystem().addSimulatorCallback(this);
    return graph;
  }

  /** Creates a debug label associated with this TestBench.  In
   * particular, the label displays information based on the state of
   * the countHit and blockHit wires. */
  JLabel createStatusLabel() {
    return _status = new JLabel("This label indicates detected hits");
  }

  // The input and output wires of the system
  protected Wire packetsIn, packetsOut, restart, countHit, blockHit;
  protected Wire[] counts;
  protected Wire[] blocks;

  /** Used to visualize the operation of the HitCounter design */
  private GraphCanvas graph;

  /** Label shows information helpful for debugging the blocking
   * function */
  private JLabel _status;

  /** Tells the simulatorUpdate method that the next cycle it needs to
   * grab the count value */
  private boolean hitDetected;

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

} // end class TestMonitor

