/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: Tegas.java
 * Original C Code written by T.J.Goodman, University of Canterbury, N.Z.
 * Translated to Java by Steven M. Rubin.
 *
 * Copyright (c) 2004, Static Free Software. 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.io.output;

import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.hierarchy.Nodable;
import com.sun.electric.database.network.Global;
import com.sun.electric.database.network.Netlist;
import com.sun.electric.database.network.Network;
import com.sun.electric.database.prototype.PortCharacteristic;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.Connection;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.PrimitivePort;
import com.sun.electric.tool.simulation.SimulationTool;
import com.sun.electric.tool.user.User;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

/**
 * This is the netlister for Tegas.
 */
public class Tegas extends Topology
{
	private static final int MAXLENGTH    = 80;
	private static final int MAXNAMECHARS = 12;

	private Map<Nodable,Integer> nodeNames;
	private Map<ArcInst,Integer> implicitInverters;
	private Netlist netList;
	private TegasPreferences localPrefs;

	public static class TegasPreferences extends OutputPreferences
    {
		String workingDirectory = "";

        public TegasPreferences(boolean factory) {
            super(factory);
            workingDirectory = factory ? "" : User.getWorkingDirectory();
        }

        public Output doOutput(Cell cell, VarContext context, String filePath)
        {
    		Tegas out = new Tegas(this);
    		if (out.openTextOutputStream(filePath)) return out.finishWrite();
    		if (out.writeCell(cell, context)) return out.finishWrite();
    		if (out.closeTextOutputStream()) return out.finishWrite();
    		System.out.println(filePath + " written");
            return out.finishWrite();
        }
    }

	/**
	 * Creates a new instance of the Tegas netlister.
	 */
	Tegas(TegasPreferences tp) { localPrefs = tp; }

	protected void start()
	{
		// parameters to the output-line-length limit and how to break long lines
		setOutputWidth(MAXLENGTH, true);

		writeWidthLimited("/* GENERATED BY THE ELECTRIC VLSI DESIGN SYSTEM */\n\n");
		writeWidthLimited("COMPILE  ;\n\n");

		// get library name, check validity
		String libname = getLibraryName(topCell);

		int length = libname.length();
		if (length > 12)
		{
            reportWarning("Library name exceeds 12 characters, The name used for the\n" +
			"TDL directory will be truncated to :- " + convertName(libname));
		}

		// check library name
		String str1 = convertName(libname);
		if (isReservedWord(str1))
		{
			reportError(str1 + " IS A RESERVED WORD, RENAME LIBRARY AND RE-RUN");
			return;
		}

		// write "directory line" to file
		writeWidthLimited("DIRECTORY:  " + str1 + " ;\n\n");

		writeWidthLimited("OPTIONS:   REPLACE  ;\n\n");
	}

	protected void done()
	{
		writeWidthLimited(" END COMPILE;\n\n ");
	}

