/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: FlagDesign.java
 *
 * Copyright (c) 2003, 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.generator.flag;

import com.sun.electric.database.EditingPreferences;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.hierarchy.HierarchyEnumerator;
import com.sun.electric.database.hierarchy.Library;
import com.sun.electric.database.prototype.PortCharacteristic;
import com.sun.electric.database.topology.Geometric;
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.technology.ArcProto;
import com.sun.electric.tool.generator.flag.router.Router;
import com.sun.electric.tool.generator.flag.router.SogRouterAdapter;
import com.sun.electric.tool.generator.flag.router.ToConnect;
import com.sun.electric.tool.generator.flag.scan.Scan;
import com.sun.electric.tool.generator.layout.AbutRouter;
import com.sun.electric.tool.generator.layout.LayoutLib;
import com.sun.electric.tool.generator.layout.TechType;
import com.sun.electric.tool.generator.layout.LayoutLib.Corner;
import com.sun.electric.tool.ncc.basic.NccCellAnnotations;
import com.sun.electric.tool.routing.SeaOfGates;
import com.sun.electric.tool.user.ExportChanges;
import com.sun.electric.util.math.Orientation;


/** Super class for the physical design objects of all Cells */
public class FlagDesign {
    public static final double DEF_SIZE = LayoutLib.DEF_SIZE;
	private final FlagConfig config;
	private final Scan scan;
	private final Router router;
	private final SogRouterAdapter sogRouterAdapter;

	public TechType tech() {return config.tech();}
    public EditingPreferences getEditingPreferences() {return router.getEditingPreferences(); };
    
	
//	public static Rectangle2D findColBounds(Collection<NodeInst> stages) {
//		double minX, minY, maxX, maxY;
//		minX = minY = Double.MAX_VALUE;
//		maxX = maxY = Double.MIN_VALUE;
//		for (NodeInst ni : stages) {
//			Rectangle2D bounds = ni.findEssentialBounds();
//			error(bounds==null, 
//					        "Layout Cell is missing essential bounds: "+
//					        ni.getProto().describe(false));
//			minX = Math.min(minX, bounds.getMinX());
//			maxX = Math.max(maxX, bounds.getMaxX());
//			minY = Math.min(minY, bounds.getMinY());
//			maxY = Math.max(maxY, bounds.getMaxY());
//		}
//		return new Rectangle2D.Double(minX, minY, maxX-minX, maxY-minY);
//	}
//	private void sortPortInstsBotToTop(List<PortInst> pis) {
//		Collections.sort(pis, new Comparator<PortInst>() {
//			public int compare(PortInst p1, PortInst p2) {
//				double diff = p1.getCenter().getY() -
//				              p2.getCenter().getY();
//				return (int) Math.signum(diff);
//			}
//		});
//	}
//	private Rectangle2D getPortInstBounds(ToConnect toConn) {
//		double minX, minY, maxX, maxY;
//		minX = minY = Double.MAX_VALUE;
//		maxX = maxY = Double.MIN_VALUE;
//		for (PortInst pi : toConn.getPortInsts()) {
//			double x = pi.getCenter().getX();
//			double y = pi.getCenter().getY();
//			if (x<minX) minX=x;
//			if (x>maxX) maxX=x;
//			if (y<minY) minY=y;
//			if (y>maxY) maxY=y;
//		}
//		return new Rectangle2D.Double(minX, minY, maxX-minX, maxY-minY);
//	}
	
//	private void connect2PinM3(ToConnect toConn) {
//		prln("M3 "+toConn.toString());
//		PortInst pi1 = toConn.getPortInsts().get(0);
//		PortInst pi2 = toConn.getPortInsts().get(1);
//		error(!connectsToM3(pi1) || ! connectsToM3(pi2),
//				        "only 1 of 2 pins connects to m3");
//		double x1 = pi1.getCenter().getX();
//		double x2 = pi2.getCenter().getX();
//		error(x1!=x2, "m3 net not vertical");
//		
//		LayoutLib.newArcInst(tech().m3(), SIGNAL_WID, pi1, pi2);
//		
//	}
	
//	private void dumpChannels(LayerChannels m2chan, LayerChannels m3chan) {
//        prln("m2 channels");
//        prln(m2chan.toString());
//        
//        prln("m3 channels");
//        prln(m3chan.toString());
//	}
//	private List<ToConnect> reduceToTwoPinX(List<List<PortInst>> portLists) {
//		List<ToConnect> twoPins = new ArrayList<ToConnect>();
//		while (portLists.size()>1) {
//			ClosestClusters cc = findClosest(portLists);
//			ToConnect tc = new ToConnect(null);
//			tc.addPortInst(cc.pair.p1);
//			tc.addPortInst(cc.pair.p2);
//			twoPins.add(tc);
//			List<PortInst> pl1 = portLists.get(cc.ndx1);
//			List<PortInst> pl2 = portLists.get(cc.ndx2);
//			pl1.addAll(pl2);
//			portLists.remove(cc.ndx2);
//		}
//		return twoPins;
//	}
	

