/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: Spice.java
 * Input/output tool: reader for Spice netlists (.spi)
 * Inspired by Graham Petley, written by Steven M. Rubin, Sun Microsystems.
 *
 * Copyright (c) 2009, 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.io.input;

import com.sun.electric.database.EditingPreferences;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.geometry.Poly;
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.id.CellId;
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.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.technologies.Artwork;
import com.sun.electric.technology.technologies.Schematics;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.placement.Placement;
import com.sun.electric.tool.placement.PlacementAdapter;
import com.sun.electric.tool.placement.PlacementFrame;
import com.sun.electric.tool.placement.PlacementAdapter.PlacementExport;
import com.sun.electric.tool.placement.PlacementAdapter.PlacementNode;
import com.sun.electric.tool.placement.PlacementAdapter.PlacementPort;
import com.sun.electric.tool.placement.PlacementFrame.PlacementNetwork;
import com.sun.electric.tool.simulation.SimulationTool;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.math.Orientation;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Class for reading and displaying Spice decks (.spi files).
 */
public class Spice extends Input
{
	private Map<String,SubcktDef> allCells = new HashMap<String,SubcktDef>();

	private SpicePreferences localPrefs;

	public static class SpicePreferences extends InputPreferences
    {
        public Placement.PlacementPreferences placementOptions;
        public boolean doPlacement;

        public SpicePreferences(boolean factory)
        {
            super(factory);
            placementOptions = new Placement.PlacementPreferences(factory);
            if (factory)
            	doPlacement = SimulationTool.isFactorySpiceInputPlaces(); else
            		doPlacement = SimulationTool.isSpiceInputPlaces();
        }

        @Override
        public Library doInput(URL fileURL, Library lib, Technology tech, EditingPreferences ep, Map<Library,Cell> currentCells, Map<CellId,BitSet> nodesToExpand, Job job)
        {
        	Spice in = new Spice(ep, this);
			in.readSpice(fileURL, lib, job);
			return lib;
        }
    }

	/**
	 * Creates a new instance of Spice.
	 */
	Spice(EditingPreferences ep, SpicePreferences ap) {
        super(ep);
        localPrefs = ap;
    }

	/**
	 * Method to import a single file or an entire library from disk.
	 * @param dirURL the path to the file or directory.
	 * @param lib the library to fill
	 */
	private void readSpice(URL theURL, Library lib, Job job)
	{
		try
		{
			String path = TextUtils.decodeString(theURL.getFile());
			File f = new File(path);
			if (f == null)
			{
				System.out.println("Unable to find '" + path + "'");
				return;
			}
			if (f.isDirectory())
			{
				// a directory: scan for all Spice files there
				String [] filesInDir = f.list();
//				File [] files = f.listFiles();
			
				if (filesInDir == null)
				{
					System.out.println("Unable to read files from directory '" + path + "'");
					return;
				}
			
				for(int i=0; i<filesInDir.length; i++)
				{
					String fileName = filesInDir[i];
					int lastDotPos = fileName.lastIndexOf('.');
					if (lastDotPos < 0) continue;
					String ext = fileName.substring(lastDotPos+1).toLowerCase();
					if (ext.equals("spi") || ext.equals("sp"))
					{
						URL url = TextUtils.makeURLToFile(path + fileName);
						if (url == null) continue;
						if (openTextInput(url)) continue;
						System.out.println("Reading " + fileName);
						readSimFile();
						closeInput();
					}
				}
			} else
			{
				// a single file: read it
				if (!openTextInput(theURL))
				{
					System.out.println("Reading " + f.getName());
					readSimFile();
					closeInput();
				}
			}
		} catch (IOException e) {}

		// file read, now create all circuitry
		placeCells(lib, job);
	}