	/**
	 * Method to write cellGeom
	 */
	protected void writeCellTopology(Cell cell, String cellName, CellNetInfo cni, VarContext context, Topology.MyCellInfo info)
	{
		// MODULE
		writeWidthLimited("MODULE:  ");
		writeWidthLimited(convertName(cell.describe(false)));
		writeWidthLimited(";\n\n");

		netList = cni.getNetList();

		// INPUTS
		if (cell.getNumPorts() > 0)
		{
			StringBuffer infstr = new StringBuffer();
			infstr.append("INPUTS:\n");
			for(Iterator<CellSignal> it = cni.getCellSignals(); it.hasNext(); )
			{
				CellSignal cs = it.next();
				if (!cs.isExported()) continue;
				Export e = cs.getExport();
				if (e.getCharacteristic() != PortCharacteristic.IN) continue;
				if (isReservedWord(convertName(e.getName())))
				{
					reportError("ERROR: " + convertName(e.getName()) + " IS A RESERVED WORD");
				}
				infstr.append("   " + convertName(e.getName()) + "\n");
			}
			infstr.append(";\n\n");
			writeWidthLimited(infstr.toString());

			// OUTPUTS
			infstr = new StringBuffer();
			infstr.append("OUTPUTS:\n");
				for(Iterator<CellSignal> it = cni.getCellSignals(); it.hasNext(); )
			{
				CellSignal cs = it.next();
				if (!cs.isExported()) continue;
				Export e = cs.getExport();
				if (e.getCharacteristic() == PortCharacteristic.OUT)
				{
					infstr.append("   " + convertName(e.getName()) + "\n");
				} else if (e.getCharacteristic() != PortCharacteristic.IN)
				{
					reportError("EXPORT " + e.getName() + " MUST BE EITHER INPUT OR OUTPUT");
				}
			}
			infstr.append(";\n\n");
			writeWidthLimited(infstr.toString());
		}

		// USE
		Set<String> instanceDeclarations = new TreeSet<String>();
		Set<String> gateDeclarations = new TreeSet<String>();
		for(Iterator<Nodable> it = netList.getNodables(); it.hasNext(); )
		{
			Nodable no = it.next();
			if (no.isCellInstance())
			{
				// This case can either be for an existing  user defined module in this
				// directory or for a module from the MASTER directory
				Cell subCell = (Cell)no.getProto();
				String instName = getNodeProtoName(no);
				instanceDeclarations.add(instName + " = " + instName + "///" + getLibraryName(subCell));
				continue;
			}

			NodeInst ni = (NodeInst)no;
			PrimitiveNode.Function fun = ni.getFunction();
			if (fun == PrimitiveNode.Function.GATEAND || fun == PrimitiveNode.Function.GATEOR || fun == PrimitiveNode.Function.GATEXOR)
			{
				// Count number of inputs
				String gateName = getGateName(ni);
				int gateWidth = getGateWidth(ni);
				gateDeclarations.add(gateWidth + "-" + gateName + " = " + gateName + "(" + gateWidth + ",1)");
				continue;
			}

			if (fun == PrimitiveNode.Function.SOURCE ||
                fun.isResistor() || // == PrimitiveNode.Function.RESIST ||
                fun.isCapacitor() || // == PrimitiveNode.Function.CAPAC ||
				fun == PrimitiveNode.Function.DIODE || fun == PrimitiveNode.Function.INDUCT || fun == PrimitiveNode.Function.METER)
			{
				reportError("CANNOT HANDLE " + ni.getProto().describe(true) + " NODES");
				continue;
			}
		}
		int numDecls = instanceDeclarations.size() + gateDeclarations.size();
		if (numDecls > 0) writeWidthLimited("USE:\n\n");
		int countDecls = 1;
		for(String decl : instanceDeclarations)
		{
			if (countDecls < numDecls) decl += ","; else decl += ";";
			countDecls++;
			writeWidthLimited("    " + decl + "\n");
		}
		for(String decl : gateDeclarations)
		{
			if (countDecls < numDecls) decl += ","; else decl += ";";
			countDecls++;
			writeWidthLimited("    " + decl + "\n");
		}
		if (numDecls > 0) writeWidthLimited("\n");

		// DEFINE
		writeWidthLimited("DEFINE:\n");

		// count no. of inverters (negated arcs not attached to logic primitives)
		implicitInverters = new HashMap<ArcInst,Integer>();
		int count = 1;
		for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
		{
			ArcInst ai = it.next();
			for(int i=0; i<2; i++)
			{
				if (!ai.isNegated(i)) continue;
				if (ai.getPortInst(i).getPortProto().getCharacteristic() == PortCharacteristic.OUT)
				{
					PrimitiveNode.Function fun = ai.getPortInst(i).getNodeInst().getFunction();
					if (fun == PrimitiveNode.Function.GATEAND || fun == PrimitiveNode.Function.GATEOR ||
						fun == PrimitiveNode.Function.GATEXOR || fun == PrimitiveNode.Function.BUFFER) continue;
				}
				implicitInverters.put(ai, Integer.valueOf(count));
				count++;
			}
		}

		// name every node
		nodeNames = new HashMap<Nodable,Integer>();
		for(Iterator<Nodable> it = netList.getNodables(); it.hasNext(); )
		{
			Nodable no = it.next();
			PrimitiveNode.Function fun = getNodableFunction(no);
			if (fun.isTransistor() || fun == PrimitiveNode.Function.GATEXOR ||
				fun == PrimitiveNode.Function.GATEAND || fun == PrimitiveNode.Function.GATEOR ||
				fun == PrimitiveNode.Function.BUFFER || fun.isFlipFlop() || no.isCellInstance())
			{
				nodeNames.put(no, Integer.valueOf(count++));
				continue;
			}
		}

		// write the nodes
		boolean wrotePower = false, wroteGround = false;
		for(Iterator<Nodable> it = netList.getNodables(); it.hasNext(); )
		{
			Nodable no = it.next();
			PrimitiveNode.Function fun = getNodableFunction(no);
			if (fun.isPin() || fun == PrimitiveNode.Function.ART) continue;
			if (fun == PrimitiveNode.Function.CONPOWER)
			{
				if (wrotePower) continue;
				wrotePower = true;
				writeWidthLimited(getNameOfPower((NodeInst)no));
				continue;
			}
			if (fun == PrimitiveNode.Function.CONGROUND)
			{
				if (wroteGround) continue;
				wroteGround = true;
				writeWidthLimited(getNameOfPower((NodeInst)no));
				continue;
			}

			// handle nodeinst descriptions
			if (fun.isTransistor() || fun == PrimitiveNode.Function.GATEXOR ||
				fun == PrimitiveNode.Function.GATEAND || fun == PrimitiveNode.Function.GATEOR ||
				fun == PrimitiveNode.Function.BUFFER || fun.isFlipFlop() || no.isCellInstance())
			{
				String str1 = getOutputSignals(no, context, cni) + " = " + getNodeProtoName(no) + getInputSignals(no, context, cni) + getGateDelay(no) + ";\n";
				writeWidthLimited(str1);
				continue;
			}
			reportWarning("NODETYPE " + no.getProto() + " NOT SUPPORTED");
		}

		// end module
		writeWidthLimited(" END MODULE;\n\n");
	}

