/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: NodeInfo.java
 * Technology Editor, node information
 * Written 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.user.tecEdit;

import com.sun.electric.database.EditingPreferences;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Library;
import com.sun.electric.database.prototype.PortCharacteristic;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.PrimitivePort;
import com.sun.electric.technology.SizeOffset;
import com.sun.electric.technology.Technology;
import com.sun.electric.util.math.EDimension;

import java.util.BitSet;
import java.util.Iterator;
import java.util.List;

/**
 * This class defines information about nodes in the Technology Editor.
 */
public class NodeInfo extends Info
{

	static class PortDetails
	{
		String                 name;
		ArcInfo []             connections;
		int                    angle;
		int                    range;
		int                    netIndex;
		Technology.TechPoint[] values;
        PortCharacteristic     characterisitic;
        boolean                isolated;
        boolean                negatable;
	}

	static class LayerDetails
	{
		LayerInfo              layer;
		Poly.Type              style;
		int                    representation;
		Technology.TechPoint[] values;
		Sample                 ns;
		int                    portIndex;
        boolean                inLayers;
        boolean                inElectricalLayers;
		boolean                multiCut;				/* true if a multi-cut layer */
		double                 multiXS, multiYS;			/* size of multicut */
		double                 multiIndent, multiSep, multiSep2D;	/* indent and separation of multicuts */
		double                 lWidth, rWidth, extendT, extendB;		/* serpentine transistor information */
        String                 message;
        TextDescriptor         descriptor;
        BitSet                 inNodes;

        public LayerDetails()
        {
        	inLayers = true;
        	inElectricalLayers = true;
        }

        public LayerDetails duplicate()
        {
        	LayerDetails dup = new LayerDetails();
        	dup.layer = layer;
        	dup.style = style;
        	dup.representation = representation;
        	dup.values = new Technology.TechPoint[values.length];
        	for(int i=0; i<values.length; i++) dup.values[i] = values[i];
        	dup.ns = ns;
        	dup.portIndex = portIndex;
        	dup.inLayers = inLayers;
        	dup.inElectricalLayers = inElectricalLayers;
        	dup.multiCut = multiCut;
        	dup.multiXS = multiXS;
        	dup.multiYS = multiYS;
        	dup.multiIndent = multiIndent;
        	dup.multiSep = multiSep;
        	dup.multiSep2D = multiSep2D;
        	dup.lWidth = lWidth;
        	dup.rWidth = rWidth;
        	dup.extendT = extendT;
        	dup.extendB = extendB;
        	dup.message = message;
        	dup.descriptor = descriptor;
        	return dup;
        }
	}

	String                 name;
	String                 abbrev;
	PrimitiveNode          generated;
	PrimitiveNode.Function func;
	boolean                serp;
    boolean                arcsShrink;
    boolean                partialCircle;
	boolean                square;
    boolean                canBeZeroSize;
	boolean                wipes;
	boolean                lockable;
    boolean                edgeSelect;
    boolean                skipSizeInPalette;
    boolean                notUsed;
    boolean                lowVt;
    boolean                highVt;
    boolean                nativeBit;
    boolean                od18;
    boolean                od25;
    boolean                od33;
    boolean                curvePin;
	LayerDetails []        nodeLayers;
	PortDetails []         nodePortDetails;
	PrimitivePort[]        primPorts;
	SizeOffset             so;
    PrimitiveNode.NodeSizeRule nodeSizeRule;
    EDimension            autoGrowth;
	int                    specialType;
	double []              specialValues;
	double                 xSize, ySize;
	String                 spiceTemplate;
    List<String>           primitiveNodeGroupNames;

	static SpecialTextDescr [] nodeTextTable =
	{
		new SpecialTextDescr(0, 18, NODEFUNCTION),
		new SpecialTextDescr(0, 15, NODESERPENTINE),
		new SpecialTextDescr(0, 12, NODESQUARE),
		new SpecialTextDescr(0,  9, NODEWIPES),
		new SpecialTextDescr(0,  6, NODELOCKABLE),
		new SpecialTextDescr(0,  3, NODESPICETEMPLATE)
	};

	NodeInfo()
	{
		func = PrimitiveNode.Function.UNKNOWN;
	}

	/**
	 * Method to return an array of cells that comprise the nodes in a technology library.
	 * @param lib the technology library.
	 * @return an array of cells for each node (in the proper order).
	 */
	public static Cell [] getNodeCells(Library lib)
	{
		Library [] oneLib = new Library[1];
		oneLib[0] = lib;
		return findCellSequence(oneLib, "node-", NODESEQUENCE_KEY);
	}