	private void readSimFile()
		throws IOException
	{
		SubcktDef current = null;
		for(;;)
		{
			String line = getNextLine();
			if (line == null) break;
			line = line.trim();
			if (line.length() == 0) continue;
			if (line.charAt(0) == '*') continue;

			// handle ".subckt" header
			String lineLC = line.toLowerCase();
			if (lineLC.startsWith(".subckt"))
			{
				String[] expPieces = line.split(" ");
				current = new SubcktDef();
				allCells.put(expPieces[1], current);
				for(int i=2; i<expPieces.length; i++) current.exports.add(expPieces[i]);
				continue;
			}
			if (lineLC.startsWith(".ends")) current = null;

			// handle transistors
			if (lineLC.charAt(0) == 'm' && current != null)
			{
				TransistorDef td = new TransistorDef();
				current.transistors.add(td);
				String[] traPieces = line.split(" ");
				if (traPieces.length < 6)
				{
					System.out.println("Error on Transistor line: " + line);
					continue;
				}
				td.name = traPieces[0].substring(1);
				td.drain = traPieces[1];
				td.gate = traPieces[2];
				td.source = traPieces[3];
				td.bias = traPieces[4];
				td.type = traPieces[5].toLowerCase();
				for(int i=6; i<traPieces.length; i++)
				{
					if (traPieces[i].toLowerCase().startsWith("w="))
						td.width = traPieces[i].substring(2);
					if (traPieces[i].toLowerCase().startsWith("l="))
						td.length = traPieces[i].substring(2);
				}
				continue;
			}

			// handle resistors
			if (lineLC.charAt(0) == 'r' && current != null)
			{
				ResistorDef rd = new ResistorDef();
				current.resistors.add(rd);
				String[] resPieces = line.split(" ");
				if (resPieces.length < 3)
				{
					System.out.println("Error on Resistor line: " + line);
					continue;
				}
				rd.name = resPieces[0].substring(1);
				rd.left = resPieces[1];
				rd.right = resPieces[2];
				if (resPieces.length >= 4)
					rd.resistance = resPieces[3];
				continue;
			}

			// handle capacitors
			if (lineLC.charAt(0) == 'c' && current != null)
			{
				CapacitorDef cd = new CapacitorDef();
				current.capacitors.add(cd);
				String[] capPieces = line.split(" ");
				if (capPieces.length < 3)
				{
					System.out.println("Error on Capacitor line: " + line);
					continue;
				}
				cd.name = capPieces[0].substring(1);
				cd.top = capPieces[1];
				cd.bottom = capPieces[2];
				if (capPieces.length >= 4)
					cd.capacitance = capPieces[3];
				continue;
			}

			// handle instances
			if (lineLC.charAt(0) == 'x' && current != null)
			{
				InstanceDef id = new InstanceDef();
				current.instances.add(id);
				String[] iPieces = line.split(" ");
				if (iPieces.length < 2)
				{
					System.out.println("Error on Instance line: " + line);
					continue;
				}
				id.name = iPieces[0].substring(1);
				id.instName = iPieces[iPieces.length-1];
				for(int i=1; i<iPieces.length-1; i++) id.signals.add(iPieces[i]);
				continue;
			}
		}
	}