	/****************************** MAIN BLOCKS ******************************/

	/**
	 * Method to write the output signals for a Nodable.
	 * @param no the Nodable to write.
	 * @param context the NodeInst's context down the hierarchy.
	 * @param cni the CellNetInfo for the node's parent cell.
	 * @return the output signals for the Nodable.
	 */
	private String getOutputSignals(Nodable no, VarContext context, CellNetInfo cni)
	{
		int nodeNumber = -1;
		Integer nodeNum = nodeNames.get(no);
		if (nodeNum != null) nodeNumber = nodeNum.intValue();
		StringBuffer infstr = new StringBuffer();
		infstr.append("U" + nodeNumber + "(");
		PrimitiveNode.Function fun = getNodableFunction(no);

		boolean needComma = false;
		if (no.isCellInstance())
		{
			CellNetInfo subCni = getCellNetInfo(parameterizedName(no, context));
			for(Iterator<CellSignal> it = subCni.getCellSignals(); it.hasNext(); )
			{
				CellSignal subCs = it.next();
				if (!subCs.isExported()) continue;
				Export e = subCs.getExport();
				if (e.getCharacteristic() == PortCharacteristic.IN) continue;

				if (needComma) infstr.append(","); else needComma = true;
				Network net = netList.getNetwork(no, e, subCs.getExportIndex());
				CellSignal cs = cni.getCellSignal(net);
				infstr.append(convertName(cs.getName()));
			}
		} else
		{
			NodeInst ni = (NodeInst)no;
			for(Iterator<PortProto> it = no.getProto().getPorts(); it.hasNext(); )
			{
				PortProto pp = it.next();
				if (fun.isTransistor())
				{
					if (pp.getName().equals("g")) continue;
				} else
				{
					if (pp.getCharacteristic() == PortCharacteristic.IN) continue;
				}

				for(Iterator<Connection> aIt = ni.getConnections(); aIt.hasNext(); )
				{
					Connection con = aIt.next();
					PortInst pi = con.getPortInst();
					if (pi.getPortProto() != pp) continue;
					ArcInst ai = con.getArc();
					if (needComma) infstr.append(","); else needComma = true;
					String nodeName = getConnectionName(con);
					infstr.append(nodeName);

					// if arc on port not negated or a description for an inverter for a negated
					// arc already exists simply write the source name of this port/arc inst.
					if (con.isNegated() && implicitInverters.get(ai) != null)
					{
						// if the negation is at this end (ie nearest this node) write an inverter
						// with the port name of the output as inverter input and the net name of the
						// arc as inverter output. The source name is the net name of the arc
						writeInverter(con, nodeName, "U" + nodeNumber + "." + convertName(pp.getName()));
					}
				}
			}
		}
		infstr.append(")");
		return infstr.toString();
	}

