import visitor.*;
import syntaxtree.*;
import java.util.*;
import java.io.*;

/**
 * Provides default methods which visit each node in the tree in depth-first
 * order.  Your visitors may extend this class.
 */
public class EdifVisitor extends DepthFirstVisitor {

  public EdifVisitor(String outFileName) {
    try {
      OutputStream os;
      if ( null == outFileName ) os = System.out;
      else os = new FileOutputStream(outFileName);
      writer = new PrintWriter(os);
    } catch (IOException ioe) {
      System.err.println("Could not open the file " + outFileName
			 + " for output writing.");
      System.exit( 1 );
    }
  }

  //
  // User-generated visitor methods below
  //

  /**
   * f0 -> <LBR>
   * f1 -> <EDIF>
   * f2 -> edifFileName()
   * f3 -> edifVersion()
   * f4 -> edifLevel()
   * f5 -> keywordMap()
   * f6 -> ( status() | external() | library() | design() | comment() | userData() )*
   * f7 -> <RBR>
   */
  public void visit(edif n) {
    msg("Visiting the edif");
    writer.print(PADS[depth] + n.f0 + n.f1 + " ");
    //n.f0.accept(this);
    //n.f1.accept(this);
    n.f2.accept(this);
    writer.println(currName); // Name of this edif
    incrDepth();
    n.f3.accept(this);
    n.f4.accept(this);
    n.f5.accept(this);
    n.f6.accept(this);
    decrDepth();
    writer.println();
    writer.println(PADS[depth] + n.f7);
    //n.f7.accept(this);
    writer.flush();
  } // */

  /**
   * f0 -> <LBR>
   * f1 -> <ARRAY>
   * f2 -> nameDef()
   * f3 -> integerValue()
   * f4 -> ( integerValue() )*
   * f5 -> <RBR>
   */
  public void visit(array n) {
    //this code assigns the width of the array to the port
    currPort.width = Integer.parseInt( n.f3.f0.choice.toString() );
    currPort.count = currPort.width;
    //n.f0.accept(this);
    //n.f1.accept(this);
    n.f2.accept(this);
    n.f3.accept(this);
    n.f4.accept(this);
    //n.f5.accept(this);
  } // */

  /**
   * f0 -> <LBR>
   * f1 -> <CELL>
   * f2 -> cellNameDef()
   * f3 -> cellType()
   * f4 -> ( status() | view() | viewMap() | property() | comment() | userData() )*
   * f5 -> <RBR>
   */
  public void visit(cell n) {
    cellList = new Hashtable();
    incrDepth();
    currCell = new Cell();
    currCell.cellType = getName( n.f2 );
    currCell.cellName = currCell.cellType;
    String edifType = currName.toLowerCase();
    contents = false;

    //n.f0.accept(this);
    //n.f1.accept(this);
    //n.f2.accept(this);
    n.f3.accept(this);
    n.f4.accept(this);
    //n.f5.accept(this);
    writer.println(PADS[depth] + "Cell -- " + currCell.cellName);

    incrDepth();
    writer.println(PADS[depth] + "ports:");
    Enumeration en = currCell.portList.elements();
    incrDepth();
    while (en.hasMoreElements()) {
      Port prt = (Port)en.nextElement();
      writer.println(PADS[depth] + prt.name
		     + "\t(width: " + prt.width + ")");
    }
    decrDepth();
    decrDepth();

    decrDepth();
  } // */