	private void placeCells(Library lib, Job job)
	{
		String algName = localPrefs.placementOptions.placementAlgorithm;
		if (!localPrefs.doPlacement) algName = "Simple";
		PlacementFrame pla = null;
		for(PlacementFrame pfObj : PlacementAdapter.getPlacementAlgorithms())
		{
			if (pfObj.getAlgorithmName().equals(algName)) { pla = pfObj;  break; }
		}
		if (pla == null) pla = PlacementAdapter.getPlacementAlgorithms()[0];
		algName = pla.getAlgorithmName();
		if (!algName.equals("Simple"))
			System.out.println("Placing cells using the " + algName + " algorithm");

		// make icons
		for(String name : allCells.keySet())
		{
			SubcktDef sd = allCells.get(name);
			Cell cell = Cell.makeInstance(ep, lib, name + "{ic}");
			sd.iconCell = cell;
			double yPos = 1;
			double lowY = yPos;
			for(String export : sd.exports)
			{
				PrimitiveNode np = Schematics.tech().wirePinNode;
				EPoint center1 = EPoint.fromLambda(0, yPos);
				EPoint center2 = EPoint.fromLambda(2, yPos);
				NodeInst ni1 = NodeInst.makeInstance(np, ep, center1, np.getDefWidth(ep), np.getDefHeight(ep), cell);
				PortInst pi1 = ni1.getOnlyPortInst();
				NodeInst ni2 = NodeInst.makeInstance(np, ep, center2, np.getDefWidth(ep), np.getDefHeight(ep), cell);
				PortInst pi2 = ni2.getOnlyPortInst();
				ArcProto ap = Schematics.tech().wire_arc;
				ArcInst.makeInstance(ap, ep, pi1, pi2);
				Export e = Export.newInstance(cell, pi1, export, ep, PortCharacteristic.UNKNOWN);
				TextDescriptor newTD = e.getTextDescriptor(Export.EXPORT_NAME).withPos(TextDescriptor.Position.LEFT);
				e.setTextDescriptor(Export.EXPORT_NAME, newTD);
				yPos += 2;
			}
			if (yPos == lowY) yPos += 2;
			double width = 10;
			double height = yPos-lowY;
			EPoint center = EPoint.fromLambda(7, (yPos-2+lowY)/2);
			PrimitiveNode np = Artwork.tech().boxNode;
			NodeInst ni = NodeInst.makeInstance(np, ep, center, width, height, cell);
			ni.setName(name.replaceAll("@", "_"));
			TextDescriptor newTD = ni.getTextDescriptor(NodeInst.NODE_NAME).withRelSize(2);
			ni.setTextDescriptor(NodeInst.NODE_NAME, newTD);
		}

		// now make the schematic cells
		for(String name : allCells.keySet())
		{
			SubcktDef sd = allCells.get(name);

			// use the placement tool to situate the objects
			List<PlacementNode> nodesToPlace = new ArrayList<PlacementNode>();
			Map<String,PlacementNetwork> allNetworks = new HashMap<String,PlacementNetwork>();
			List<PlacementExport> exportsToPlace = new ArrayList<PlacementExport>();

			// make PlacementNodes for the transistors
			for(TransistorDef td : sd.transistors)
			{
				int bits = 0;
				PrimitiveNode np = Schematics.tech().transistorNode;
				if (td.type.equals("p")) bits = np.getPrimitiveFunctionBits(PrimitiveNode.Function.TRAPMOS); else
					bits = np.getPrimitiveFunctionBits(PrimitiveNode.Function.TRANMOS);
				NodeInst niDummy = NodeInst.makeDummyInstance(np, ep);

				List<PlacementPort> ports = new ArrayList<PlacementPort>();
				PortInst piSource = niDummy.getTransistorSourcePort();
				ports.add(addPlacementPort(niDummy, piSource, td.source, allNetworks, sd.exports, sd.usedExports, exportsToPlace));
				PortInst piGate = niDummy.getTransistorGatePort();
				ports.add(addPlacementPort(niDummy, piGate, td.gate, allNetworks, sd.exports, sd.usedExports, exportsToPlace));
				PortInst piDrain = niDummy.getTransistorDrainPort();
				ports.add(addPlacementPort(niDummy, piDrain, td.drain, allNetworks, sd.exports, sd.usedExports, exportsToPlace));

				PlacementNode plNode = new PlacementNode(null, np, "m" + td.name, bits, np.getDefWidth(ep), np.getDefHeight(ep), ports, false);
				nodesToPlace.add(plNode);
				for(PlacementPort plPort : ports)
					plPort.setPlacementNode(plNode);
				plNode.setOrientation(Orientation.IDENT);
				if (td.width != null) plNode.addVariable(Schematics.ATTR_WIDTH.getName(), td.width);
				if (td.length != null) plNode.addVariable(Schematics.ATTR_LENGTH.getName(), td.length);
			}

			// place resistors and capacitors
			for(ResistorDef rd : sd.resistors)
			{
				PrimitiveNode np = Schematics.tech().resistorNode;
				NodeInst niDummy = NodeInst.makeDummyInstance(np, ep);

				List<PlacementPort> ports = new ArrayList<PlacementPort>();
				PortInst piSource = niDummy.getPortInst(0);
				ports.add(addPlacementPort(niDummy, piSource, rd.left, allNetworks, sd.exports, sd.usedExports, exportsToPlace));
				PortInst piGate = niDummy.getPortInst(1);
				ports.add(addPlacementPort(niDummy, piGate, rd.right, allNetworks, sd.exports, sd.usedExports, exportsToPlace));

				PlacementNode plNode = new PlacementNode(null, np, "r" + rd.name, 0, np.getDefWidth(ep), np.getDefHeight(ep), ports, false);
				nodesToPlace.add(plNode);
				for(PlacementPort plPort : ports)
					plPort.setPlacementNode(plNode);
				plNode.setOrientation(Orientation.IDENT);
				if (rd.resistance != null) plNode.addVariable(Schematics.SCHEM_RESISTANCE.getName(), rd.resistance);
			}
			for(CapacitorDef cd : sd.capacitors)
			{
				PrimitiveNode np = Schematics.tech().capacitorNode;
				NodeInst niDummy = NodeInst.makeDummyInstance(np, ep);

				List<PlacementPort> ports = new ArrayList<PlacementPort>();
				PortInst piSource = niDummy.getPortInst(0);
				ports.add(addPlacementPort(niDummy, piSource, cd.top, allNetworks, sd.exports, sd.usedExports, exportsToPlace));
				PortInst piGate = niDummy.getPortInst(1);
				ports.add(addPlacementPort(niDummy, piGate, cd.bottom, allNetworks, sd.exports, sd.usedExports, exportsToPlace));

				PlacementNode plNode = new PlacementNode(null, np, "c" + cd.name, 0, np.getDefWidth(ep), np.getDefHeight(ep), ports, false);
				nodesToPlace.add(plNode);
				for(PlacementPort plPort : ports)
					plPort.setPlacementNode(plNode);
				plNode.setOrientation(Orientation.IDENT);
				if (cd.capacitance != null) plNode.addVariable(Schematics.SCHEM_CAPACITANCE.getName(), cd.capacitance);
			}

			// place instances
			for(InstanceDef id : sd.instances)
			{
				SubcktDef subSD = allCells.get(id.instName);
				if (subSD == null)
				{
					System.out.println("Cannot find subcircuit "+id.instName);
					continue;
				}
				Cell np = subSD.iconCell;
				NodeInst niDummy = NodeInst.makeDummyInstance(np, ep);
				if (subSD.exports.size() != id.signals.size())
				{
					System.out.println("Error: Subcircuit " + id.instName + " has " + subSD.exports.size() +
						" exports but instance " + id.name + " of it in cell " + name + "{sch}" +
						" has " + id.signals.size() + " signals on it");
					continue;
				}
				List<PlacementPort> ports = new ArrayList<PlacementPort>();
				for(int i=0; i<subSD.exports.size(); i++)
				{
					PortProto pp = subSD.iconCell.findPortProto(subSD.exports.get(i));
					if (pp == null) continue;
					PortInst pi = niDummy.findPortInstFromProto(pp);
					String sigName = id.signals.get(i);
					ports.add(addPlacementPort(niDummy, pi, sigName, allNetworks, sd.exports, sd.usedExports, exportsToPlace));
				}

				PlacementNode plNode = new PlacementNode(null, np, id.name.replace('@', '_'), 0, np.getDefWidth(), np.getDefHeight(), ports, false);
				nodesToPlace.add(plNode);
				for(PlacementPort plPort : ports)
					plPort.setPlacementNode(plNode);
				plNode.setOrientation(Orientation.IDENT);
			}

			// add dummy pins for unused exports
			for(String export : sd.exports)
			{
				if (sd.usedExports.contains(export)) continue;
				PrimitiveNode np = Schematics.tech().wirePinNode;
				NodeInst niDummy = NodeInst.makeDummyInstance(np, ep);
				List<PlacementPort> ports = new ArrayList<PlacementPort>();
				PortInst pi = niDummy.getPortInst(0);
				ports.add(addPlacementPort(niDummy, pi, export, allNetworks, sd.exports, sd.usedExports, exportsToPlace));

				PlacementNode plNode = new PlacementNode(null, np, null, 0, np.getDefWidth(ep), np.getDefHeight(ep), ports, false);
				nodesToPlace.add(plNode);
				for(PlacementPort plPort : ports)
					plPort.setPlacementNode(plNode);
				plNode.setOrientation(Orientation.IDENT);
			}

			// make a list of PlacementNetworks
			List<PlacementNetwork> nets = new ArrayList<PlacementNetwork>();
			for(String netName : allNetworks.keySet())
			{
				PlacementNetwork net = allNetworks.get(netName);
				for(PlacementFrame.PlacementPort port : net.getPortsOnNet())
					port.setPlacementNetwork(net);
				if (net.getPortsOnNet() == null || net.getPortsOnNet().size() <= 1) continue;
				nets.add(net);
			}

			// run placement
			int quiet = algName.equals("Simple") ? -1 : 0;
			PlacementAdapter.doPlacement(pla, lib, name + "{sch}", nodesToPlace, nets, exportsToPlace,
				sd.iconCell, ep, localPrefs.placementOptions, quiet, job);

			// the old way...
//			Cell cell = Cell.makeInstance(lib, name + "{sch}");
//			if (sd.iconCell != null)
//			{
//				EPoint center = new EPoint(0, 15);
//				NodeInst.makeInstance(sd.iconCell, center, sd.iconCell.getDefWidth(), sd.iconCell.getDefHeight(), cell);
//			}
//
//			// place transistors
//			double pPos = 0, nPos = 0;
//			for(TransistorDef td : sd.transistors)
//			{
//				EPoint center = null;
//				PrimitiveNode.Function func = null;
//				if (td.type.equals("p"))
//				{
//					center = new EPoint(pPos, 5);
//					pPos += 10;
//					func = PrimitiveNode.Function.TRAPMOS;
//				} else
//				{
//					center = new EPoint(nPos, -5);
//					nPos += 10;
//					func = PrimitiveNode.Function.TRANMOS;
//				}
//				PrimitiveNode np = Schematics.tech().transistorNode;
//				Orientation o = Orientation.R;
//				NodeInst ni = NodeInst.makeInstance(np, center, np.getDefWidth(), np.getDefHeight(),
//					cell, o, "m" + td.name, func);
//				TextDescriptor tdesc = TextDescriptor.newTextDescriptor(MutableTextDescriptor.getNodeTextDescriptor());
//		        TextDescriptor.Rotation r = TextDescriptor.Rotation.getRotation(270);
//				ni.setTextDescriptor(NodeInst.NODE_NAME, tdesc.withRotation(r).withPos(TextDescriptor.Position.DOWNLEFT));
//				if (td.width != null)
//				{
//					Variable var = ni.newDisplayVar(Schematics.ATTR_WIDTH, td.width);
//					TextDescriptor newTD = var.getTextDescriptor().
//						withRotation(r).withOff(0.5, -0.5).withRelSize(1).withPos(TextDescriptor.Position.DOWN);
//					ni.setTextDescriptor(Schematics.ATTR_WIDTH, newTD);
//				}
//				if (td.length != null)
//				{
//					Variable var = ni.newDisplayVar(Schematics.ATTR_LENGTH, td.length);
//					TextDescriptor newTD = var.getTextDescriptor().
//						withRotation(r).withOff(-0.5, -0.75).withRelSize(0.5).withPos(TextDescriptor.Position.DOWN);
//					ni.setTextDescriptor(Schematics.ATTR_LENGTH, newTD);
//				}
//				PortInst piSource = ni.getTransistorSourcePort();
//				addLead(piSource, 0, -2, cell, td.source, sd.exports, sd.usedExports);
//				PortInst piGate = ni.getTransistorGatePort();
//				addLead(piGate, -2, 0, cell, td.gate, sd.exports, sd.usedExports);
//				PortInst piDrain = ni.getTransistorDrainPort();
//				addLead(piDrain, 0, 2, cell, td.drain, sd.exports, sd.usedExports);
//			}
//
//			// place resistors and capacitors
//			for(ResistorDef rd : sd.resistors)
//			{
//				EPoint center = new EPoint(pPos, 5);
//				pPos += 10;
//				PrimitiveNode np = Schematics.tech().resistorNode;
//				NodeInst ni = NodeInst.makeInstance(np, center, np.getDefWidth(), np.getDefHeight(),
//					cell, Orientation.IDENT, "r" + rd.name);
//				if (rd.resistance != null)
//				{
//					Variable var = ni.newDisplayVar(Schematics.SCHEM_RESISTANCE, rd.resistance);
//					TextDescriptor newTD = var.getTextDescriptor().
//						withOff(0, 0.5).withPos(TextDescriptor.Position.UP);
//					ni.setTextDescriptor(Schematics.SCHEM_RESISTANCE, newTD);
//				}
//				PortInst piSource = ni.getPortInst(0);
//				addLead(piSource, -2, 0, cell, rd.left, sd.exports, sd.usedExports);
//				PortInst piGate = ni.getPortInst(1);
//				addLead(piGate, 2, 0, cell, rd.right, sd.exports, sd.usedExports);
//			}
//			for(CapacitorDef cd : sd.capacitors)
//			{
//				EPoint center = new EPoint(nPos, -5);
//				nPos += 10;
//				PrimitiveNode np = Schematics.tech().capacitorNode;
//				NodeInst ni = NodeInst.makeInstance(np, center, np.getDefWidth(), np.getDefHeight(),
//					cell, Orientation.IDENT, "c" + cd.name);
//				if (cd.capacitance != null)
//					ni.newDisplayVar(Schematics.SCHEM_CAPACITANCE, cd.capacitance);
//				PortInst piSource = ni.getPortInst(0);
//				addLead(piSource, 0, 2, cell, cd.top, sd.exports, sd.usedExports);
//				PortInst piGate = ni.getPortInst(1);
//				addLead(piGate, 0, -2, cell, cd.bottom, sd.exports, sd.usedExports);
//			}
//
//			// place instances
//			double iPos = 0;
//			for(InstanceDef id : sd.instances)
//			{
//				SubcktDef subSD = allCells.get(id.instName);
//				if (subSD == null)
//				{
//					System.out.println("Cannot find subcircuit "+id.instName);
//					continue;
//				}
//				Cell np = subSD.iconCell;
//				EPoint center = new EPoint(iPos, -10-np.getDefHeight());
//				iPos += 30;
//				NodeInst ni = NodeInst.makeInstance(np, center, np.getDefWidth(), np.getDefHeight(), cell);
//				ni.setName(id.name.replace('@', '_'));
//				TextDescriptor newTD = ni.getTextDescriptor(NodeInst.NODE_NAME).
//					withOff(1, -1).withPos(TextDescriptor.Position.DOWN);
//				ni.setTextDescriptor(NodeInst.NODE_NAME, newTD);
//				if (subSD.exports.size() != id.signals.size())
//				{
//					System.out.println("Error: Subcircuit " + id.instName + " has " + subSD.exports.size() +
//						" exports but instance " + id.name + " of it in cell " + cell.describe(false) +
//						" has " + id.signals.size() + " signals on it");
//					continue;
//				}
//				for(int i=0; i<subSD.exports.size(); i++)
//				{
//					PortProto pp = subSD.iconCell.findPortProto(subSD.exports.get(i));
//					if (pp == null) continue;
//					PortInst pi = ni.findPortInstFromProto(pp);
//					String sigName = id.signals.get(i);
//					addLead(pi, -2, 0, cell, sigName, sd.exports, sd.usedExports);
//				}
//			}
//
//			// add dummy pins for unused exports
//			double dummyPos = 30;
//			for(String export : sd.exports)
//			{
//				if (sd.usedExports.contains(export)) continue;
//				EPoint ctr = new EPoint(dummyPos, 15);
//				dummyPos += 10;
//				PrimitiveNode np = Schematics.tech().wirePinNode;
//				NodeInst ni = NodeInst.makeInstance(np, ctr, np.getDefWidth(), np.getDefHeight(), cell);
//				Export.newInstance(cell, ni.getOnlyPortInst(), export, PortCharacteristic.UNKNOWN);
//			}
		}
	}