	/**
	 * Method to write the input signals for a Nodable.
	 * @param no the Nodable to write.
	 * @param context the NodeInst's context down the hierarchy.
	 * @param cni the CellNetInfo for the node's parent cell.
	 * @return the input signals for the Nodable.
	 */
	private String getInputSignals(Nodable no, VarContext context, CellNetInfo cni)
	{
		PrimitiveNode.Function fun = getNodableFunction(no);

		if (fun.isFlipFlop())
			return getFlipFlopInputSignals((NodeInst)no);

		if (no.getProto().getNumPorts() == 0) return "";
		StringBuffer infstr = new StringBuffer();
		infstr.append("(");
		boolean first = true;
		if (no.isCellInstance())
		{
			CellNetInfo subCni = getCellNetInfo(parameterizedName(no, context));
			for(Iterator<CellSignal> it = subCni.getCellSignals(); it.hasNext(); )
			{
				CellSignal subCs = it.next();
				if (!subCs.isExported()) continue;
				Export e = subCs.getExport();
				if (e.getCharacteristic() == PortCharacteristic.OUT) continue;

				if (first) first = false; else infstr.append(",");
				Network net = netList.getNetwork(no, e, subCs.getExportIndex());
				CellSignal cs = cni.getCellSignal(net);
				infstr.append(convertName(cs.getName()));
			}
		} else
		{
			NodeInst ni = (NodeInst)no;
			for(Iterator<PortProto> it = no.getProto().getPorts(); it.hasNext(); )
			{
				PrimitivePort pp = (PrimitivePort)it.next();
				if (pp.getCharacteristic() != PortCharacteristic.IN) continue;

				// The transistor "s" port is treated as an inport by electric but is used
				// as an outport by TDL
				if (fun.isTransistor() && pp.getName().startsWith("s")) continue;

				// Buffer primitive has a "c" port not used by TDL
				if (fun == PrimitiveNode.Function.BUFFER && pp.getName().equals("c")) continue;

				boolean portWired = false;
				for(Iterator<Connection> aIt = ni.getConnections(); aIt.hasNext(); )
				{
					Connection con = aIt.next();
					if (con.getPortInst().getPortProto() == pp)
					{
						if (first) first = false; else infstr.append(",");
						infstr.append(getInvertedConnectionName(con));
						portWired = true;

						// if port is not isolated then write one signal only
						if (!pp.isIsolated()) break;
					}
				}
				if (!portWired)
				{
					reportWarning("UNWIRED PORT " + pp.getName());
					if (first) first = false; else infstr.append(",");
					infstr.append("NC");
				}
			}
		}
		infstr.append(")");
		String result = infstr.toString();
		return result;
	}