  /**
   * f0 -> <LBR>
   * f1 -> <CELLREF>
   * f2 -> cellNameRef()
   * f3 -> [ libraryRef() ]
   * f4 -> <RBR>
   */
  public void visit(cellRef n) {
    if ( currInstanceCell == null )
      return;
    //n.f0.accept(this);
    //n.f1.accept(this);
    n.f2.accept(this);
    String cellRef = currName.toLowerCase();
    Cell tmpCell = (Cell)cellList.get( cellRef );
    if ( tmpCell == null ) {
      System.out.println( "ERROR: cellRef " + cellRef + " is not specified" );
      return;
    }
    currInstanceCell.cellType = tmpCell.cellType;
    currInstanceCell.cellList = tmpCell.cellList;
    currInstanceCell.netList = tmpCell.netList;
    Enumeration e = tmpCell.portList.elements();
    while( e.hasMoreElements() ) {
      Port port = (Port)e.nextElement();
      currInstanceCell.portList.put( port.name, new Port( port ) );
    }
    n.f3.accept(this);
    //n.f4.accept(this);
  } // */

  /**
   * f0 -> <LBR>
   * f1 -> <DIRECTION>
   * f2 -> ( <INOUT> | <INPUT> | <OUTPUT> )
   * f3 -> <RBR>
   */
  public void visit(direction n) {
    if ( n.f2.choice.toString().equals( "INPUT" ) )
      currDirection = Port.IN;
    else if ( n.f2.choice.toString().equals( "OUTPUT" ) )
      currDirection = Port.OUT;
    else if ( n.f2.choice.toString().equals( "INOUT" ) )
      currDirection = Port.INOUT;

    //n.f0.accept(this);
    //n.f1.accept(this);
    //n.f2.accept(this);
    //n.f3.accept(this);
  } // */

  /**
   * f0 -> <LBR>
   * f1 -> <INSTANCE>
   * f2 -> instanceNameDef()
   * f3 -> ( viewRef() | viewList() )
   * f4 -> ( transform() | parameterAssign() | portInstance() | designator() | timing() | property() | comment() | userData() )*
   * f5 -> <RBR>
   */
  public void visit(instance n) {
    currInstanceCell = new Cell();
    currInstanceCell.cellName = getName( n.f2, true );
    //n.f0.accept(this);
    //n.f1.accept(this);
    //n.f2.accept(this);
    n.f3.accept(this);
    n.f4.accept(this);
    //n.f5.accept(this);
    currCell.cellList.put( currInstanceCell.cellName.toLowerCase(), currInstanceCell ); //EBB
    currInstanceCell = null;
  } // */

  /**
   * f0 -> <LBR>
   * f1 -> <INSTANCEREF>
   * f2 -> instanceNameRef()
   * f3 -> [ instanceRef() | viewRef() ]
   * f4 -> <RBR>
   */
  public void visit(instanceRef n) {
    instanceName = getName( n.f2 );

    //n.f0.accept(this);
    //n.f1.accept(this);
    //n.f2.accept(this);
    n.f3.accept(this);
    //n.f4.accept(this);
  } // */

  /**
   * f0 -> <LBR>
   * f1 -> <MEMBER>
   * f2 -> nameRef()
   * f3 -> integerValue()
   * f4 -> ( integerValue() )*
   * f5 -> <RBR>
   */
  public void visit(member n) {
    //n.f0.accept(this);
    //n.f1.accept(this);
    n.f2.accept(this);
    n.f3.accept(this);
    memberNum = Integer.parseInt(currInt);
    n.f4.accept(this);
    //n.f5.accept(this);
  } // */

  /**
   * f0 -> <LBR>
   * f1 -> <NET>
   * f2 -> netNameDef()
   * f3 -> joined()
   * f4 -> ( criticality() | netDelay() | figure() | net() | instance() | commentGraphics() | property() | comment() | userData() )*
   * f5 -> <RBR>
   */
  public void visit(net n) {
    currDirection = 0;
    portConnectedToNet = null;
    currNet = new Net();
    currNet.name = getName( n.f2, true );
    netConnectedToPort = false;

    //n.f0.accept(this);
    //n.f1.accept(this);
    //n.f2.accept(this);
    n.f3.accept(this);
    n.f4.accept(this);
    //n.f5.accept(this);

    if ( !netConnectedToPort )
      currCell.netList.put( currNet.name, currNet );
    else
      //this little piece of code assigns direction to directionless ports
      if ( portConnectedToNet.direction == 0 )
	portConnectedToNet.direction = currDirection;
  } // */

