/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: GetNetlist.java
 * Silicon compiler tool (QUISC): read a netlist
 * Written by Andrew R. Kostiuk, Queen's University.
 * Translated to Java by Steven M. Rubin, Sun Microsystems.
 *
 * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.sun.electric.tool.sc;

import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.hierarchy.Library;
import com.sun.electric.database.hierarchy.View;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.prototype.PortCharacteristic;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.util.TextUtils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * This is the netlist reader of the Silicon Compiler tool.
 */
public class GetNetlist
{
	/***********************************************************************
		General Constants
	------------------------------------------------------------------------*/
	private static final int GND = 0;
	private static final int PWR = 1;

	/***** Directions that ports can be attached to *****/
	/** mask for port direction */		public static final int PORTDIRMASK	= 0x0000000F;
	/** port direction up */			public static final int PORTDIRUP	= 0x00000001;
	/** port direction down */			public static final int PORTDIRDOWN	= 0x00000002;
	/** port direction right */			public static final int PORTDIRRIGHT= 0x00000004;
	/** port direction left */			public static final int PORTDIRLEFT	= 0x00000008;
	/** port type mask */				public static final int PORTTYPE	= 0x000003F0;
	/** ground port */					public static final int GNDPORT		= 0x00000010;
	/** power port */					public static final int PWRPORT		= 0x00000020;
	/** bidirectional port */			public static final int BIDIRPORT	= 0x00000040;
	/** output port */					public static final int OUTPORT		= 0x00000080;
	/** input port */					public static final int INPORT		= 0x00000100;
	/** unknown port */					public static final int UNPORT		= 0x00000200;

	/***********************************************************************
		QUISC Cell Structure
	------------------------------------------------------------------------*/

	static class SCCell
	{
		/** name of complex cell */				String    		name;
		/** maximum number of nodes */			int       		maxNodeNum;
		/** list of instances for cell */		List<SCNiTree>	niList;
		/** extracted nodes */					ExtNode 		exNodes;
		/** flags for processing cell */		int       		bits;
		/** list of power ports */				ExtNode 		power;
		/** list of ground ports */				ExtNode 		ground;
		/** list of ports */					SCPort    		ports, lastPort;
		/** placement information of cell */	Place.SCPlace   placement;
		/** routing information for cell */		Route.SCRoute   route;
		/** next in list of SC cells */			SCCell    		next;
	};

	static class SCPort
	{
		/** name of port */						String   name;
		/** special node */						SCNiTree node;
		/** cell on which this port resides */	SCCell   parent;
		/** port attributes */					int      bits;
		/** pointer to next port */				SCPort   next;
	};

	static class SCCellNums
	{
		/** active area from top */				int		topActive;
		/** active are from bottom */			int		bottomActive;
		/** active area from left */			int		leftActive;
		/** active are from right */			int		rightActive;
	};

	/***********************************************************************
		Instance Tree Structure
	------------------------------------------------------------------------*/

	/***** Types of Instances *****/
	public static final int LEAFCELL     = 0;
	public static final int COMPLEXCELL  = 1;
	public static final int SPECIALCELL  = 2;
	public static final int FEEDCELL     = 3;
	public static final int STITCH       = 4;
	public static final int LATERALFEED  = 5;

	static class SCNiTree
	{
		/** name of instance */					String		name;
		/** alternative number of node */		int			number;
		/** type of instance */					int			type;
		/** leaf cell or SCCell if complex */	Object		np;
		/** x size if leaf cell */				double		size;
		/** pointer to connection list */		ConList		connect;
		/** list of io ports and ext nodes */	SCNiPort	ports;
		/** list of actual power ports */		SCNiPort	power;
		/** list of actual ground ports */		SCNiPort	ground;
		/** bits for silicon compiler */		int			flags;
		/** generic temporary pointer */		Object		tp;

		/**
		 * Method to create a new instance with a given name and type.
		 * @param name name of the instance.
		 * @param type type of the instance.
		 */
		SCNiTree(String name, int type)
		{
			this.name = name;
			this.type = type;
			this.number = 0;
			this.np = null;
			this.size = 0;
			this.connect = null;
			this.ports = null;
			this.power = null;
			this.ground = null;
			this.flags = 0;
			this.tp = null;
		}
	};

	static class SCNiPort
	{
		/** leaf port or SCPort if on cell */	Object		port;
		/** extracted node */					ExtNode		extNode;
		/** bits for processing */				int			bits;
		/** x position if leaf port */			double		xPos;
		/** list of instance ports */			SCNiPort	next;

		SCNiPort() {}

		SCNiPort(SCNiTree instance)
		{
			this.port = null;
			this.extNode = null;
			this.bits = 0;
			this.xPos = 0;
			this.next = instance.ports;
			instance.ports = this;
		}
	};

	/***********************************************************************
		Connection Structures
	------------------------------------------------------------------------*/

	private static class ConList
	{
		/** pointer to port on node A */		SCNiPort	portA;
		/** pointer to node B */				SCNiTree	nodeB;
		/** pointer to port on node B */		SCNiPort	portB;
		/** pointer to extracted node */		ExtNode		extNode;
		/** pointer to next list element */		ConList   	next;
	};

	/***********************************************************************
		Extraction Structures
	------------------------------------------------------------------------*/

	private static class ExtPort
	{
		/** instance of extracted node */		SCNiTree	node;
		/** instance port */					SCNiPort	port;
		/** next in list of common node */		ExtPort		next;
	};

	/**
	 * Class for communicating netlist information to the placer and router.
	 */
	public static class ExtNode
	{
		/** optional name of port */			String		name;
		/** link list of ports */				ExtPort		firstPort;
		/** flags for processing */				int			flags;
		/** generic pointer for processing */	Object		ptr;
		/** link list of nodes */				ExtNode		next;
	};

	SCCell	scCells, curSCCell;