	/**
	 * Method to write the input signals for flip flops.
	 * @param ni the flip-flop NodeInst to convert.
	 * @return the input signals for the flip-flop.
	 */
	private String getFlipFlopInputSignals(NodeInst ni)
	{
		String [] signals = new String[5];
		int x = 0;
		for(Iterator<PortProto> it = ni.getProto().getPorts(); it.hasNext(); )
		{
			PortProto pp = it.next();
			if (pp.getCharacteristic() == PortCharacteristic.OUT) continue;
			String ptr = "NC";   // if no-connection write NC

			for(Iterator<Connection> pIt = ni.getConnections(); pIt.hasNext(); )
			{
				Connection con = pIt.next();
				if (con.getPortInst().getPortProto() == pp)
				{
					ptr = getInvertedConnectionName(con);
					break;
				}
			}
			for(Iterator<Export> eIt = ni.getExports(); eIt.hasNext(); )
			{
				Export e = eIt.next();
				if (e.getOriginalPort().getPortProto() == pp)
				{
					ptr = convertName(e.getName());
					break;
				}
			}
			if (x >= 5) break;
			signals[x++] = ptr;
		}

		// We now have the signals in 5 arrays ready to be output in the correct
		// order which is 2,0,1,3,4.If the flip flop is a D or T type don't put
		// out array[1][].
		StringBuffer infstr = new StringBuffer();
		infstr.append("(" + signals[2] + "," + signals[0]);

		// JK and SR have one input more than D or T flip flops
		PrimitiveNode.Function fun = ni.getFunction();
		if (fun == PrimitiveNode.Function.FLIPFLOPRSMS || fun == PrimitiveNode.Function.FLIPFLOPRSP || fun == PrimitiveNode.Function.FLIPFLOPRSN ||
			fun == PrimitiveNode.Function.FLIPFLOPJKMS || fun == PrimitiveNode.Function.FLIPFLOPJKP || fun == PrimitiveNode.Function.FLIPFLOPJKN)
		{
			infstr.append("," + signals[1]);
		}

		infstr.append("," + signals[3] + "," + signals[4] + ")");
		return infstr.toString();
	}

	/****************************** HELPER METHODS ******************************/

	/**
	 * Method to determine whether a nodeinst has an output arc that is negated.
	 * @param ni the NodeInst that may be negated.
	 * @return true if the NodeInst is negated (its output is negated).
	 */
	private boolean isNegatedNode(NodeInst ni)
	{
		for(Iterator<Connection> it = ni.getConnections(); it.hasNext(); )
		{
			Connection con = it.next();
			PortInst pi = con.getPortInst();
			PortProto pp = pi.getPortProto();
			if (pp.getCharacteristic() != PortCharacteristic.OUT) continue;
			if (con.isNegated()) return true;
		}
		return false;
	}

	/**
	 * Method to return the TDL delay on a Nodable.
	 * @param no the Nodable to get delay information on.
	 * @return the node delay.  If no delay information is on the node,
	 * returns the default: "/1,1/".
	 */
	private String getGateDelay(Nodable no)
	{
		if (no.isCellInstance()) return "";
		NodeInst ni = (NodeInst)no;

		Variable var = ni.getVar(SimulationTool.RISE_DELAY_KEY);
		String str1 = "/1,";
		if (var != null) str1 = "/" + var.getPureValue(-1) + ",";

		var = ni.getVar(SimulationTool.FALL_DELAY_KEY);
		String str2 = "/1,";
		if (var != null) str2 = "/" + var.getPureValue(-1) + ",";
		return str1 + str2;
	}

	private int getGateWidth(NodeInst ni)
	{
		int inputs = 0;
		for(Iterator<Connection> iIt = ni.getConnections(); iIt.hasNext(); )
		{
			Connection con = iIt.next();
			if (con.getPortInst().getPortProto().getCharacteristic() == PortCharacteristic.IN) inputs++;
		}
		if (inputs < 2)
			reportError("MUST HAVE AT LEAST TWO INPUTS ON " + ni);
		return inputs;
	}

	private String getGateName(NodeInst ni)
	{
		// Determine whether node should be NAND, NOR or NXOR
		boolean negated = false;
		if (isNegatedNode(ni)) negated = true;

		// Write USE description for current node
		PrimitiveNode.Function fun = ni.getFunction();
		if (fun == PrimitiveNode.Function.GATEAND)
		{
			if (negated) return "NAND";
			return "AND";
		}
		if (fun == PrimitiveNode.Function.GATEOR)
		{
			if (negated) return "NOR";
			return "OR";
		}
		if (fun == PrimitiveNode.Function.GATEXOR)
		{
			if (negated) return "NXOR";
			return "XOR";
		}
		return "";
	}

	/**
	 * Method to write out an inverter description for a negated Connection.
	 * @param con the Connection that is inverted.
	 * @param str1 the output signal name.
	 * @param str2 the input signal name.
	 */
	private void writeInverter(Connection con, String str1, String str2)
	{
		Integer index = implicitInverters.get(con.getArc());
		if (index == null) return;

		writeWidthLimited("I" + index + "(" + convertName(str1) + ") = NOT(" + convertName(str2) + ");\n");
		implicitInverters.remove(con.getArc());  // write once only
	}