  /**
   * f0 -> <LBR>
   * f1 -> <PORT>
   * f2 -> portNameDef()
   * f3 -> ( direction() | unused() | designator() | dcFaninLoad() | dcFanoutLoad() | dcMaxFanin() | acLoad() | portDelay() | property() | comment() | userData() )*
   * f4 -> <RBR>
   */
  public void visit(port n) {
    currPort = new Port();
    currDirection = 0;
    currPort.name = getName( n.f2, true ).toLowerCase(); //EBB

    //n.f0.accept(this);
    //n.f1.accept(this);
    //n.f2.accept(this);
    n.f3.accept(this);
    //n.f4.accept(this);
    currPort.direction = currDirection;
    currCell.portList.put( currPort.name, currPort );

    if ( currPort.width > 1 ) {
      for ( int i = 0; i < currPort.width; i++ ) {
	Port tmpPort = new Port();
	tmpPort.name = currPort.name + Integer.toString( i );
	tmpPort.direction = currPort.direction;
	currCell.portList.put( tmpPort.name, tmpPort );
      }
    }
    /**/
    // Do bus reconstruction on top level cell only
    //else if (notAPrimitive(currCell.cellName) && getIndex( currPort.name ) != -1 ) {
    else if ( getIndex( currPort.name ) != -1 ) {
      //      msg("Merging port: " + currPort.name + " on top level cell: " + currCell.cellName);
      String baseName = getBaseName(currPort.name);
      Port port = (Port)currCell.portList.get(baseName);
      //      msg(currPort.name + " " + baseName);
      if ( port == null ) {
	Port tmpPort = new Port();
	tmpPort.name = getBaseName( currPort.name );
	tmpPort.width = getIndex( currPort.name ) + 1;
	tmpPort.count = 1;
	tmpPort.synthetic = true;
	tmpPort.direction = currDirection;
	currCell.portList.put( tmpPort.name, tmpPort );
      } 
      // See if this is a synthetic port, only do coalescing if it is
      else if (port.synthetic) {
	if ( getIndex( currPort.name ) >= port.width )
	  port.width = getIndex( currPort.name ) + 1;
	port.count++;
      }
      else
	msg("Not merging port " + currPort.name + " into port " +
	    port.name + " since " + port.name + " is an original port on the design.");
    } // */
  } // */

  /**
   * f0 -> <LBR>
   * f1 -> <PORTREF>
   * f2 -> portNameRef()
   * f3 -> [ portRef() | instanceRef() | viewRef() ]
   * f4 -> <RBR>
   */
  public void visit(portRef n) {
    memberNum = -1;
    String portName = getName( n.f2 ).toLowerCase(); //EBB
    instanceName = null;
    //n.f0.accept(this);
    //n.f1.accept(this);
    //n.f2.accept(this);
    n.f3.accept(this);
    //n.f4.accept(this);

    // If memberNum was set it means that this is a member of a wide
    // port.  Need to build its name by looking at the port's width
    //    if (memberNum != -1) {
    //      System.out.println("Member: " + portName + " " + memberNum);
    //    }
    if ( instanceName == null ) {
      Port port = (Port)currCell.portList.get( portName );
      if ( port == null ) {
	System.out.println( "Port " + portName + " not found" );
	System.out.println(currCell);
	return;
      }
      // Now find the actual specific port for this one if it is a member of a wide port
      if (memberNum != -1) {
	portName = portName + (port.width-1-memberNum);
	port = (Port)currCell.portList.get( portName );
	if ( port == null ) {
	  System.out.println( "Port " + portName + " not found" );
	  System.out.println(currCell);
	  return;
	}
      }
      netConnectedToPort = true;
      portConnectedToNet = port;
      port.attachedWire = currNet.name;
      return;
    }
    Cell cell = (Cell)currCell.cellList.get( instanceName.toLowerCase() );
    if ( cell == null ) {
      System.out.println( "Cell " + instanceName + " not found" );
      return;
    }
    Port port = (Port)cell.portList.get( portName );
    if ( port == null ) {
      System.out.println( "Port " + portName + " not found" );
      System.out.println(cell);
      return;
    }
    // Now find the actual specific port for this one if it is a member of wide port
    if (memberNum != -1) {
      portName = portName + (port.width-1-memberNum);
      port = (Port)cell.portList.get( portName );
      if ( port == null ) {
	System.out.println( "Port " + portName + " not found" );
	System.out.println(cell);
	return;
      }
    }

    port.aliasedWire = currNet.name;
    //We want to assign a connected port that doesn't have direction
    //information.  We use the currDirection value to assign
    //portConnectedToNet when we've gone throgh all the connections in
    //the list of nets rule ( if all in, then assign in if even one is
    //out assign out )
    if ( port.direction == Port.IN ) {
      if ( currDirection != Port.OUT )
	currDirection = Port.IN;
    } else if ( port.direction == Port.OUT ) {
      currDirection = Port.OUT;
    }
  } // */