	/**
	 * Read the netlist associated with the current cell.
	 * @return true on error.
	 */
	public boolean readNetCurCell(Cell cell)
	{
		scCells = null;
		curSCCell = null;
		if (cell.getView() != View.NETLISTQUISC)
		{
			System.out.println("Current cell must have QUISC Netlist view");
			return true;
		}
		String [] strings = cell.getTextViewContents();
		if (strings == null)
		{
			System.out.println("Cell " + cell.describe(true) + " has no text in it");
			return true;
		}

		// read entire netlist
		boolean errors = false;
		for(int i=0; i<strings.length; i++)
		{
			String inBuf = strings[i].trim();

			// check for a comment line or empty line
			if (inBuf.length() == 0 || inBuf.charAt(0) == '!') continue;

			// break into keywords
			List<String> parameters = new ArrayList<String>();
			for(int sPtr = 0; sPtr < inBuf.length(); sPtr++)
			{
				// get rid of leading white space
				while (sPtr < inBuf.length() && (inBuf.charAt(sPtr) == ' ' || inBuf.charAt(sPtr) == '\t')) sPtr++;
				if (sPtr >= inBuf.length()) break;

				// check for string
				if (inBuf.charAt(sPtr) == '"')
				{
					sPtr++;
					int endQuote = inBuf.indexOf('"', sPtr);
					if (endQuote < 0)
					{
						System.out.println("ERROR line " + (i+1) + ": Unbalanced quotes ");
						errors = true;
						break;
					}
					parameters.add(inBuf.substring(sPtr, endQuote));
					sPtr = endQuote;
				} else
				{
					int endSpace = inBuf.indexOf(' ', sPtr);
					int endTab = inBuf.indexOf('\t', sPtr);
					if (endSpace < 0) endSpace = endTab;
					if (endSpace < 0) endSpace = inBuf.length();
					if (endTab >= 0 && endTab < endSpace) endSpace = endTab;
					parameters.add(inBuf.substring(sPtr, endSpace));
					sPtr = endSpace;
				}
			}
			String err = parse(parameters);
			if (err != null)
			{
				System.out.println("ERROR line " + (i+1) + ": " + err);
				System.out.println("      Line: " + inBuf);
				errors = true;
				break;
			}
		}

		// do the "pull" (extract)
		String err = pull();
		if (err != null)
		{
			System.out.println(err);
			errors = true;
		}
		return errors;
	}

	/**
	 * Main parsing routine for the Silicon Compiler.
	 */
	private String parse(List<String> keywords)
	{
		if (keywords.size() == 0) return null;
		String mainKeyword = keywords.get(0);

		if (mainKeyword.equalsIgnoreCase("connect")) return connect(keywords);
		if (mainKeyword.equalsIgnoreCase("create")) return create(keywords);
		if (mainKeyword.equalsIgnoreCase("export")) return export(keywords);
		if (mainKeyword.equalsIgnoreCase("extract")) return extract(keywords);
		if (mainKeyword.equalsIgnoreCase("set")) return xSet(keywords);
		return "Unknown keyword: " + mainKeyword;
	}

	/**************************************** NETLIST READING: THE CREATE KEYWORD ****************************************/

	private String create(List<String> keywords)
	{
		if (keywords.size() <= 1) return "No keyword for CREATE command";
		String sPtr = keywords.get(1);
		if (sPtr.equalsIgnoreCase("cell"))
		{
			if (keywords.size() <= 2) return "No name for CREATE CELL command";
			sPtr = keywords.get(2);

			// check if cell already exists in cell list
			for(SCCell cell = scCells; cell != null; cell = cell.next)
			{
				if (sPtr.equalsIgnoreCase(cell.name))
					return "Cell '" + sPtr + "' already exists in current library";
			}

			// generate warning message if a leaf cell of the same name exists
			if (findLeafCell(sPtr) != null)
				System.out.println("WARNING - cell " + sPtr + " may be overridden by created cell");

			// create new cell
			SCCell newCell = new SCCell();
			newCell.name = sPtr;
			newCell.maxNodeNum = 0;
			newCell.niList = new ArrayList<SCNiTree>();
			newCell.exNodes = null;
			newCell.bits = 0;
			newCell.power = null;
			newCell.ground = null;
			newCell.ports = null;
			newCell.lastPort = null;
			newCell.placement = null;
			newCell.route = null;
			newCell.next = scCells;
			scCells = newCell;
			curSCCell = newCell;

			// create dummy ground and power nodes
			SCNiTree ntp = findNi(curSCCell, "ground");
			if (ntp != null) return "Instance 'ground' already exists";
			ntp = new SCNiTree("ground", SPECIALCELL);
			curSCCell.niList.add(ntp);
			new SCNiPort(ntp);
			ntp.number = curSCCell.maxNodeNum++;

			ntp = findNi(curSCCell, "power");
			if (ntp != null) return "Instance 'power' already exists";
			ntp = new SCNiTree("power", SPECIALCELL);
			curSCCell.niList.add(ntp);
			new SCNiPort(ntp);
			ntp.number = curSCCell.maxNodeNum++;
			return null;
		}

		if (sPtr.equalsIgnoreCase("instance"))
		{
			if (keywords.size() <= 2) return "No instance name for CREATE INSTANCE command";
			String noden = keywords.get(2);

			if (keywords.size() <= 3) return "No type name for CREATE INSTANCE command";
			String nodep = keywords.get(3);

			// search for cell in cell list
			SCCell cell = null;
			for(SCCell c = scCells; c != null; c = c.next)
			{
				if (nodep.equalsIgnoreCase(c.name)) { cell = c;   break; }
			}
			Object proto = cell;
			int type = COMPLEXCELL;
			double size = 0;
			if (cell == null)
			{
				// search for leaf cell in library
				Cell bc = findLeafCell(nodep);
				if (bc == null)
					return "There is no '" + nodep + "' in the standard cell library";
				proto = bc;
				type = LEAFCELL;
				size = SilComp.leafCellXSize(bc);
			}

			// check if currently working in a cell
			if (curSCCell == null) return "No cell selected";

			// check if instance name already exits
			SCNiTree ntp = findNi(curSCCell, noden);
			if (ntp != null)
				return "Instance '" + noden + "' already exists";

			// add instance name to tree
			ntp = new SCNiTree(noden, type);
			curSCCell.niList.add(ntp);
			ntp.number = curSCCell.maxNodeNum++;
			ntp.np = proto;
			ntp.size = size;

			// create ni port list
			if (type == COMPLEXCELL)
			{
				SCNiPort oldNiPort = null;
				for (SCPort port = ((SCCell)proto).ports; port != null; port = port.next)
				{
					SCNiPort niPort = new SCNiPort();
					niPort.port = port;
					niPort.extNode = null;
					niPort.bits = 0;
					niPort.xPos = 0;
					switch (port.bits & PORTTYPE)
					{
						case GNDPORT:
							niPort.next = ntp.ground;
							ntp.ground = niPort;
							break;
						case PWRPORT:
							niPort.next = ntp.power;
							ntp.power = niPort;
							break;
						default:
							niPort.next = null;
							if (oldNiPort == null)
							{
								ntp.ports = niPort;
							} else
							{
								oldNiPort.next = niPort;
							}
							oldNiPort = niPort;
							break;
					}
				}
			} else
			{
				SCNiPort oldNiPort = null;
				Cell realCell = (Cell)proto;
				for(Iterator<PortProto> it = realCell.getPorts(); it.hasNext(); )
				{
					Export bp = (Export)it.next();
					SCNiPort niPort = new SCNiPort();
					niPort.port = bp;
					niPort.extNode = null;
					niPort.bits = 0;
					niPort.xPos = SilComp.leafPortXPos(bp);
					switch (getLeafPortType(bp))
					{
						case GNDPORT:
							niPort.next = ntp.ground;
							ntp.ground = niPort;
							break;
						case PWRPORT:
							niPort.next = ntp.power;
							ntp.power = niPort;
							break;
						default:
							niPort.next = null;
							if (oldNiPort == null)
							{
								ntp.ports = niPort;
							} else
							{
								oldNiPort.next = niPort;
							}
							oldNiPort = niPort;
							break;
					}
				}
			}
			return null;
		}
		return "Unknown CREATE command: " + sPtr;
	}