	/**
	 * Method to return the name of a Power/Ground node.
	 * @param ni the Power/Ground node.
	 * @return the node name (returns an empty string if not applicable).
	 */
	private String getNameOfPower(NodeInst ni)
	{
		// To prevent Un-connected power nodes
		if (!ni.hasConnections())
		{
			reportWarning("PWR / GND NODE UNCONNECTED");
			return "";
		}

		PrimitiveNode.Function fun = ni.getFunction();
		if (fun == PrimitiveNode.Function.CONGROUND || fun == PrimitiveNode.Function.CONPOWER)
		{
			if (ni.hasConnections())
			{
				Connection con = ni.getConnections().next();
				Network net = netList.getNetwork(con.getArc(), 0);
				String decl = net.describe(false) + " = ";
				if (fun == PrimitiveNode.Function.CONPOWER) decl += "PWR"; else
					decl += "GRND";
				return decl + ";\n";
			}
		}

		return "";
	}

	/**
	 * Method to return the name of the network on a given Connection.
	 * @param con the Connection to name.
	 * @return the name of that network.
	 */
	private String getConnectionName(Connection con)
	{
		Network net = netList.getNetwork(con.getArc(), 0);
		if (net != null) return convertName(net.describe(false));
		return "???";
	}

	/**
	 * Method to return the name of the signal on a Connection.
	 * @param con the Connection to name.
	 * @return the name of the Connection network.
	 * Also emits an implicit inverter if there is one.
	 */
	private String getInvertedConnectionName(Connection con)
	{
		String conName = getConnectionName(con);
		ArcInst ai = con.getArc();
		if (!con.isNegated()) return conName;

		// insert an inverter description if a negated arc attached to primitive
		// other than AND,OR,XOR
		Integer index = implicitInverters.get(ai);
		if (index != null)
		{
			String str = "I" + index + ".O";
			writeInverter(con, str, conName);
			return str;
		}
		return conName;
	}

	private PrimitiveNode.Function getNodableFunction(Nodable no)
	{
		PrimitiveNode.Function fun = PrimitiveNode.Function.UNKNOWN;
		if (!no.isCellInstance())
		{
			NodeInst ni = (NodeInst)no;
			fun = ni.getFunction();
		}
		return fun;
	}

	/**
	 * Method to return the TDL name of a Nodable.
	 * @param no the Nodable to report.
	 * @return the name of that Nodable.
	 */
	private String getNodeProtoName(Nodable no)
	{
		if (no.isCellInstance())
		{
			return convertName(no.getProto().describe(false));
		}
		NodeInst ni = (NodeInst)no;
		PrimitiveNode.Function fun = ni.getFunction();
		if (fun == PrimitiveNode.Function.GATEAND || fun == PrimitiveNode.Function.GATEOR || fun == PrimitiveNode.Function.GATEXOR)
		{
			return getGateWidth(ni) + "-" + getGateName(ni);
		}
		if (fun == PrimitiveNode.Function.BUFFER)
		{
			if (isNegatedNode(ni)) return "NOT";
			return "DELAY";
		}
		if (fun.isFlipFlop()) return getFlipFlopName(ni);
		if (fun == PrimitiveNode.Function.TRANS) return "BDSWITCH";
		return "";
	}

	/**
	 * Method to limit a string to the max 12 chars and in upper case for TDL.
	 * @param str a name.
	 * @return the name in upper case and limited to 12 characters.
	 */
	private String convertName(String str)
	{
		if (str.length() > MAXNAMECHARS) str = str.substring(0, MAXNAMECHARS);
		return str.toUpperCase();
	}

	/**
	 * Method to return the library name to use, given a cell.
	 * @param cell the Cell whose library is desired.
	 * @return the library name of the cell.
	 */
	String getLibraryName(Cell cell)
	{
		return convertName(cell.getLibrary().getName());
	}