	/** Convert each ToConnect with more than two pins to multiple two 
	 * pin TwoConnects */ 
//	private List<ToConnect> reduceToTwoPin(List<ToConnect> toConns) {
//		List<ToConnect> twoPins = new ArrayList<ToConnect>();
//		for (ToConnect tc : toConns) {
//			// Skip Exported net that touches no stage PortInsts 
//			if (tc.size()==0) continue;
//			
//			// Some PortInsts on a ToConnect may already be connected in 
//			// schematic by abut router
//			List<List<PortInst>> connPorts = groupConnectedPorts(tc);
//
//			// Generate a list of two pin ToConnects that connects  
//			// disconnected pin lists.
//			twoPins.addAll(reduceToTwoPinX(connPorts));
//		}
//		return twoPins;
//	}
	
	
//	private void fattenVerticalM3(Map<Double, PortInst> bot,
//			                      Map<Double, PortInst> top) {
//		for (Double x : bot.keySet()) {
//			PortInst piBot = bot.get(x);
//			PortInst piTop = top.get(x);
//			if (piTop==null) continue;
//			LayoutLib.newArcInst(tech().m3(), getEditingPreferences(), config.m3PwrGndWid, 
//		                         piBot, piTop);
//		}
//	}
	
	// Hack until I get a chance to fatten based upon need.
//	private void fattenPwrGnd(List<NodeInst> stages) {
//		Rectangle2D colBounds = findBounds(stages.get(0).getParent());
//		NodeInst topInst = stages.get(stages.size()-1);
//		Map<Double, PortInst> topPwr = new TreeMap<Double, PortInst>();
//		Map<Double, PortInst> topGnd = new TreeMap<Double, PortInst>();
//		getM3PwrGndExports(topPwr, topGnd, topInst, colBounds.getMaxY());
//		
//		NodeInst botInst = stages.get(0);
//		Map<Double, PortInst> botPwr = new TreeMap<Double, PortInst>();
//		Map<Double, PortInst> botGnd = new TreeMap<Double, PortInst>();
//		getM3PwrGndExports(botPwr, botGnd, botInst, colBounds.getMinY());
//
//		fattenVerticalM3(botGnd, topGnd);
//		fattenVerticalM3(botPwr, topPwr);
//	}
	
	
	private static class CloseToBound implements Comparator<PortInst> {
		private Rectangle2D bound;
		private double distToBound(PortInst pi) {
			double x = pi.getCenter().getX();
			double l = Math.abs(x - bound.getMinX());
			double r = Math.abs(x - bound.getMaxX());
			double y = pi.getCenter().getY();
			double b = Math.abs(y - bound.getMinY());
			double t = Math.abs(y - bound.getMaxY());
			return Math.min(Math.min(l, r), 
					        Math.min(t, b)); 
		}
		public CloseToBound(Rectangle2D bound) {this.bound=bound;}
		public int compare(PortInst pi1, PortInst pi2) {
			double d = distToBound(pi1) - distToBound(pi2);
			return (int) Math.signum(d);
		}
	}
	
	
	
//	private void reExportIfPortNameMatches(List<String> expNms,
//			                               List<PortInst> ports) {
//		if (ports.isEmpty()) return;
//		Cell parent = ports.get(0).getNodeInst().getParent();
//		for (Iterator<String> sIt=expNms.iterator(); sIt.hasNext();) {
//			String nm = sIt.next();
//			if (parent.findExport(nm)!=null) {
//				sIt.remove();
//			} else {
//				
//			}
//		}
//	}
	/** If PortInst name matches Export name then export PortInst and
	 * remove PortInst and export name from respective lists. */
	private void exportPortInstsWithMatchingNames(List<String> expNames,
			                                      List<PortInst> ports) {
		for (Iterator<String> sIt=expNames.iterator(); sIt.hasNext();) {
			String expNm = sIt.next();
			for (Iterator<PortInst> pIt=ports.iterator(); pIt.hasNext();) {
				PortInst pi = pIt.next();
				String portNm = pi.getPortProto().getName();
				if (expNm.equals(portNm)) {
					Export.newInst(pi.getNodeInst().getParent(), 
					                   pi, expNm, getEditingPreferences());
					sIt.remove();
					pIt.remove();
					break;
				}
			}
		}
	}
	