	/**
	 * Method to find the location in a SCNiTree where the node should be placed.
	 * If the pointer does not have a value of null, then the object already exits.
	 */
	private SCNiTree findNi(SCCell cell, String name)
	{
		for(SCNiTree nPtr : cell.niList)
		{
			if (nPtr.name.equalsIgnoreCase(name)) return nPtr;
		}
		return null;
	}

	/**************************************** NETLIST READING: THE CONNECT KEYWORD ****************************************/

	private String connect(List<String> keywords)
	{
		if (keywords.size() < 4) return "Not enough parameters for CONNECT command";

		// search for the first node
		String node0Name = keywords.get(1);
		String port0Name = keywords.get(2);
		SCNiTree ntpA = findNi(curSCCell, node0Name);
		if (ntpA == null) return "Cannot find instance '" + node0Name + "'";
		SCNiPort portA = findPp(ntpA, port0Name);
		if (portA == null)
			return "Cannot find port '" + port0Name + "' on instance '" + node0Name + "'";

		// search for the second node
		String node1Name = keywords.get(3);
		SCNiTree ntpB = findNi(curSCCell, node1Name);
		if (ntpB == null) return "Cannot find instance '" + node1Name + "'";

		// check for special power or ground node
		SCNiPort portB = ntpB.ports;
		if (ntpB.type != SPECIALCELL)
		{
			if (keywords.size() < 5) return "Not enough parameters for CONNECT command";
			String port1Name = keywords.get(4);
			portB = findPp(ntpB, port1Name);
			if (portB == null)
				return "Cannot find port '" + port1Name + "' on instance '" + node1Name + "'";
		}
		conList(ntpA, portA, ntpB, portB);
		return null;
	}

	/**
	 * Method to find the port on the given node instance.
	 * @return null if port is not found.
	 */
	private SCNiPort findPp(SCNiTree ntp, String name)
	{
		SCNiPort port = null;

		if (ntp == null) return null;
		switch (ntp.type)
		{
			case SPECIALCELL:
				return ntp.ports;
			case COMPLEXCELL:
				for (port = ntp.ports; port != null; port = port.next)
				{
					if (((SCPort)port.port).name.equalsIgnoreCase(name)) break;
				}
				break;
			case LEAFCELL:
				for (port = ntp.ports; port != null; port = port.next)
				{
					Export pp = (Export)port.port;
					if (pp.getName().equalsIgnoreCase(name)) break;
				}
				break;
		}
		return port;
	}

	/**
	 * Method to add a connection count for the two node instances indicated.
	 */
	private void conList(SCNiTree ntpA, SCNiPort portA, SCNiTree ntpB, SCNiPort portB)
	{
		// add connection to instance A
		ConList cl = new ConList();
		cl.portA = portA;
		cl.nodeB = ntpB;
		cl.portB = portB;
		cl.extNode = null;

		// add to head of the list
		cl.next = ntpA.connect;
		ntpA.connect = cl;

		// add connection to instance B
		cl = new ConList();
		cl.portA = portB;
		cl.nodeB = ntpA;
		cl.portB = portA;
		cl.extNode = null;

		// add to head of the list
		cl.next = ntpB.connect;
		ntpB.connect = cl;
	}

	/**************************************** NETLIST READING: THE EXPORT KEYWORD ****************************************/