	/**
	 * Method to build the appropriate descriptive information for a node into
	 * cell "np".  The function is in "func", the serpentine transistor factor
	 * is in "serp", the node is square if "square" is true, the node
	 * is invisible on 1 or 2 arcs if "wipes" is true, and the node is lockable
	 * if "lockable" is true.
	 */
	void generate(Cell np, EditingPreferences ep)
	{
		// load up the structure with the current values
		loadTableEntry(nodeTextTable, NODEFUNCTION, func);
		loadTableEntry(nodeTextTable, NODESERPENTINE, Boolean.valueOf(serp));
		loadTableEntry(nodeTextTable, NODESQUARE, Boolean.valueOf(square));
		loadTableEntry(nodeTextTable, NODEWIPES, Boolean.valueOf(wipes));
		loadTableEntry(nodeTextTable, NODELOCKABLE, Boolean.valueOf(lockable));
		loadTableEntry(nodeTextTable, NODESPICETEMPLATE, spiceTemplate);

		// now create those text objects
		createSpecialText(np, nodeTextTable, ep);
	}

	/**
	 * Method to parse the node cell in "np" and return an NodeInfo object that describes it.
	 */
	static NodeInfo parseCell(Cell np)
	{
		NodeInfo nIn = new NodeInfo();

		// look at all nodes in the arc description cell
		for(Iterator<NodeInst> it = np.getNodes(); it.hasNext(); )
		{
			NodeInst ni = it.next();
			Variable var = ni.getVar(OPTION_KEY);
			if (var == null) continue;

			switch (((Integer)var.getObject()).intValue())
			{
				case NODEFUNCTION:
					nIn.func = PrimitiveNode.Function.findName(getValueOnNode(ni));
					if (nIn.func == null) nIn.func = PrimitiveNode.Function.UNKNOWN;
					break;
				case NODESQUARE:
					nIn.square = getValueOnNode(ni).equalsIgnoreCase("yes");
					break;
				case NODEWIPES:
					nIn.wipes = getValueOnNode(ni).equalsIgnoreCase("yes");
					break;
				case NODELOCKABLE:
					nIn.lockable = getValueOnNode(ni).equalsIgnoreCase("yes");
					break;
				case NODESERPENTINE:
					nIn.serp = getValueOnNode(ni).equalsIgnoreCase("yes");
					break;
				case NODESPICETEMPLATE:
					nIn.spiceTemplate = getValueOnNode(ni);
					break;
			}
		}
		return nIn;
	}

	/**
	 * Method to compact a Node technology-edit cell
	 */
	static void compactCell(Cell cell)
	{
		// move the examples
		TechConversionResult tcr = new TechConversionResult();
		List<Example> neList = Example.getExamples(cell, true, tcr, null);
   		if (tcr.failed()) tcr.showError();
		if (neList == null || neList.size() == 0) return;
		Example firstEx = neList.get(0);
		int numExamples = neList.size();
		Example smallest = firstEx;
		Example biggest = firstEx;
		for(Example ne : neList)
		{
			if (ne.hx-ne.lx > biggest.hx-biggest.lx || ne.hy-ne.ly > biggest.hx-biggest.ly)
                biggest = ne;
		}
		if (numExamples == 1)
		{
			moveExample(firstEx, -(firstEx.lx + firstEx.hx) / 2, -firstEx.hy);
			return;
		}
		if (numExamples != 4) return;

		Example stretchX = null;
		Example stretchY = null;
		for(Example ne : neList)
		{
			if (ne == biggest || ne == smallest) continue;
			if (stretchX == null) stretchX = ne; else
				if (stretchY == null) stretchY = ne;
		}
		if (stretchX.hx-stretchX.lx < stretchY.hx-stretchY.lx)
		{
			Example swap = stretchX;
			stretchX = stretchY;
			stretchY = swap;
		}

		double separation = Math.min(smallest.hx - smallest.lx, smallest.hy - smallest.ly);
		double totalWid = (stretchX.hx-stretchX.lx) + (smallest.hx-smallest.lx) + separation;
		double totalHei = (stretchY.hy-stretchY.ly) + (smallest.hy-smallest.ly) + separation;

		// center the smallest (main) example
		double cX = -totalWid / 2 - smallest.lx;
		double cY = -smallest.hy - 1;
		moveExample(smallest, cX, cY);

		// center the stretch-x (upper-right) example
		cX = totalWid/2 - stretchX.hx;
		cY = -stretchX.hy - 1;
		moveExample(stretchX, cX, cY);

		// center the stretch-y (lower-left) example
		cX = -totalWid/2 - stretchY.lx;
		cY = -totalHei - stretchY.ly - 1;
		moveExample(stretchY, cX, cY);

		// center the biggest (lower-right) example
		cX = totalWid/2 - biggest.hx;
		cY = -totalHei - biggest.ly - 1;
		moveExample(biggest, cX, cY);
	}

	private static void moveExample(Example ne, double dX, double dY)
	{
		for(Sample ns : ne.samples)
		{
			ns.node.move(dX, dY);
		}
	}
}