	private void exportTheRest(List<String> expNames, List<PortInst> ports) {
		for (int i=0; i<expNames.size(); i++) {
			String expNm = expNames.get(i);
			if (i>=ports.size()) {
				prln("Error: Schematic export: "+expNm+
					  " couldn't be added to layout");
				continue;
			}
			PortInst pi = ports.get(i);
			Export.newInst(pi.getNodeInst().getParent(), 
	                           pi, expNm, getEditingPreferences());
		}
	}
	
	/** Re-export all ports that the schematic exports. Don't do power
	 * and ground because they are handled by another method. 
	 * There are three criteria for selecting which PortInst to export. First, 
	 * prefer a PortInst with the same as the export. Second, prefer a PortInst
	 * without connections. Third, prefer a PortInst that is close to the cell
	 * boundary. */
	private void reExport(Cell layCell, List<ToConnect> toConns) {
		Rectangle2D colBounds = Utils.findBounds(layCell);
		CloseToBound closeToBound = new CloseToBound(colBounds);
		for (ToConnect tc : toConns) {
			if (tc.numPortInsts()>0 && tc.isExported() && !tc.isPowerOrGround()) {
				// separate out connected and not connected PortInsts
				// Because PortInst.hasConnections() is so slow, make sure
				// we do this test once. Keeping two different lists accomplishes
				// this.
				List<PortInst> unconnPorts = new ArrayList<PortInst>();
				List<PortInst> connPorts = new ArrayList<PortInst>();
				for (PortInst pi : tc.getPortInsts()) {
					if (pi.hasConnections()) connPorts.add(pi);
					else  unconnPorts.add(pi);
				}
				Collections.sort(connPorts, closeToBound);
				Collections.sort(unconnPorts, closeToBound);

				// allPorts sorted by following criterea in order of importance:
				// 1) no arcs connected
				// 2) distance to boundary
				List<PortInst> allPorts = new ArrayList<PortInst>();
				allPorts.addAll(unconnPorts);
				allPorts.addAll(connPorts);
				
				List<String> expNames = new ArrayList<String>();
				expNames.addAll(tc.getExportName());
				exportPortInstsWithMatchingNames(expNames, allPorts);
				
				exportTheRest(expNames, allPorts);
			}
		}
	}
	
	
	private static class CompareLayInstSchPos implements Comparator<NodeInst> {
		Map<NodeInst, SchematicPosition> layInstToSchPos;
		public int compare(NodeInst ni1, NodeInst ni2) {
			SchematicPosition sp1, sp2;
			sp1 = layInstToSchPos.get(ni1);
			sp2 = layInstToSchPos.get(ni2);
			return sp1.compareTo(sp2);
		}
		public CompareLayInstSchPos(Map<NodeInst, SchematicPosition> layInstToSchPos) {
			this.layInstToSchPos = layInstToSchPos;
		}
	}
	// sort layout instances according to X-coordinate in schematic
	private List<NodeInst> getSortedLayInsts(SchematicVisitor visitor) {
        List<NodeInst> layInsts = 
        	new ArrayList<NodeInst>(visitor.getLayInsts());
        Map<NodeInst, SchematicPosition> layInstSchPos = 
        	visitor.getLayInstSchematicPositions();
        
        CompareLayInstSchPos compareLayInstSchPos = 
        	new CompareLayInstSchPos(layInstSchPos);
        
        Collections.sort(layInsts, compareLayInstSchPos);
        return layInsts;
	}

	private Map<Cell,Integer> getCellCounts(List<NodeInst> layInsts) {
		Map<Cell,Integer> cellToCount = new HashMap<Cell,Integer>();
		for (NodeInst ni : layInsts) {
			Cell c = (Cell) ni.getProto();
			Integer cnt = cellToCount.get(c);
			if (cnt==null) cnt = 0;
			cellToCount.put(c, cnt+1);
		}
		return cellToCount; 
	}
	