	private String export(List<String> keywords)
	{
		// check to see if working in a cell
		if (curSCCell == null) return "No cell selected";

		// search current cell for node
		if (keywords.size() <= 1) return "No instance specified for EXPORT command";
		String instName = keywords.get(1);
		SCNiTree nPtr = findNi(curSCCell, instName);
		if (nPtr == null) return "Cannot find instance '" + instName + "' for EXPORT command";

		// search for port
		if (keywords.size() <= 2) return "No port specified for EXPORT command";
		String portName = keywords.get(2);
		SCNiPort port = findPp(nPtr, portName);
		if (port == null)
			return "Cannot find port '" + portName + "' on instance '" + instName + "' for EXPORT command";

		// check for export name
		if (keywords.size() <= 3) return "No export name specified for EXPORT command";
		String exportName = keywords.get(3);

		// check possible port type
		int type = UNPORT;
		if (keywords.size() >= 5)
		{
			String typeName = keywords.get(4);
			if (typeName.equalsIgnoreCase("input"))
			{
				type = INPORT;
			} else if (typeName.equalsIgnoreCase("output"))
			{
				type = OUTPORT;
			} else if (typeName.equalsIgnoreCase("bidirectional"))
			{
				type = BIDIRPORT;
			} else
			{
				return "Unknown port type '" + typeName + "' for EXPORT command";
			}
		}

		// create special node
		SCNiTree searchNPtr = findNi(curSCCell, exportName);
		if (searchNPtr != null) return "Export name '" + exportName + "' is not unique";
		SCNiTree newNPtr = new SCNiTree(exportName, SPECIALCELL);
		curSCCell.niList.add(newNPtr);
		newNPtr.number = curSCCell.maxNodeNum++;
		searchNPtr = newNPtr;
		SCNiPort niPort = new SCNiPort();
		niPort.port = null;
		niPort.extNode = null;
		niPort.next = null;
		newNPtr.ports = niPort;

		// add to export port list
		SCPort newPort = new SCPort();
		niPort.port = newPort;
		newPort.name = exportName;
		newPort.node = newNPtr;
		newPort.parent = curSCCell;
		newPort.bits = type;
		newPort.next = null;
		if (curSCCell.lastPort == null)
		{
			curSCCell.ports = curSCCell.lastPort = newPort;
		} else
		{
			curSCCell.lastPort.next = newPort;
			curSCCell.lastPort = newPort;
		}

		// add to connect list
		conList(nPtr, port, newNPtr, niPort);
		return null;
	}

	/**************************************** NETLIST READING: THE SET KEYWORD ****************************************/

	/**
	 * Method to handle the "SET" keyword.
	 * Current options are:
	 * 1.  leaf-cell-numbers = Magic numbers for leaf cells.
	 * 2.  node-name	     = Name an extracted node.
	 * 3.  port-direction    = Direction allowed to attach to a port.
	 */
	private String xSet(List<String> keywords)
	{
		if (keywords.size() <= 1) return "No option for SET command";
		String whatToSet = keywords.get(1);

		if (whatToSet.equalsIgnoreCase("leaf-cell-numbers"))
		{
			String cellName = keywords.get(2);
			Cell leafCell = findLeafCell(cellName);
			if (leafCell == null) return "Cannot find cell '" + cellName + "'";
			SCCellNums cNums = getLeafCellNums(leafCell);
			int numPar = 3;
			while (numPar < keywords.size())
			{
				String parName = keywords.get(numPar);
				if (parName.equalsIgnoreCase("top-active"))
				{
					numPar++;
					if (numPar < keywords.size())
						cNums.topActive = TextUtils.atoi(keywords.get(numPar++));
					continue;
				}
				if (parName.equalsIgnoreCase("bottom-active"))
				{
					numPar++;
					if (numPar < keywords.size())
						cNums.bottomActive = TextUtils.atoi(keywords.get(numPar++));
					continue;
				}
				if (parName.equalsIgnoreCase("left-active"))
				{
					numPar++;
					if (numPar < keywords.size())
						cNums.leftActive = TextUtils.atoi(keywords.get(numPar++));
					continue;
				}
				if (parName.equalsIgnoreCase("right-active"))
				{
					numPar++;
					if (numPar < keywords.size())
						cNums.rightActive = TextUtils.atoi(keywords.get(numPar++));
					continue;
				}
				return "Unknown option '" + parName + "' for SET LEAF-CELL-NUMBERS command";
			}
			setLeafCellNums(leafCell, cNums);
			return null;
		}

		if (whatToSet.equalsIgnoreCase("node-name"))
		{
			// check for sufficient parameters
			if (keywords.size() <= 4) return "Insufficent parameters for SET NODE-NAME command";

			// search for instance
			String instName = keywords.get(2);
			SCNiTree instPtr = findNi(curSCCell, instName);
			if (instPtr == null)
				return "Cannot find instance '" + instName + "' in SET NODE-NAME command";

			// search for port on instance
			String portName = keywords.get(3);
			SCNiPort iPort;
			for (iPort = instPtr.ports; iPort != null; iPort = iPort.next)
			{
				if (instPtr.type == LEAFCELL)
				{
					Export e = (Export)iPort.port;
					if (e.getName().equalsIgnoreCase(portName)) break;
				} else if (instPtr.type == COMPLEXCELL)
				{
					SCPort scp = (SCPort)iPort.port;
					if (scp.name.equalsIgnoreCase(portName)) break;
				}
			}
			if (iPort == null)
				return "Cannot find port '" + portName + "' on instance '" + instName + "' in SET NODE-NAME command";

			// set extracted node name if possible
			if (iPort.extNode == null) return "Cannot find extracted node to set name in SET NODE-NAME command";
			iPort.extNode.name = keywords.get(4);
			return null;
		}

		if (whatToSet.equalsIgnoreCase("port-direction"))
		{
			String cellName = keywords.get(2);
			String portName = keywords.get(3);
			SCCell cell;
			for (cell = scCells; cell != null; cell = cell.next)
			{
				if (cell.name.equalsIgnoreCase(cellName)) break;
			}
			int bits = 0;
			if (cell == null)
			{
				Cell leafCell = findLeafCell(cellName);
				if (leafCell == null)
					return "Cannot find cell '" + cellName + "'";
				Export leafPort = leafCell.findExport(portName);
				if (leafPort  == null) return "Cannot find port '" + portName + "' on cell '" + cellName + "'";
			} else
			{
				SCPort port;
				for (port = cell.ports; port != null; port = port.next)
				{
					if (port.name.equalsIgnoreCase(portName)) break;
				}
				if (port == null)
					return "Cannot find port '" + portName + "' on cell '" + cellName + "'";
				bits = port.bits;
			}
			bits &= ~PORTDIRMASK;
			String dir = keywords.get(4);
			for(int i=0; i<dir.length(); i++)
			{
				char dirCh = dir.charAt(i);
				switch (dirCh)
				{
					case 'u': bits |= PORTDIRUP;   break;
					case 'd': bits |= PORTDIRDOWN;   break;
					case 'r': bits |= PORTDIRRIGHT;   break;
					case 'l': bits |= PORTDIRLEFT;   break;
					default:
						return "Unknown port direction specifier '" + dir + "'";
				}
			}
			return null;
		}
		return "Unknown option '" + whatToSet+ "' for SET command";
	}