  /**
   * f0 -> <LBR>
   * f1 -> <PROPERTY>
   * f2 -> propertyNameDef()
   * f3 -> typedValue()
   * f4 -> ( owner() | unit() | property() | comment() )*
   * f5 -> <RBR>
   */
  public void visit(property n) {
    //n.f0.accept(this);
    //n.f1.accept(this);
    n.f2.accept(this);
    n.f3.accept(this);
    if ( currInstanceCell == null )
      return;
    currInstanceCell.propertyList.put( currName, currStringName );
    n.f4.accept(this);
    //n.f5.accept(this);
  } // */

  /**
   * f0 -> <LBR>
   * f1 -> <RENAME>
   * f2 -> ( ident() | name() )
   * f3 -> ( <STRING_TOK> | stringDisplay() )
   * f4 -> <RBR>
   */
  public void visit(rename n) {
    renaming = true;
    currStringName = n.f3.choice.toString().substring( 1, n.f3.choice.toString().length() - 1 );

    //n.f0.accept(this);
    //n.f1.accept(this);
    n.f2.accept(this);
    n.f3.accept(this);
    //n.f4.accept(this);
  } // */

  /**
   * f0 -> <LBR>
   * f1 -> <VIEW>
   * f2 -> viewNameDef()
   * f3 -> viewType()
   * f4 -> myInterface()
   * f5 -> ( status() | contents() | comment() | property() | userData() )*
   * f6 -> <RBR>
   */
  public void visit(view n) {
    if ( n.f5.present() )
      contents = true;
    //n.f0.accept(this);
    //n.f1.accept(this);
    n.f2.accept(this);
    n.f3.accept(this);
    n.f4.accept(this);
    n.f5.accept(this);
    //n.f6.accept(this);
  } // */

  /**
   * f0 -> .. See DepthFirstVisitor for possibilities ..
   */
  public void visit(ident n) {
    currName = n.f0.choice.toString();
    //n.f0.accept(this);
  } // */

  /**
   * f0 -> <INTEGER_TOK>
   *       | valueNameRef()
   *       | floor()
   *       | ceiling()
   *       | fix()
   *       | mod()
   *       | xCoord()
   *       | yCoord()
   *       | abs()
   *       | max()
   *       | min()
   *       | negate()
   *       | product()
   *       | subtract()
   *       | sum()
   */
  public void visit(integerValue n) {
    currInt = n.f0.choice.toString();
    //n.f0.accept(this);
  } // */