	// Print report in the same order as sortedLayInsts
	private void printInstanceReport(List<NodeInst> sortedLayInsts) {
		prln("Here are the Cell's I've instantiated: ");
		Map<Cell,Integer> cellToCount = getCellCounts(sortedLayInsts);
		for (NodeInst ni : sortedLayInsts) {
			Cell c = (Cell) ni.getProto();
			Integer cnt = cellToCount.get(c);
			if (cnt!=null) {
				prln("    "+cnt+"   "+c.describe(false));
				// Only print report the first time we encounter the Cell
				cellToCount.remove(c);
			}
		}
		
	}
	
	//	private boolean isPlain(NodeInst ni) {
//		return ni.getProto().getName().contains("aPlainStage");
//	}
//	
//	// infinityA and infinityB need to be stretched to the height of infinityC
//	private void stretchInfinityAB(List<NodeInst> layInsts) {
//		Cell parent = layInsts.get(0).getParent();
//		Cell dummyStage = STAGE_LIB.findNodeProto("aDummyStage{lay}");
//		if (!layInsts.get(0).getParent().getName().contains("infinityA") &&
//			!layInsts.get(0).getParent().getName().contains("infinityB")) 
//			return;
//		
//		NodeInst prev = null;
//		int stretchCnt = 0;
//		for (ListIterator<NodeInst> niIt=layInsts.listIterator(); niIt.hasNext();) {
//			NodeInst ni =  niIt.next();
//			if (stretchCnt>=NUM_PLACES_TO_STRETCH) return;
//			if (prev!=null && isPlain(prev) && isPlain(ni)) {
//				NodeInst dumInst = LayoutLib.newNodeInst(dummyStage, 0, 0, 
//						                                 DEF_SIZE, DEF_SIZE, 
//						                                 0, parent);
//				niIt.add(dumInst);
//				stretchCnt++;
//			}
//			prev = ni;
//		}
//	}
	// infinityC needs its 2nd and 3rd stages overlapped. Same with the 
	// two stages next to the last stage.
//	private void overlapInfinityC(List<NodeInst> layInsts) {
//		NodeInst dataFan = layInsts.get(1);
//		if (!dataFan.getParent().getName().contains("infinityC"))
//			return;
//
//		double h = dataFan.findEssentialBounds().getHeight();
//		for (int i=2; i<layInsts.size(); i++) {
//			NodeInst ni = layInsts.get(i);
//			ni.move(0, -h);
//		}
//		if (true) {
//			for (int i=layInsts.size()-2; i<layInsts.size(); i++) {
//				NodeInst ni = layInsts.get(i);
//				ni.move(0, -h);
//			}
//		}
//	}
	// infinityC needs stages stacked top to bottom. Furthermore,
	// all stages except for the first three and last three need to be
	// mirrored top <=> bottom 
//	private void flipInfinityC(List<NodeInst> layInsts) {
//		NodeInst dataFan = layInsts.get(1);
//		if (!dataFan.getParent().getName().contains("infinityC"))
//			return;
//		Collections.reverse(layInsts);
//		int end =  3;
//		for (int i=3; i<layInsts.size()-end; i++) {
//			NodeInst ni = layInsts.get(i);
//			ni.modifyInstance(0, 0, 0, 0, Orientation.Y);
//		}
//	}
	