	/**
	 * Method to fill in the cell_nums structure for the indicated leaf cell.
	 */
	static SCCellNums getLeafCellNums(Cell leafCell)
	{
		SCCellNums sNums = new SCCellNums();
//		// check if variable exits
//		var = getvalkey((INTBIG)leafCell, VNODEPROTO, VINTEGER|VISARRAY, sc_numskey);
//		if (var != NOVARIABLE)
//		{
//			iarray = (int *)nums;
//			i = sizeof(SCCellNums) / sizeof(int);
//			iarray = (int *)nums;
//			jarray = (int *)var->addr;
//			for (j = 0; j < i; j++) iarray[j] = jarray[j];
//		}
		return sNums;
	}

	/**
	 * Method to set the cell_nums variable for the indicated leaf cell.
	 */
	static void setLeafCellNums(Cell leafCell, SCCellNums nums)
	{
//		VARIABLE	*var;
//		int		i, j, *iarray;
//		INTBIG   *jarray;
//
//		i = sizeof(SCCellNums) / sizeof(int);
//
//		// check if variable exits
//		var = getvalkey((INTBIG)leafCell, VNODEPROTO, VINTEGER|VISARRAY, sc_numskey);
//		if (var == NOVARIABLE)
//		{
//			if ((jarray = emalloc((i + 1) * sizeof(INTBIG), sc_tool->cluster)) == 0)
//				return(Sc_seterrmsg(SC_NOMEMORY));
//			iarray = (int *)nums;
//			for (j = 0; j < i; j++) jarray[j] = iarray[j];
//			jarray[j] = -1;
//			if (setvalkey((INTBIG)leafCell, VNODEPROTO, sc_numskey, (INTBIG)jarray,
//				VINTEGER | VISARRAY) == NOVARIABLE)
//					return(Sc_seterrmsg(SC_NOSET_CELL_NUMS));
//			return(SC_NOERROR);
//		}
//		iarray = (int *)nums;
//		jarray = (INTBIG *)var->addr;
//		for (j = 0; j < i; j++) jarray[j] = iarray[j];
//		return(SC_NOERROR);
	}

	/**
	 * Method to find the named cell.
	 * Looks in the main cell library, too.
	 */
	private Cell findLeafCell(String name)
	{
		NodeProto np = Cell.findNodeProto(name);
		if (!(np instanceof Cell)) np = null;
		Cell cell = (Cell)np;
		Library lib = Library.findLibrary(SilComp.SCLIBNAME);
		if (cell == null && lib != null)
		{
			cell = lib.findNodeProto(name);
			if (cell == null) return null;
		}
		if (cell != null)
		{
			Cell layCell = cell.otherView(View.LAYOUT);
			if (layCell != null) cell = layCell;
		}
		return cell;
	}

	/**************************************** NETLIST READING: THE EXTRACT KEYWORD ****************************************/

	/**
	 * Method to extract the node netlist for a given cell.
	 */
	private String extract(List keywords)
	{
		if (curSCCell == null) return "No cell selected";
		extractClearFlag(curSCCell);
		curSCCell.exNodes = null;

		extractFindNodes(curSCCell);

		// get ground nodes
		curSCCell.ground = new ExtNode();
		curSCCell.ground.name = "ground";
		curSCCell.ground.flags = 0;
		curSCCell.ground.ptr = null;
		curSCCell.ground.firstPort = null;
		curSCCell.ground.next = null;
		ExtNode oldNList = curSCCell.exNodes;
		for (ExtNode nList = curSCCell.exNodes; nList != null; nList = nList.next)
		{
			ExtPort oldPList = nList.firstPort;
			ExtPort pList;
			for (pList = nList.firstPort; pList != null; pList = pList.next)
			{
				if (pList.node.number == GND)
				{
					curSCCell.ground.firstPort = nList.firstPort;
					if (oldNList == nList)
					{
						curSCCell.exNodes = nList.next;
					} else
					{
						oldNList.next = nList.next;
					}
					if (oldPList == pList)
					{
						curSCCell.ground.firstPort = pList.next;
					} else
					{
						oldPList.next = pList.next;
					}
					break;
				}
				oldPList = pList;
			}
			if (pList != null) break;
			oldNList = nList;
		}
		for (ExtPort pList = curSCCell.ground.firstPort; pList != null; pList = pList.next)
			pList.port.extNode = curSCCell.ground;

		// get power nodes
		curSCCell.power = new ExtNode();
		curSCCell.power.name = "power";
		curSCCell.power.flags = 0;
		curSCCell.power.ptr = null;
		curSCCell.power.firstPort = null;
		curSCCell.power.next = null;
		oldNList = curSCCell.exNodes;
		for (ExtNode nList = curSCCell.exNodes; nList != null; nList = nList.next)
		{
			ExtPort oldPList = nList.firstPort;
			ExtPort pList;
			for (pList = nList.firstPort; pList != null; pList = pList.next)
			{
				if (pList.node.number == PWR)
				{
					curSCCell.power.firstPort = nList.firstPort;
					if (oldNList == nList)
					{
						curSCCell.exNodes = nList.next;
					} else
					{
						oldNList.next = nList.next;
					}
					if (oldPList == pList)
					{
						curSCCell.power.firstPort = pList.next;
					} else
					{
						oldPList.next = pList.next;
					}
					break;
				}
				oldPList = pList;
			}
			if (pList != null) break;
			oldNList = nList;
		}
		for (ExtPort pList = curSCCell.power.firstPort; pList != null; pList = pList.next)
			pList.port.extNode = curSCCell.power;

		extractFindPower(curSCCell, curSCCell);

		extractCollectUnconnected(curSCCell);

		// give the names of the cell ports to the extracted node
		for (SCPort port = curSCCell.ports; port != null; port = port.next)
		{
			switch (port.bits & PORTTYPE)
			{
				case PWRPORT:
				case GNDPORT:
					break;
				default:
					// Note that special nodes only have one niport
					port.node.ports.extNode.name = port.name;
					break;
			}
		}

		// give arbitrary names to unnamed extracted nodes
		int nodenum = 2;
		for (ExtNode ext = curSCCell.exNodes; ext != null; ext = ext.next)
		{
			if (ext.name == null) ext.name = "n" + (nodenum++);
		}
		return null;
	}