  /**
   * f0 -> "("
   * f1 -> "interface"
   * f2 -> ( port() | portBundle() | symbol() | protectionFrame() | arrayRelatedInfo() | parameter() | joined() | mustJoin() | weakJoined() | permutable() | timing() | simulate() | designator() | constant() | constraint() | variable() | property() | comment() | userData() )*
   * f3 -> ")"
   */
  public void visit(myInterface n) {
    //n.f0.accept(this);
    //n.f1.accept(this);
    n.f2.accept(this);
    //n.f3.accept(this);

    Enumeration e = currCell.portList.elements();

    // Is there not an interface at this level?  Should only happen at top level.
    if (e == null) {
	System.out.println("No interface in cell...");
	return;
    }

    //msg("Setting port widths to 0 in cell: " + currCell.cellName);
    //msg("" + currCell.portList);
    while ( e.hasMoreElements() ) {
      Port port = (Port)e.nextElement();
      //if bus has width one, or the count and width don't match up, remove it
      if ( port.count == 1 || ( port.width > 1 && port.count != port.width ) ) {
	currCell.portList.remove( port.name );
      }
      //set wire widths of ports in bus port to 0
      else if ( port.width > 1 ) {
	//msg("Wide port: " + port.name + " " + port.width + " " + port.count);	
	for ( int i = 0; i < port.count; i++ ) {
	  Port tmp = (Port)currCell.portList.get( port.name + Integer.toString( i ) );
	  if ( tmp == null )
	    tmp = (Port)currCell.portList.get( port.name + "_" + Integer.toString( i ) + "_" );
	  if ( tmp == null )
	    tmp = (Port)currCell.portList.get( port.name + "_" + Integer.toString( i ) );
	  if (tmp == null) {
	    System.err.println("Cannot find individual port " + i + " for wide port " + port.name +
			       " in cell: " + currCell);
	    System.exit(1);
	  }
	  tmp.width = 0;
	}
      }
    }

    //msg("Done setting port widths to 0 in cell: " + currCell.cellName);
    //msg("" + currCell.portList);
  } // */

  /**
   * f0 -> <STRING_TOK>
   *       | valueNameRef()
   *       | concat()
   */
  public void visit(stringValue n) {
    currStringName = n.f0.choice.toString().substring( 1, n.f0.choice.toString().length() - 1 );
    //n.f0.accept(this);
  } // */

  /** Constant to limit the padding depth of the output */
  private static final int MAX_DEPTH = 7;
  /** Increments the member depth if depth is not already MAX_DEPTH */
  private void incrDepth() {
    if (depth < MAX_DEPTH)
      ++depth;
  }
  /** Decrements the member depth if depth is not already zero */
  private void decrDepth() {
    if (depth > 0)
      --depth;
  }

  /** Get the name of a Node being visited */
  protected String getName(Node n) {
    return getName(n, false);
  }

  /** Get the name of a Node being visited */
  protected String getName(Node n, boolean getOldName) {
    renaming = false;
    n.accept( this );
    if ( renaming && !getOldName )
      return currStringName;
    else
      return currName;
  }

  
  public static int getIndex( String pName ) {
    //We want to look for two kinds of bus formats.  The first is any
    //port that ends in a number the second involves any port that has
    //a number surrounded by underscores this checks the two cases
    //then returns the bus info
    try {
      if ( ( !Character.isDigit( pName.charAt( pName.length() - 1 ) ) && 
	     pName.charAt( pName.length() - 1 ) != '_' ) || 
	   ( pName.charAt( pName.length() - 1 ) == '_' && 
	     !Character.isDigit( pName.charAt( pName.length() - 2 ) ) ) ) {
	return -1;
      }
    } catch( ArrayIndexOutOfBoundsException e ) {
      return -1;
    }
    int i = 2;
    boolean underscore = false;
    if ( pName.charAt( pName.length() - 1 ) == '_' ) {
      underscore = true;
      i = 3;
    }
    while ( Character.isDigit( pName.charAt( pName.length() - i ) ) )
      i++;
    //If we have an underscore at the end, we check to see if there is
    //another underscore on the other side.  If not this is not valid
    //and we return a -1 for the index.  If it is, then we return the
    //index value
    if ( underscore )
      if ( pName.charAt( pName.length() - i ) == '_' )
	return Integer.parseInt( pName.substring( pName.length() - i + 1, pName.length() - 1 ) );
      else
	return -1;
    else
      return Integer.parseInt( pName.substring( pName.length() - i + 1, pName.length() ) );
  }