	private PlacementPort addPlacementPort(NodeInst ni, PortInst pi, String name, Map<String,PlacementNetwork> allNetworks,
		List<String> exports, Set<String> usedExports, List<PlacementExport> exportsToPlace)
	{
		PlacementNetwork net = allNetworks.get(name);
		if (net == null) allNetworks.put(name, net = new PlacementNetwork(new ArrayList<PlacementFrame.PlacementPort>(), false));
		List<PlacementFrame.PlacementPort> portsOnNet = net.getPortsOnNet();

		Poly poly = pi.getPoly();
		double offX = poly.getCenterX() - ni.getTrueCenterX();
		double offY = poly.getCenterY() - ni.getTrueCenterY();
		PlacementPort plPort = new PlacementPort(offX, offY, pi.getPortProto());
		portsOnNet.add(plPort);

		// see if this port is exported
		if (exports.contains(name) && !usedExports.contains(name))
		{
			usedExports.add(name);
			PlacementExport plExport = new PlacementExport(plPort, name, PortCharacteristic.UNKNOWN);
			exportsToPlace.add(plExport);
		}
		return plPort;
	}

//	private void addLead(PortInst pi, double dX, double dY, Cell cell, String name, List<String> exports, Set<String> usedExports)
//	{
//		EPoint ctr1 = pi.getCenter();
//		EPoint ctr2 = new EPoint(ctr1.getX()+dX, ctr1.getY()+dY);
//
//		PrimitiveNode np = Schematics.tech().wirePinNode;
//		NodeInst ni = NodeInst.makeInstance(np, ctr2, np.getDefWidth(), np.getDefHeight(), cell);
//		ArcProto ap = Schematics.tech().wire_arc;
//		PortInst pi2 = ni.getOnlyPortInst();
//		ArcInst ai = ArcInst.makeInstance(ap, pi, pi2);
//
//		name = name.replace('@', '_');
//		if (exports.contains(name) && !usedExports.contains(name))
//		{
//			usedExports.add(name);
//			Export.newInstance(cell, pi2, name, PortCharacteristic.UNKNOWN);
//		} else
//		{
//			ai.setName(name);
//			TextDescriptor.Position p = (dX == 0) ? TextDescriptor.Position.LEFT : TextDescriptor.Position.DOWN;
//			TextDescriptor newTD = ai.getTextDescriptor(ArcInst.ARC_NAME).withPos(p);
//			ai.setTextDescriptor(ArcInst.ARC_NAME, newTD);
//		}
//	}