	/**
	 * Method to return the TDL primitive name of a flip-flop.
	 * @param ni the NodeInst that is a flip-flop.
	 * @return the proper TDL name for that type of flip-flop.
	 */
	private String getFlipFlopName(NodeInst ni)
	{
		PrimitiveNode.Function fun = ni.getFunction();
		if (fun == PrimitiveNode.Function.FLIPFLOPRSMS) return "SRMNE";
		if (fun == PrimitiveNode.Function.FLIPFLOPRSP)  return "SREPE";
		if (fun == PrimitiveNode.Function.FLIPFLOPRSN)  return "SRENE";

		if (fun == PrimitiveNode.Function.FLIPFLOPJKMS) return "JKMNE";
		if (fun == PrimitiveNode.Function.FLIPFLOPJKP)  return "JKEPE";
		if (fun == PrimitiveNode.Function.FLIPFLOPJKN)  return "JKENE";

		if (fun == PrimitiveNode.Function.FLIPFLOPDMS)  return "DMNE";
		if (fun == PrimitiveNode.Function.FLIPFLOPDP)   return "DEPE";
		if (fun == PrimitiveNode.Function.FLIPFLOPDN)   return "DENE";

		if (fun == PrimitiveNode.Function.FLIPFLOPTP || fun == PrimitiveNode.Function.FLIPFLOPTN)
			reportError("T TYPE FLIP-FLOP MUST BE MS");
		return "TMNE";
	}

	private List<String> reservedWords = null;

	/**
	 * Method that takes a string of max length 12 and checks to see if it is a reserved word.
	 * @param str the name to check.
	 * @return true if the word is reserved.
	 * Returns false if the word is not reserved.
	 */
	private boolean isReservedWord(String str)
	{
		if (reservedWords == null)
		{
			reservedWords = new ArrayList<String>();
			BufferedReader br;
			try
			{
				File f = new File(localPrefs.workingDirectory + File.separator + "reservedwords.dat");
				FileReader fr = new FileReader(f);
				br = new BufferedReader(fr);
				for(;;)
				{
					String line = br.readLine();
					if (line == null) break;
					reservedWords.add(line);
				}
				br.close();
				fr.close();
			} catch (IOException e)
			{
				return false;
			}
		}
		if (str.length() < 2) return false;
		for(String match : reservedWords)
		{
			if (match.startsWith(str)) return true;
		}
		return false;
	}

	/****************************** SUBCLASSED METHODS FOR THE TOPOLOGY ANALYZER ******************************/

	/**
	 * Method to adjust a cell name to be safe for Tegas output.
	 * @param name the cell name.
	 * @return the name, adjusted for Tegas output.
	 */
	protected String getSafeCellName(String name) { return name; }

	/** Method to return the proper name of Power */
	protected String getPowerName(Network net) { return ".VDD"; }

	/** Method to return the proper name of Ground */
	protected String getGroundName(Network net) { return "GRND"; }

	/** Method to return the proper name of a Global signal */
	protected String getGlobalName(Global glob) { return glob.getName(); }

	/** Method to report that export names DO take precedence over
	 * arc names when determining the name of the network. */
	protected boolean isNetworksUseExportedNames() { return true; }

	/** Method to report that library names ARE always prepended to cell names. */
	protected boolean isLibraryNameAlwaysAddedToCellName() { return false; }

	/** Method to report that aggregate names (busses) ARE used. */
	protected boolean isAggregateNamesSupported() { return false; }

	/** Abstract method to decide whether aggregate names (busses) can have gaps in their ranges. */
	protected boolean isAggregateNameGapsSupported() { return false; }

	/** Method to report whether input and output names are separated. */
	protected boolean isSeparateInputAndOutput() { return true; }

	/** Abstract method to decide whether netlister is case-sensitive (Verilog) or not (Spice). */
	protected boolean isCaseSensitive() { return true; }

	/**
	 * Method to adjust a network name to be safe for Tegas output.
	 */
	protected String getSafeNetName(String name, boolean bus) { return name; }

    /** Tell the Hierarchy enumerator how to short resistors */
    @Override
    protected Netlist.ShortResistors getShortResistors() { return Netlist.ShortResistors.ALL; }

	/**
	 * Method to tell whether the topological analysis should mangle cell names that are parameterized.
	 */
	protected boolean canParameterizeNames() { return true; }

}