  public static String getBaseName( String pName ) {
    int i = 1;
    int numUnderscores = 0;
    if ( pName.length() == 0 )
      return "";
    while ( Character.isDigit( pName.charAt( pName.length() - i ) )
	    || pName.charAt( pName.length() - i ) == '_' ) {
      if ( pName.charAt( pName.length() - i ) == '_' )
	numUnderscores++;
      i++;

      if ( numUnderscores == 2 )
	break;
    }
    return pName.substring( 0, pName.length() - i + 1 );
  }

  /** Based on the value of depth, the output PrintWriter will also
      print out the corresponding padding from the member PADS */
  private int depth = 0;
  /** Constant strings that act as padding for corresponding levels of
   depth when writer writes its output */
  private String[] PADS = {"", "  ", "    ", "      ",
  "        ", "          ", "            ", "              "};

  /** The standard output of this class.  If this class is
      instantiated with a String filename, writer will be a
      PrintWriter that outputs to a file with the name filename,
      otherwise, it will print to System.out */
  protected PrintWriter writer = null;

  /** Shorthand way to send output to System.out
      @param o This object will be printed to System.out (using its
      toString method). */
  protected static void msg(Object o) { System.out.println( o + ""); }

  /** Container for cells */
  private Hashtable cellList;
  /** Root cell of the edif file */
  private Cell rootCell;
  /** Cell currently being visited */
  private Cell currCell;
  /** Instance currently being visited */
  private Cell currInstanceCell;
  /** This will be the current name of a Node being visited as found
      when an ident is being visited. */
  private String currName;
  /** This will be the current string name of a Node being visited as
      found when a rename or a stringValue is being visited. */
  private String currStringName;
  /** String of int currently being visited */
  private String currInt;
  /** */
  private String instanceName;
  /** Indicates direction of current port */
  private int currDirection;
  /** Current net being visited */
  private Net currNet;
  /** Indicates connection status of current net and current port*/
  private boolean netConnectedToPort = false;
  /** Port currently being visited */
  private Port currPort;
  /** Port connected to current net */
  private Port portConnectedToNet;
  /** Indicates if visiting a node that has a rename construct. */
  private boolean renaming;
  /** Indicates if the current node (cell) has any contents. */
  private boolean contents;
  /** */
  private int memberNum;
}
  
class Port {
  public String name = "";
  public int    direction = 0;
  public int    width = 1;
  public int    count = 0;
  public String attachedWire = "";
  public String aliasedWire = "";
  public boolean reversed = false;
  public boolean synthetic = false;
  public Port() {
  }
  public Port( Port port ) {
    name = port.name;
    direction = port.direction;
    width =  port.width;
    attachedWire = port.attachedWire;
  }
  public static final int IN    = 1;
  public static final int OUT   = 2;
  public static final int INOUT = 3;
  public static final int IN_PORT = 0x04000000;
  public static final int OUT_PORT = 0x08000000;
  public static final int INOUT_PORT = IN_PORT | OUT_PORT;
  public static final int OUTE_PORT = 0x10000000;
}