	/**
	 * Method to clear the extract pointer on all node instance ports.
	 */
	private void extractClearFlag(SCCell cell)
	{
		for (SCNiTree ntp : cell.niList)
		{
			ntp.flags &= Place.BITS_EXTRACT;
			for (SCNiPort port = ntp.ports; port != null; port = port.next)
				port.extNode = null;
		}
	}

	/**
	 * Method to go though the INSTANCE list, finding all resultant connections.
	 */
	private void extractFindNodes(SCCell cell)
	{
		for (SCNiTree niTree : cell.niList)
		{
			niTree.flags |= Place.BITS_EXTRACT;
			for (ConList cl = niTree.connect; cl != null; cl = cl.next)
			{
				extractSnake(niTree, cl.portA, cl);
			}
		}
	}

	/**
	 * Method to snake through connection list extracting common connections.
	 */
	private void extractSnake(SCNiTree nodeA, SCNiPort portA, ConList cl)
	{
		for ( ; cl != null; cl = cl.next)
		{
			if (cl.portA != portA) continue;
			if (portA != null && portA.extNode != null)
			{
				if (!(cl.portB != null && cl.portB.extNode != null))
				{
					extractAddNode(portA.extNode, cl.nodeB, cl.portB);
					if ((cl.nodeB.flags & Place.BITS_EXTRACT) == 0)
					{
						cl.nodeB.flags |= Place.BITS_EXTRACT;
						extractSnake(cl.nodeB, cl.portB, cl.nodeB.connect);
						cl.nodeB.flags ^= Place.BITS_EXTRACT;
					}
				}
			} else
			{
				if (cl.portB != null && cl.portB.extNode != null)
				{
					extractAddNode(cl.portB.extNode, nodeA, portA);
				} else
				{
					ExtNode common = extractAddNode(null, nodeA, portA);
					common = extractAddNode(common, cl.nodeB, cl.portB);
					if ((cl.nodeB.flags & Place.BITS_EXTRACT) == 0)
					{
						cl.nodeB.flags |= Place.BITS_EXTRACT;
						extractSnake(cl.nodeB, cl.portB, cl.nodeB.connect);
						cl.nodeB.flags ^= Place.BITS_EXTRACT;
					}
				}
			}
		}
	}

	/**
	 * Method to add a node and port to a ExtNode list.
	 * Modify the root if necessary.
	 */
	private ExtNode extractAddNode(ExtNode simNode, SCNiTree node, SCNiPort port)
	{
		ExtPort newPort = new ExtPort();
		if (simNode == null)
		{
			simNode = new ExtNode();
			simNode.firstPort = newPort;
			simNode.flags = 0;
			simNode.ptr = null;
			simNode.name = null;
			newPort.node = node;
			newPort.port = port;
			if (port != null)
				port.extNode = simNode;
			newPort.next = null;
			simNode.next = curSCCell.exNodes;
			curSCCell.exNodes = simNode;
		} else
		{
			newPort.node = node;
			newPort.port = port;
			if (port != null)
				port.extNode = simNode;
			newPort.next = simNode.firstPort;
			simNode.firstPort = newPort;
		}
		return simNode;
	}

	/**
	 * Method to find the implicit power and ground ports.
	 * Does a search of the instance tree and adds to the appropriate port list.
	 * Skips over the dummy ground and power instances and special cells.
	 */
	private void extractFindPower(SCCell cell, SCCell vars)
	{
		for (SCNiTree ntp : cell.niList)
		{
			// process node
			if (ntp.number > PWR)
			{
				switch (ntp.type)
				{
					case COMPLEXCELL:
						break;
					case SPECIALCELL:
						break;
					case LEAFCELL:
						for (SCNiPort port = ntp.ground; port != null; port = port.next)
						{
							ExtPort pList = new ExtPort();
							pList.node = ntp;
							pList.port = port;
							port.extNode = vars.ground;
							pList.next = vars.ground.firstPort;
							vars.ground.firstPort = pList;
						}
						for (SCNiPort port = ntp.power; port != null; port = port.next)
						{
							ExtPort pList = new ExtPort();
							pList.node = ntp;
							pList.port = port;
							port.extNode = vars.power;
							pList.next = vars.power.firstPort;
							vars.power.firstPort = pList;
						}
						break;
					default:
						break;
				}
			}
		}
	}

	/**************************************** PLACEMENT ****************************************/

	/**
	 * Method to flatten all complex cells in the current cell.
	 * It does this by creating instances of all instances from the
	 * complex cells in the current cell.  To insure the uniqueness of all
	 * instance names, the new instances have names "parent_inst.inst"
	 * where "parent_inst" is the name of the instance being expanded and
	 * "inst" is the name of the subinstance being pulled up.
	 */
	private String pull()
	{
		// check if a cell is currently selected
		if (curSCCell == null) return "No cell selected";

		// remember the original ones and delete them later
		List<SCNiTree> cellList = new ArrayList<SCNiTree>();
		for (SCNiTree inst : curSCCell.niList)
		{
			if (inst.type == COMPLEXCELL)
				cellList.add(inst);
		}

		// expand all instances of complex cell type
		for (SCNiTree inst : cellList)
		{
			String err = pullInst(inst, curSCCell);
			if (err != null) return err;
		}

		// now remove the original ones
		List<SCNiTree> deleteList = new ArrayList<SCNiTree>();
		for (SCNiTree inst : curSCCell.niList)
		{
			if (inst.type == COMPLEXCELL)
				deleteList.add(inst);
		}
		for (SCNiTree inst : deleteList)
		{
			curSCCell.niList.remove(inst);
		}
		return null;
	}