	private String lastLine = null;

	private String getNextLine()
		throws IOException
	{
		String line = lastLine;
		if (line == null) line = getLine();
		if (line != null)
		{
			lastLine = getLine();
			while (lastLine != null && lastLine.startsWith("+"))
			{
				line += lastLine.substring(1);
				lastLine = getLine();
			}
		}
		return line;
	}

	private static class SubcktDef
	{
		List<String> exports;
		List<TransistorDef> transistors;
		List<ResistorDef> resistors;
		List<CapacitorDef> capacitors;
		List<InstanceDef> instances;
		Set<String> usedExports;
		Cell iconCell;

		public SubcktDef()
		{
			exports = new ArrayList<String>();
			transistors = new ArrayList<TransistorDef>();
			resistors = new ArrayList<ResistorDef>();
			capacitors = new ArrayList<CapacitorDef>();
			instances = new ArrayList<InstanceDef>();
			usedExports = new HashSet<String>();
		}
	}

	private static class InstanceDef
	{
		String name;
		String instName;
		List<String> signals;

		public InstanceDef()
		{
			signals = new ArrayList<String>();
		}
	}

	private static class TransistorDef
	{
		String name;
		String source, gate, drain;
		String bias;
		String type;
		String width, length;
	}

	private static class ResistorDef
	{
		String name;
		String left, right;
		String resistance;
	}

	private static class CapacitorDef
	{
		String name;
		String top, bottom;
		String capacitance;
	}
}