	// infinityC needs scan stitched bottom to top
//	private void reverseScanListInfinityC(List<NodeInst> scanList) {
//		NodeInst first = scanList.get(1);
//		if (!first.getParent().getName().contains("infinityC"))
//			return;
//		Collections.reverse(scanList);
//	}
	
//	private NodeInst findInst(String type, List<NodeInst> insts) {
//		for (NodeInst ni : insts) {
//			String t = ni.getProto().getName();
//			if (t.equals(type)) return ni;
//		}
//		return null;
//	}
	
//	private void doInfinity(Cell autoLay, Cell schCell, EditingPreferences ep) {
//        Library autoLib = autoLay.getLibrary();
//        List<Library> primLibs = new ArrayList<Library>();
//        primLibs.add(autoLib);
//        SchematicVisitor visitor = new SchematicVisitor(autoLay, ep);
//        HierarchyEnumerator.enumerateCell(schCell, VarContext.globalContext, visitor);
//
//        List<NodeInst> layInsts = visitor.getLayInsts();
//        
//		NodeInst niA = findInst("infinityA", layInsts);
//		NodeInst niB = findInst("infinityB", layInsts);
//		NodeInst niC = findInst("infinityC", layInsts);
//		
//		double colWid = niA.findEssentialBounds().getWidth();
//		double numFillCellsAcross = Math.ceil(colWid/config.fillCellWidth);
//		double columnPitch = config.fillCellWidth * numFillCellsAcross;
//		double spaceBetweenCols = config.fillCellWidth + columnPitch - colWid;
//		
//		LayoutLib.alignCorners(niC, Corner.TL, niA, Corner.TR, -spaceBetweenCols, 0);
//		LayoutLib.alignCorners(niC, Corner.BR, niB, Corner.BL, spaceBetweenCols, 0);
//		List<ArcProto> horizLayers = new ArrayList<ArcProto>();
//		horizLayers.add(tech().m2());
//		horizLayers.add(tech().m4());
//		AbutRouter.abutRouteLeftRight(niA, niC, 0, horizLayers, ep);
//		AbutRouter.abutRouteLeftRight(niC, niB, 0, horizLayers, ep);
//
////        Rectangle2D bounds = findColBounds(layInsts);
////        addEssentialBounds(layInsts, bounds);
//		addEssentialBounds(autoLay);
//        ExportNamer vddNm = new ExportNamer("vdd");
//        ExportNamer gndNm = new ExportNamer("gnd");
//        //exportPwrGnd(autoLay, vddNm, gndNm);
//        exportPwrGnd(layInsts,  vddNm, gndNm);
//
//        List<ToConnect> toConns = visitor.getLayoutToConnects();
//        reExport(autoLay, toConns);
//	}
//	private void doGuts(Cell autoLay, Cell schCell, EditingPreferences ep) {
//        Library autoLib = autoLay.getLibrary();
//        List<Library> primLibs = new ArrayList<Library>();
//        primLibs.add(autoLib);
//        SchematicVisitor visitor = new SchematicVisitor(autoLay, ep);
//        HierarchyEnumerator.enumerateCell(schCell, VarContext.globalContext, visitor);
//
//        List<NodeInst> layInsts = visitor.getLayInsts();
//        
//		NodeInst niI = findInst("infinity", layInsts);
//		NodeInst niC = findInst("crosser", layInsts);
//		NodeInst niR = findInst("ring", layInsts);
//		
//		double colWid = niR.findEssentialBounds().getWidth();
//		double numFillCellsAcross = Math.ceil(colWid/config.fillCellWidth);
//		double columnPitch = config.fillCellWidth * numFillCellsAcross;
//		double spaceBetweenCols = columnPitch - colWid;
//
//		LayoutLib.alignCorners(niI, Corner.BR, niC, Corner.BL, spaceBetweenCols, (670-2));
//		LayoutLib.alignCorners(niC, Corner.TL, niR, Corner.BL, 0, 0);
//		
//		List<ArcProto> horizLayers = new ArrayList<ArcProto>();
//		horizLayers.add(tech().m2());
//		horizLayers.add(tech().m4());
//
//		AbutRouter.abutRouteLeftRight(niI, niC, 0, horizLayers, ep);
//		AbutRouter.abutRouteLeftRight(niI, niR, 0, horizLayers, ep);
//	}
	
//	private boolean portOnLayer(PortInst pi, List<ArcProto> layers) {
//		for (ArcProto ap : layers) {
//			if (pi.getPortProto().connectsTo(ap)) return true;
//		}
//		return false;
//	}
//	private void exportPwrGnd(List<NodeInst> stages, 
//			                  ExportNamer vdd, ExportNamer gnd) {
//		Rectangle2D colBounds = Utils.findBounds(stages.get(0).getParent());
//		List<ArcProto> vertLayers = new ArrayList<ArcProto>();
//		vertLayers.add(tech().m3());
//		List<ArcProto> horiLayers = new ArrayList<ArcProto>();
//		horiLayers.add(tech().m2());
//		for (NodeInst ni : stages) {
//			 for (Iterator piIt=ni.getPortInsts(); piIt.hasNext();) {
//				 PortInst pi = (PortInst) piIt.next();
//				 if (Utils.isPwrGnd(pi)) {
//					 if ((Utils.onTopOrBottom(pi, colBounds, 0) && 
//					      portOnLayer(pi, vertLayers)) ||
//						 (Utils.onLeftOrRight(pi, colBounds, 0) && 
//						  portOnLayer(pi, horiLayers))) {
//						 Cell parent = pi.getNodeInst().getParent();
//						 String exptNm;
//						 if (Utils.isPwr(pi)) {
//							 exptNm = vdd.nextName();
//						 } else {
//							 exptNm = gnd.nextName();
//						 }
//						 Export.newInst(parent, pi, exptNm, getEditingPreferences());
//					 }
//				 }
//			 }
//		}
//	}
	