class Cell {
  public String    cellName       = "";
  public String    cellType       = "";
  public Hashtable propertyList   = new Hashtable();
  public Hashtable cellList       = new Hashtable();
  public Hashtable netList        = new Hashtable();
  public Hashtable wires          = new Hashtable();
  public Hashtable portList       = new Hashtable();
  public Hashtable tmpwires       = new Hashtable();
  public String toString() {
    String s= "Cell: " + cellName + ", type: " + cellType + "\n";
    s += "Properties: " + propertyList + "\n";
    s += "Ports: " + "\n";
    Enumeration e = portList.elements();
    while (e.hasMoreElements()) {
      Port p = (Port)e.nextElement();
      s += " (" + p.name + " " + p.width + ")";
    }
    s += "\n";
    return s;
  }
  int oppositeDirection(int dir) {
    switch (dir) {
    case Port.IN:
      return Port.OUT;
    case Port.OUT:
      return Port.IN;
    default:
      throw new RuntimeException("OppositeDirection: unknown direction: " + dir);
    }
  }
  boolean cellPortExists(Cell cell, String nam) {
    nam = nam.toLowerCase();
    Enumeration e1 = cell.portList.elements();
    while( e1.hasMoreElements() ) {
      Port port = (Port)e1.nextElement();
      if (port.width != 0 && port.name.toLowerCase().equals(nam))
	return true;
    }
    return false;
  }
  void addPortWRTInput(Cell cell) {
    Enumeration e1 = cell.portList.elements();
    while( e1.hasMoreElements() ) {
      Port port = (Port)e1.nextElement();
      //we want to skip zero width ports
      if ( port.width != 1 || !port.name.equals("o"))
	continue;
      String wireName = (String)tmpwires.get( port.aliasedWire );
      //check to see if a port is unconnected, if so, 
      //force to ground if input or nc the output
      if ( wireName == null ) 
	System.out.println("Null wire on input buf, cannot add as port, " + cell.cellName);
      else if (cellPortExists(cell, wireName))
	msg("Duplicate port: " + wireName + ", ignored."); 
      else {
	Port currPort = new Port();
	currPort.name = wireName;
	currPort.width = 1;
	currPort.direction = oppositeDirection(port.direction);
	currPort.aliasedWire = wireName;
	portList.put( currPort.name.toLowerCase(), currPort ); //EBB
	msg("Making input port: " + currPort.name);
      }
    }
  }
  void addPortWRTOutput(Cell cell) {
    Enumeration e1 = cell.portList.elements();
    while( e1.hasMoreElements() ) {
      Port port = (Port)e1.nextElement();
      //we want to skip zero width ports
      if ( port.width != 1 || !port.name.equals("i"))
	continue;
      String wireName = (String)tmpwires.get( port.aliasedWire );
      //check to see if a port is unconnected, if so, 
      //force to ground if input or nc the output
      if ( wireName == null ) 
	System.out.println("Null wire on output buf, cannot add as port, " + cell.cellName);
      // else add new port to this cell
      else if (cellPortExists(cell, wireName))
	msg("Duplicate port: " + wireName + ", ignored."); 
      else {
	Port currPort = new Port();
	currPort.name = wireName;
	currPort.width = 1;
	currPort.direction = oppositeDirection(port.direction);
	currPort.aliasedWire = wireName;
	portList.put( currPort.name.toLowerCase(), currPort ); //EBB
	msg("Making output port: " + currPort.name);
      }
    }
  }
  void addPortWRTTriCtl(Cell cell) {
    Enumeration e1 = cell.portList.elements();
    while( e1.hasMoreElements() ) {
      Port port = (Port)e1.nextElement();
      //we want to skip zero width ports
      if ( port.width != 1 || !port.name.equals("t"))
	continue;
      String wireName = (String)tmpwires.get( port.aliasedWire );
      //check to see if a port is unconnected, if so, 
      //force to ground if input or nc the output
      if ( wireName == null ) 
	System.out.println("Null wire on tristate control for output buf, cannot add as port, " + cell.cellName);
      // else add new port to this cell if no such port already exists
      else if (!cellPortExists(cell, wireName)) {
	Port currPort = new Port();
	currPort.name = wireName;
	currPort.width = 1;
	currPort.direction = Port.OUT;
	currPort.aliasedWire = wireName;
	portList.put( currPort.name.toLowerCase(), currPort ); //EBB
	msg("Making output port: " + currPort.name);
      }
    }
  }
  void msg(Object o) { System.out.println(o+""); }
}
class Net {
  public String name = "";
}