	/**
	 * Method to pull the indicated instance of a complex cell into the indicated parent cell.
	 * @param inst instance to be pulled up.
	 * @param cell parent cell.
	 */
	private String pullInst(SCNiTree inst, SCCell cell)
	{
		SCCell subCell = (SCCell)inst.np;

		// first create components
		for (SCNiTree subInst : subCell.niList)
		{
			if (subInst.type != SPECIALCELL)
			{
				List<String> createPars = new ArrayList<String>();
				createPars.add("create");
				createPars.add("instance");
				createPars.add(inst.name + "." + subInst.name);
				if (subInst.type == LEAFCELL)
				{
					createPars.add(((Cell)subInst.np).getName());
				} else
				{
					createPars.add(((SCCell)subInst.np).name);
				}
				String err = create(createPars);
				if (err != null) return err;
			}
		}

		// create connections among these subinstances by using the
		// subCell's extracted node list.  Also resolve connections
		// to the parent cell instances by using exported port info.
		for (ExtNode eNode = subCell.exNodes; eNode != null; eNode = eNode.next)
		{
			ExtNode bNode = null;

			// check if the extracted node is an exported node
			for (SCNiPort iPort = inst.ports; iPort != null; iPort = iPort.next)
			{
				if (((SCPort)(iPort.port)).node.ports.extNode == eNode)
				{
					bNode = iPort.extNode;
					break;
				}
			}
			if (bNode == null)
			{
				// this is a new internal node
				bNode = new ExtNode();
				bNode.name = null;
				bNode.firstPort = null;
				bNode.ptr = null;
				bNode.flags = 0;
				bNode.next = cell.exNodes;
				cell.exNodes = bNode;
			}

			// add ports to extracted node bNode
			for (ExtPort ePort = eNode.firstPort; ePort != null; ePort = ePort.next)
			{
				// only add leaf cells or complex cells
				if (ePort.node.type != SPECIALCELL)
				{
					ExtPort nport = new ExtPort();
					nport.node = findNi(cell, inst.name + "." + ePort.node.name);

					// add reference to extracted node to instance port list
					for (SCNiPort iPort = nport.node.ports; iPort != null; iPort = iPort.next)
					{
						if (iPort.port == ePort.port.port)
						{
							nport.port = iPort;
							iPort.extNode = bNode;
							nport.next = bNode.firstPort;
							bNode.firstPort = nport;
							break;
						}
					}
				}
			}
		}

		// add power ports for new instances
		for (ExtPort ePort = subCell.power.firstPort; ePort != null; ePort = ePort.next)
		{
			if (ePort.node.type == SPECIALCELL) continue;
			ExtPort nport = new ExtPort();
			nport.node = findNi(cell, inst.name + "." + ePort.node.name);

			// add reference to extracted node to instance port list
			SCNiPort iPort;
			for (iPort = nport.node.ports; iPort != null; iPort = iPort.next)
			{
				if (iPort.port == ePort.port.port)
				{
					nport.port = iPort;
					iPort.extNode = cell.power;
					break;
				}
			}
			if (iPort == null)
			{
				for (iPort = nport.node.power; iPort != null; iPort = iPort.next)
				{
					if (iPort.port == ePort.port.port)
					{
						nport.port = iPort;
						iPort.extNode = cell.power;
						break;
					}
				}
			}
			nport.next = cell.power.firstPort;
			cell.power.firstPort = nport;
		}

		// remove references to original instance in power list
		ExtPort nPort = cell.power.firstPort;
		for (ExtPort ePort = cell.power.firstPort; ePort != null; ePort = ePort.next)
		{
			if (ePort.node == inst)
			{
				if (ePort == nPort)
				{
					cell.power.firstPort = ePort.next;
					nPort = ePort.next;
				} else
				{
					nPort.next = ePort.next;
				}
			} else
			{
				nPort = ePort;
			}
		}

		// add ground ports
		for (ExtPort ePort = subCell.ground.firstPort; ePort != null; ePort = ePort.next)
		{
			if (ePort.node.type == SPECIALCELL) continue;
			nPort = new ExtPort();
			nPort.node = findNi(cell, inst.name + "." + ePort.node.name);

			// add reference to extracted node to instance port list
			SCNiPort iPort;
			for (iPort = nPort.node.ports; iPort != null; iPort = iPort.next)
			{
				if (iPort.port == ePort.port.port)
				{
					nPort.port = iPort;
					iPort.extNode = cell.ground;
					break;
				}
			}
			if (iPort == null)
			{
				for (iPort = nPort.node.ground; iPort != null; iPort = iPort.next)
				{
					if (iPort.port == ePort.port.port)
					{
						nPort.port = iPort;
						iPort.extNode = cell.ground;
						break;
					}
				}
			}
			nPort.next = cell.ground.firstPort;
			cell.ground.firstPort = nPort;
		}

		// remove references to original instance in ground list
		nPort = cell.ground.firstPort;
		for (ExtPort ePort = cell.ground.firstPort; ePort != null; ePort = ePort.next)
		{
			if (ePort.node == inst)
			{
				if (ePort == nPort)
				{
					cell.ground.firstPort = ePort.next;
					nPort = ePort.next;
				} else
				{
					nPort.next = ePort.next;
				}
			} else
			{
				nPort = ePort;
			}
		}

		// remove references to instance in exported node list
		for (ExtNode eNode = cell.exNodes; eNode != null; eNode = eNode.next)
		{
			nPort = eNode.firstPort;
			for (ExtPort ePort = eNode.firstPort; ePort != null; ePort = ePort.next)
			{
				if (ePort.node == inst)
				{
					if (ePort == nPort)
					{
						eNode.firstPort = ePort.next;
						nPort = ePort.next;
					} else
					{
						nPort.next = ePort.next;
					}
				} else
				{
					nPort = ePort;
				}
			}
		}

		// find the value of the largest generically named extracted node
		int oldNum = 0;
		for (ExtNode eNode = cell.exNodes; eNode != null; eNode = eNode.next)
		{
			if (eNode.name != null)
			{
				int sPtr = 0;
				char firstCh = eNode.name.charAt(0);
				if (Character.toUpperCase(firstCh) == 'N')
				{
					sPtr++;
					while (sPtr < eNode.name.length())
					{
						if (!Character.isDigit(eNode.name.charAt(sPtr))) break;
						sPtr++;
					}
					if (sPtr >= eNode.name.length())
					{
						int newnum = TextUtils.atoi(eNode.name.substring(1));
						if (newnum > oldNum)
							oldNum = newnum;
					}
				}
			}
		}

		// set the name of any unnamed nodes
		for (ExtNode eNode = cell.exNodes; eNode != null; eNode = eNode.next)
		{
			if (eNode.name == null)
			{
				eNode.name = "n" + (++oldNum);
			}
		}

		// flatten any subinstances which are also complex cells
		for (SCNiTree subInst : subCell.niList)
		{
			if (subInst.type == COMPLEXCELL)
			{
				SCNiTree ninst = findNi(cell, inst.name + "." + subInst.name);
				String err = pullInst(ninst, cell);
				if (err != null) return err;
			}
		}
		return null;
	}
//
//	/***********************************************************************
//	Module:  Sc_extract_print_nodes
//	------------------------------------------------------------------------
//	Description:
//		Print the common nodes found.
//	------------------------------------------------------------------------
//	*/
//
//	void Sc_extract_print_nodes(SCCell *vars)
//	{
//		int		i;
//		ExtNode	*simNode;
//		ExtPort	*pList;
//		CHAR	*portname;
//
//		i = 0;
//		if (vars->ground)
//		{
//			ttyputmsg(M_("Node %d  %s:"), i, vars->ground->name);
//			for (pList = vars->ground->firstPort; pList; pList = pList->next)
//			{
//				switch (pList->node->type)
//				{
//					case SPECIALCELL:
//						portname = M_("Special");
//						break;
//					case COMPLEXCELL:
//						portname = ((SCPort *)(pList->port->port))->name;
//						break;
//					case LEAFCELL:
//						portname = Sc_leaf_port_name(pList->port->port);
//						break;
//					default:
//						portname = M_("Unknown");
//						break;
//				}
//				ttyputmsg(x_("    %-20s    %s"), pList->node->name, portname);
//			}
//		}
//		i++;
//
//		if (vars->power)
//		{
//			ttyputmsg(M_("Node %d  %s:"), i, vars->power->name);
//			for (pList = vars->power->firstPort; pList; pList = pList->next)
//			{
//				switch (pList->node->type)
//				{
//					case SPECIALCELL:
//						portname = M_("Special");
//						break;
//					case COMPLEXCELL:
//						portname = ((SCPort *)(pList->port->port))->name;
//						break;
//					case LEAFCELL:
//						portname = Sc_leaf_port_name(pList->port->port);
//						break;
//					default:
//						portname = M_("Unknown");
//						break;
//				}
//				ttyputmsg(x_("    %-20s    %s"), pList->node->name, portname);
//			}
//		}
//		i++;
//
//		for (simNode = vars->exNodes; simNode; simNode = simNode->next)
//		{
//			ttyputmsg(M_("Node %d  %s:"), i, simNode->name);
//			for (pList = simNode->firstPort; pList; pList = pList->next)
//			{
//				switch (pList->node->type)
//				{
//					case SPECIALCELL:
//						portname = M_("Special");
//						break;
//					case COMPLEXCELL:
//						portname = ((SCPort *)(pList->port->port))->name;
//						break;
//					case LEAFCELL:
//						portname = Sc_leaf_port_name(pList->port->port);
//						break;
//					default:
//						portname = M_("Unknown");
//						break;
//				}
//				ttyputmsg(x_("    %-20s    %s"), pList->node->name, portname);
//			}
//			i++;
//		}
//	}
//