	// Re-export all PortInsts of all NodeInsts that have the power or ground
	// characteristic that aren't connected to arcs.
//	private void exportPwrGnd(Cell c, ExportNamer vdd, ExportNamer gnd) {
//        EditingPreferences ep = getEditingPreferences();
//		for (Iterator<NodeInst> niIt=c.getNodes(); niIt.hasNext();) {
//			NodeInst ni = niIt.next();
//			if (!(ni.getProto() instanceof Cell)) continue;
//			for (Iterator<PortInst> piIt=ni.getPortInsts(); piIt.hasNext();) {
//				PortInst pi = piIt.next();
//				if (!Utils.isPwrGnd(pi)) continue;
//				if (pi.hasConnections()) continue;
//				Export e;
//				if (Utils.isPwr(pi)) {
//					e = Export.newInst(c, pi, vdd.nextName(), ep);
//					e.setCharacteristic(PortCharacteristic.PWR);
//				} else {
//					e = Export.newInst(c, pi, gnd.nextName(), ep);
//					e.setCharacteristic(PortCharacteristic.GND);
//				}
//			}
//		}
//	}
	
	
//	private void doInfinityABC(Cell autoLay, Cell schCell) {
//        List<NodeInst> layInsts = new ArrayList<NodeInst>();
//        List<ToConnect> toConns = new ArrayList<ToConnect>();
//        LayoutNetlist layNets = createLayInstsFromSch(autoLay, schCell);
//
//        flipInfinityC(layInsts);
//        stackLayInsts(layInsts);
//        overlapInfinityC(layInsts);
//        
////        Rectangle2D colBounds = findColBounds(layInsts);
////        addEssentialBounds(layInsts, colBounds);
//        addEssentialBounds(autoLay);
//        router.connectPwrGnd(layInsts);
//
//        List<NodeInst> scanList = new ArrayList<NodeInst>(layInsts);
//        reverseScanListInfinityC(scanList);
//        scan.stitchScanChains(scanList, router);
//        
//		// debug
//		//for (ToConnect cl : toConns)  prln("  N-Pin "+cl.toString());
//        
//        //List<ToConnect> twoPins = reduceToTwoPin(toConns);
////        List<ToConnect> twoOrThreePins = reduceToTwoOrThreePin(toConns);
////        
////        LayerChannels m2chan = new LayerChannels();
////        LayerChannels m3chan = new LayerChannels();
////        
////        findChannels(m2chan, m3chan, layInsts);
////        
////        route(twoOrThreePins, m2chan, m3chan);
//        
////        List<NodeInst> covers = coverWithContacts(layInsts);
////        //exportVddGndCover(vddCnt, gndCnt, covers);
////        List<ArcProto> m3Layer = new ArrayList<ArcProto>();
////        m3Layer.add(tech().m3());
////		AbutRouter.abutRouteBotTop(covers.get(0), layInsts.get(0), 0, m3Layer);
//		
//        ExportNamer vddNmr = new ExportNamer("vdd");
//        ExportNamer gndNmr = new ExportNamer("gnd");
//		//exportPwrGnd(autoLay, vddNmr, gndNmr);
//        exportPwrGnd(layInsts,  vddNmr, gndNmr);
//		//fattenPwrGnd(layInsts);
//        
//        // Debug
//        //dumpChannels(m2chan, m3chan);
//
//        System.out.println("done.");
//	}
	
	/** If any PortInst is a scan port then ToConnect is a scan ToConnect */
	boolean isScanToConnect(ToConnect tc) {
		for (PortInst pi : tc.getPortInsts()) {
			if (scan.isScan(pi)) return true;
		}
		return false;
	}
	
