
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 javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

/** This is a basic test of the HitCounterArray circuit.  This
 * TestBench simply creates an instance of the HitCounterArray 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 TestHitCounters extends Logic
  implements TestBench, SimulatorCallback, Browser {

  /** 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 ( 3 > args.length )
      showUsage(1);
    int width=0, countWidth=0;
    int[] hitValues = new int[args.length - 2];
    // parse user input
    try {
      width = Integer.parseInt( args[0] );
      countWidth = Integer.parseInt( args[1] );
      for ( int vi = 0; vi < hitValues.length; ++vi )
	hitValues[vi] = Integer.parseInt( args[2+vi] );
    } catch ( NumberFormatException nfe ) {
      showUsage(1);
    }
    // build the TestBench and the design circuit
    TestHitCounters thc
      = new TestHitCounters( new HWSystem(),
			     hitValues,
			     width,
			     countWidth );
    // put the design in a CVT frame
    new cvtFrame(thc);
    // build a custom view of the HitCounterArray counts
    GraphCanvas gc = thc.createGraphView();
    JLabel l1 = new JLabel("HitCounters Test"),
      l2 = new JLabel("HitCounters Test");
    JPanel panel = new JPanel(new BorderLayout());
    panel.add(gc,BorderLayout.CENTER);
    panel.add(l1,BorderLayout.NORTH);
    panel.add(l2,BorderLayout.SOUTH);
    JFrame graphFrame = new JFrame("Test HitCounters");
    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( "TestHitCounters usage:" );
    System.err.println( "  java TestHitCounters <wire_width> "
			+"<counter_width> <target_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 TestHitCounters( Node parent, int[] hitValues,
			  int width, int countWidth ) {
    super( parent,"TestHitCounters" );
    new HitCounterArray( this,
			 hitValues,
			 input = wire(width,"input"),
			 restart = wire(1,"restart"),
			 countWidth,
			 hit = wire(1,"hit") );
    Stimulator stim = new Stimulator( this );
    stim.addWire(input);
    stim.addWire(restart);
    counts = new Wire[ hitValues.length ];
    for ( int ci = 0; ci < hitValues.length; ++ci )
      counts[ci] = (Wire)
	getSystem().findNamed("TestHitCounters/HitCounterArray/Counter"
			      +ci+"/count");
  }

  // 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 ( hitDetected ) {
      for ( int ci = 0; ci < counts.length; ++ci )
	try {
	  graph.updateValue(ci,counts[ci].get((Browser)this));
	} catch ( WireValueException wve ) {
	  System.err.println("Caught a WireValueException:"+wve.getMessage());
	  wve.printStackTrace();
	  System.exit(1);
	}
      hitDetected = false;
      // every so often on long simulation runs, update graph
      if ( 0 == cycle%UPDATE_FREQUENCY )
	graph.repaint();
    }
    hitDetected = hit.getB(this);
  }
  /** Simply tells the GraphCanvas to repaint */
  public void simulatorRefresh(int cycle, int step) {
    graph.repaint();
  }

  /** Creates a new GraphCanvas linked with this TestBench */
  GraphCanvas createGraphView() {
    String[] labels = new String[ counts.length ];
    int[] values = new int[ counts.length ];
    Color[] colors = new Color[ counts.length ];
    for ( int ind = 0; ind < counts.length; ++ind ) {
      labels[ind] = "constant"+ind;
      values[ind] = 0;
      int red = 127*(ind%3), green = 127*((ind/3)%3), blue = 127*((ind/9)%3);
      //System.err.println("Creating a color with "+red+","+green+","+blue);
      colors[ind] = new Color(red,  // vary the colors
			      green,// arbitrarily
			      blue,100);
    }
    graph = new GraphCanvas( labels,
			     values,
			     colors,
			     "Number of hits");
    getSystem().addSimulatorCallback(this);
    return graph;
  }

  // The input and output wires of the system
  protected Wire input, restart, hit;
  protected Wire[] counts;

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

  /** 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 TestHitCounters