	/**
	 * Method to collect the unconnected ports and create an extracted node for each.
	 */
	private void extractCollectUnconnected(SCCell cell)
	{
		for (SCNiTree nPtr : cell.niList)
		{
			// process node
			switch (nPtr.type)
			{
				case COMPLEXCELL:
				case LEAFCELL:
					for (SCNiPort port = nPtr.ports; port != null; port = port.next)
					{
						if (port.extNode == null)
						{
							ExtNode ext = new ExtNode();
							ext.name = null;
							ExtPort ePort = new ExtPort();
							ePort.node = nPtr;
							ePort.port = port;
							ePort.next = null;
							ext.firstPort = ePort;
							ext.flags = 0;
							ext.ptr = null;
							ext.next = cell.exNodes;
							cell.exNodes = ext;
							port.extNode = ext;
						}
					}
					break;
				default:
					break;
			}
		}
	}

	/**
	 * Method to return the type of the leaf port.
	 * @param leafPort pointer to leaf port.
	 * @return type of port.
	 */
	static int getLeafPortType(Export leafPort)
	{
		if (leafPort.isPower()) return PWRPORT;
		if (leafPort.isGround()) return GNDPORT;
		if (leafPort.getCharacteristic() == PortCharacteristic.BIDIR) return BIDIRPORT;
		if (leafPort.getCharacteristic() == PortCharacteristic.OUT) return OUTPORT;
		if (leafPort.getCharacteristic() == PortCharacteristic.IN) return INPORT;
		return UNPORT;
	}

	/**
	 * Method to return the directions that a port can be attached to.
	 * Values can be up, down, left, right.
	 */
	static int getLeafPortDirection(PortProto port)
	{
		return PORTDIRUP | PORTDIRDOWN;
	}

}