	private List<ToConnect> selectScanToConnects(List<ToConnect> toConns) {
		List<ToConnect> scans = new ArrayList<ToConnect>();
		for (ToConnect tc : toConns) if (isScanToConnect(tc)) scans.add(tc);
		
		// debug
		for (ToConnect tc : scans) {
			prln("selectScanToCOnnects: "+tc.toString());
		}
		
		return scans;
	}
	private List<ToConnect> selectSignalToConnects(List<ToConnect> toConns) {
		List<ToConnect> signals = new ArrayList<ToConnect>();
		for (ToConnect tc : toConns) {
			if (isScanToConnect(tc)) continue;
			if (Utils.isPwrGnd(tc)) continue;
			signals.add(tc);
		}
		return signals;
	}
	
	
	// --------------------- methods for physical designers -------------------
	protected FlagDesign(FlagConfig cfg, FlagConstructorData data) {
		this.config = cfg;
		scan = new Scan(config.chains, cfg);
		router = new Router(config, scan, data.getEditingPreferences());
		sogRouterAdapter = new SogRouterAdapter(data.getJob());
	}
	
	protected static void prln(String s) {Utils.prln(s);}
	protected static void pr(String s) {Utils.pr(s);}
	protected static void error(boolean cond, String msg) {Utils.error(cond, msg);}
	
	protected void addEssentialBounds(Cell c) {
        EditingPreferences ep = getEditingPreferences();
		Rectangle2D bounds = Utils.findBounds(c);
		LayoutLib.newNodeInst(tech().essentialBounds(), ep,
							  bounds.getMinX(),
		                      bounds.getMinY(),
		                      DEF_SIZE, DEF_SIZE, 180, c);
		LayoutLib.newNodeInst(tech().essentialBounds(), ep,
		                      bounds.getMaxX(),
		                      bounds.getMaxY(),
		                      DEF_SIZE, DEF_SIZE, 0, c);
	}
	/** Traverse schematic hierarchy. For each instance of primitive Cell
	 * in the schematic, instantiate the layout of that primitive Cell in
	 * the layout. Return a list of the layout
	 * instances sorted by y coordinate of corresponding schematic instances. */
	protected LayoutNetlist createLayoutInstancesFromSchematic(FlagConstructorData data) {
		Cell layCell = data.getLayoutCell();
		Cell schCell = data.getSchematicCell();
        SchematicVisitor visitor = new SchematicVisitor(layCell, getEditingPreferences());
        HierarchyEnumerator.enumerateCell(schCell, VarContext.globalContext, visitor);
        List<NodeInst> sortedLayInsts = getSortedLayInsts(visitor);
        printInstanceReport(sortedLayInsts);
        return new LayoutNetlist(layCell, sortedLayInsts,
        		                 visitor.getLayoutToConnects());
	}
	protected void stitchScanChains(LayoutNetlist layNets) {
		scan.stitchScanChains(layNets.getLayoutInstancesSortedBySchematicPosition(), router);
	}
	protected void stitchScanChainsSog(LayoutNetlist layNets, EditingPreferences ep, SeaOfGates.SeaOfGatesOptions prefs) {
		List<ToConnect> scans = selectScanToConnects(layNets.getToConnects());
		sogRouterAdapter.route(scans, ep, prefs);
	}
	protected void routeSignalsSog(List<ToConnect> toConns, EditingPreferences ep, SeaOfGates.SeaOfGatesOptions prefs) {
		List<ToConnect> signals = selectSignalToConnects(toConns);
		sogRouterAdapter.route(signals, ep, prefs);
	}
	protected void routeSignals(LayoutNetlist layNets) {
		List<ToConnect> signals = selectSignalToConnects(layNets.getToConnects());
		router.routeSignals(signals, layNets);
	}
	protected void reexportPowerGround(Cell c) {
        EditingPreferences ep = getEditingPreferences();
		List<Geometric> allNodes = new ArrayList<Geometric>();
		for (Iterator<NodeInst> it = c.getNodes(); it.hasNext(); ) {
			allNodes.add(it.next());
		}
		/* int num = */ ExportChanges.reExportNodes(c, allNodes, false, true, true, true, true, ep);
	}
	protected void reexportSignals(LayoutNetlist layNets) {
		Cell layCell = layNets.getLayoutCell();
		List<ToConnect> toConns = layNets.getToConnects();
		reExport(layCell, toConns);
	}
	protected void addNccVddGndExportsConnectedByParent(Cell c) {
        EditingPreferences ep = getEditingPreferences();
		NccCellAnnotations.addNccAnnotation(c, "exportsConnectedByParent vdd /vdd_[0-9]+/", ep);
		NccCellAnnotations.addNccAnnotation(c, "exportsConnectedByParent gnd /gnd_[0-9]+/", ep);
	}
	

}
