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

/* 
 * NccGlobals holds all the global state for an Ncc run.  All class
 * members that were previously static have been moved to this
 * class. This allows an Ncc run to be completely thread safe.
 */
package com.sun.electric.tool.ncc;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.HierarchyEnumerator.NetNameProxy;
import com.sun.electric.database.hierarchy.HierarchyEnumerator.NodableNameProxy;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.tool.ncc.basic.NccUtils;
import com.sun.electric.tool.ncc.netlist.NccNetlist;
import com.sun.electric.tool.ncc.netlist.NetObject;
import com.sun.electric.tool.ncc.netlist.Part;
import com.sun.electric.tool.ncc.netlist.Wire;
import com.sun.electric.tool.ncc.result.BenchmarkResults;
import com.sun.electric.tool.ncc.trees.Circuit;
import com.sun.electric.tool.ncc.trees.EquivRecord;
import com.sun.electric.tool.ncc.trees.LeafEquivRecords;
import com.sun.electric.tool.user.ncc.NccGuiInfo;
import com.sun.electric.tool.Job;

/**
 * Generate non-recurring random integers
 */
class NccRandom {
	private Random randGen = new Random(204);
	private HashSet<Integer> randoms = new HashSet<Integer>();

	public int next() {
		while (true) {
			Integer r = new Integer(randGen.nextInt());
			if (!randoms.contains(r)) {
				randoms.add(r);
				return r.intValue();
			}
		}
	}
}
/** I tried to make NCC thread safe. Therefore there are a minimum of 
 * static variables. Instead, most of what would have been NCC's 
 * global variables are stored in NccGlobals. A new NccGlobals
 * is created every time NccEngine.compare() is started. Therefore two jobs
 * can call NCC at the same time without interfering with each
 * other.
 */
public class NccGlobals implements NccGlobalsReportable {
	// ---------------------------- private data ------------------------------
	private static final int CODE_PART= 0;
	private static final int CODE_WIRE= 1;
	private static final int CODE_PORT= 2;
	/** used to assign new hash code values to EquivRecords */ 
	private NccRandom randGen = new NccRandom();
	/** all options controlling an Ncc run */ private final NccOptions options;
	/** object says when user wants abort */  private final Aborter aborter;

    /** root of the EquivRecord tree */       private EquivRecord root;
    /** subtree holding parts */              private EquivRecord parts;
    /** subtree holding wires */              private EquivRecord wires;
    /** subtree holding ports */              private EquivRecord ports;
	/** root Cell of each netlist */  		  private Cell[] rootCells;
	/** VarContext of root of each netlist */ private VarContext[] rootContexts;
	/** pass number shared by strategies */   public int passNumber;
	/** leaf nodes of parts tree */           private LeafEquivRecords partLeafRecs;
	/** leaf nodes of wires tree */           private LeafEquivRecords wireLeafRecs;
	/** leaf nodes of ports tree */			  private LeafEquivRecords portLeafRecs;
    /** can't build netlist? */               private boolean[] cantBuildNetlist;
    /** mismatches displayed by GUI */        private NccGuiInfo nccGuiInfo;
    /** holds performance counters */         private BenchmarkResults benchResults = new BenchmarkResults();

    
	// ----------------------------- private methods --------------------------
	private List getNetObjs(int code, NccNetlist nets) {
		switch (code) {
		  case CODE_PART:  return nets.getPartArray();
		  case CODE_WIRE:  return nets.getWireArray(); 
		  case CODE_PORT:  return nets.getPortArray();
		}
		error("invalid code");
		return null;
	}	
	
	private void countNetObjs(int[] counts, Iterator<EquivRecord> it) {
		while (it.hasNext()) {
			EquivRecord er = it.next();
			error(!er.isLeaf(), "Must be leaf");
			int numCkts = er.numCircuits();
			error(counts.length!=numCkts, "different number of Circuits");
			Iterator<Circuit> it2 = er.getCircuits();
			for (int i=0; i<numCkts; i++) {
				counts[i] += it2.next().numNetObjs(); 
			}
		}
	}

	private int[] getNetObjCounts(LeafEquivRecords leaves) {
    	int[] counts = new int[getNumNetlistsBeingCompared()];
    	countNetObjs(counts, leaves.getMatched());
    	countNetObjs(counts, leaves.getNotMatched());
    	return counts;
	}
	
	private EquivRecord buildEquivRec(int code, List<NccNetlist> nccNets) {
		boolean atLeastOneNetObj = false;
		List<Circuit> ckts = new ArrayList<Circuit>();
		for (NccNetlist nets : nccNets) {
			List<NetObject> netObjs = getNetObjs(code, nets);
			if (netObjs.size()!=0)  atLeastOneNetObj = true;
			ckts.add(Circuit.please(netObjs));
		}
		if (!atLeastOneNetObj) return null;
		return EquivRecord.newLeafRecord(code, ckts, this);
	}
	
	// ----------------------------- public methods --------------------------
	/**
	 * The constructor initializes global root, parts, wires, and ports from 
	 * net lists. 
	 * @param options the options controlling how NCC performs the comparison
	 * @param aborter an object that NCC queries to determine if the user
	 * wants to abort NCC in the middle of a run. 
	 */
	public NccGlobals(NccOptions options, Aborter aborter) {
		this.options = options;
		this.aborter = aborter;
        nccGuiInfo = new NccGuiInfo();
	}
	/** A conveniently terse method for printing to Electric's message window */
	public void prln(String s) {System.out.println(s); System.out.flush();}
	/** A conveniently terse method for printing to Electric's message window */
	public void pr(String s) {System.out.print(s); System.out.flush();}

	/** Build the initial equivalence record trees from the netlists that are
	 * to be compared. In principle, NCC can compare more than two netlists
	 * at the same time, but in practice we've never found a use for this.
	 * @param nccNets two or more netlists that are supposed to be topologcially
	 * identical.
	 */
	public void setInitialNetlists(List<NccNetlist> nccNets) {
		parts = buildEquivRec(CODE_PART, nccNets);
		wires = buildEquivRec(CODE_WIRE, nccNets);
		ports = buildEquivRec(CODE_PORT, nccNets);

		List<EquivRecord> el = new ArrayList<EquivRecord>();
		if (parts!=null) el.add(parts); 
		if (wires!=null) el.add(wires); 
		if (ports!=null) el.add(ports);
		root = EquivRecord.newRootRecord(el);
		
		rootCells = new Cell[nccNets.size()];
		rootContexts = new VarContext[nccNets.size()];
		cantBuildNetlist = new boolean[nccNets.size()];
		int i=0;
		for (Iterator<NccNetlist> it=nccNets.iterator(); it.hasNext(); i++) {
			NccNetlist nl = it.next();
			rootCells[i] = nl.getRootCell();
			rootContexts[i] = nl.getRootContext();
            cantBuildNetlist[i] = nl.cantBuildNetlist();
		}
	}
	/** Initialization.
	 * Tricky! initLeafLists() must be called AFTER series/parallel merging.
	 * and AFTER Local Partitioning!!! Both of those processes violate 
	 * invariants assumed by LeafEquivRecords. */
	public void initLeafLists() {
		partLeafRecs = new LeafEquivRecords(parts, this);
		wireLeafRecs = new LeafEquivRecords(wires, this);
		portLeafRecs = new LeafEquivRecords(ports, this);
	}
	/** get the root of the equivalence record tree */
	public EquivRecord getRoot() {return root;}
	/** get the root of equivalence record subtree for Parts */
	public EquivRecord getParts() {return parts;}
	/** get the root of the equivalence record subtree for Wires */
	public EquivRecord getWires() {return wires;}
	/** get the root of the equivalence record subtree for Ports */
	public EquivRecord getPorts() {return ports;}
	/** Say how many netlists are being compared. This will usually be two. 
	 * In principle NCC can compare any number of netlists but we've never
	 * found a use for this feature. */
	public int getNumNetlistsBeingCompared() {return rootCells.length;}
	/** get an array of root Cells, one per netlist */
	public Cell[] getRootCells() {return rootCells;}
	/** get an array of VarContexts, one per netlist */
	public VarContext[] getRootContexts() {return rootContexts;}
	/** get an array of root Cell Names, one per netlist.  */
	public String[] getRootCellNames() {
		String[] rootCellNames = new String[rootCells.length];
		for (int i=0; i<rootCells.length; i++) {
			rootCellNames[i] = NccUtils.fullName(rootCells[i]);
		}
		return rootCellNames;
	}
	/** Print more important status messages into the Electric 
	 * messages window. The user selects how verbose NCC is
	 * using the howMuchStatus option. 
	 * @param msg the message to be printed.
	 */
	public void status1(String msg) {
		if (options.howMuchStatus>=1) prln(msg);
	}
	/** Print less important status messages into the Electric 
	 * messages window. The user selects how verbose NCC is
	 * using the howMuchStatus option. 
	 * @param msg the message to be printed.
	 */
	public void status2(String msg) {
		if (options.howMuchStatus>=2) prln(msg); 
	}
	/** Flush System.out */
	public void flush() {System.out.flush();}
	/** Print a message and abort execution if pred is true.
	 * @param pred if true then an error has occurred
	 * @param msg message to print when error occurs
	 */
	public void error(boolean pred, String msg) {
		Job.error(pred, msg);
	}
	/** Print a message and abort execution
	 * @param msg message to print when error occurs
	 */
	public void error(String msg) {
        Job.error(true, msg);}
	
	/** Get the NCC options.
	 */
	public NccOptions getOptions() {return options;}
	
	/** Generate non-recurring pseudo-random integers */
	public int getRandom() {return randGen.next();}
	
	/** Get the leaf equivalence records of the Part equivalence
	 * record sub tree
	 */  
	public LeafEquivRecords getPartLeafEquivRecs() {return partLeafRecs;}
	/** Get the leaf equivalence records of the Wire equivalence
	 * record sub tree
	 */  
	public LeafEquivRecords getWireLeafEquivRecs() {return wireLeafRecs;}
	/** Get the leaf equivalence records of the Port equivalence
	 * record sub tree
	 */  
	public LeafEquivRecords getPortLeafEquivRecs() {return portLeafRecs;}

	/** @return an NetNameProxy[][]. NetNameProxy[d][n] gives the nth net of
	 * the dth design.  NetNameProxy[a][n] is NCC equivalent to NetNameProxy[b][n]
	 * for all a and b.*/
	public NetNameProxy[][] getEquivalentNets() {
		int numDes = getNumNetlistsBeingCompared();
		NetNameProxy[][] equivNets = new NetNameProxy[numDes][];
		int numMatched = wireLeafRecs.numMatched();
		for (int i=0; i<numDes; i++) {
			equivNets[i] = new NetNameProxy[numMatched];
		}
		int wireNdx = 0;
		for (Iterator<EquivRecord> it=wireLeafRecs.getMatched(); it.hasNext(); wireNdx++) {
			EquivRecord er = it.next();
			int cktNdx = 0;
			for (Iterator<Circuit> cit=er.getCircuits(); cit.hasNext(); cktNdx++) {
				Circuit ckt = cit.next();
				Job.error(ckt.numNetObjs()!=1, "not matched?");
				Wire w = (Wire) ckt.getNetObjs().next();
				equivNets[cktNdx][wireNdx] = w.getNameProxy().getNetNameProxy();
			}
		}
		return equivNets;
	}
    
	/** @return an NodableNameProxy[][]. NodableNameProxy[d][n] gives the nth net of
	 * the dth design.  NetNameProxy[a][n] is NCC equivalent to NetNameProxy[b][n]
	 * for all a and b.*/
	public NodableNameProxy[][] getEquivalentNodes() {
		int numDes = getNumNetlistsBeingCompared();
		NodableNameProxy[][] equivParts = new NodableNameProxy[numDes][];
		int numMatched = partLeafRecs.numMatched();
		for (int i=0; i<numDes; i++) {
			equivParts[i] = new NodableNameProxy[numMatched];
		}
		int partNdx = 0;
		for (Iterator<EquivRecord> it=partLeafRecs.getMatched(); it.hasNext(); partNdx++) {
			EquivRecord er = it.next();
			int cktNdx = 0;
			for (Iterator<Circuit> cit=er.getCircuits(); cit.hasNext(); cktNdx++) {
				Circuit ckt = cit.next();
				Job.error(ckt.numNetObjs()!=1, "not matched?");
				Part p = (Part) ckt.getNetObjs().next();
				equivParts[cktNdx][partNdx] = p.getNameProxy().getNodableNameProxy();
			}
		}
		return equivParts;
	}
    
    /** Get mismatches to be displayed in the GUI
     * @return an object with mismatches to be displayed in GUI */
    public NccGuiInfo getNccGuiInfo() {return nccGuiInfo;}
    
    /** @return true if some netlist can't be built */
    public boolean cantBuildNetlist() {
    	for (int i=0; i<cantBuildNetlist.length; i++) 
    		if (cantBuildNetlist[i]) return true;
    	return false; 
    }
    /** @return true if ith netlist can't be built */
    public boolean[] cantBuildNetlistBits() {return cantBuildNetlist;}
    
	/** @return array of Part counts. One array element per Circuit */
    public int[] getPartCounts() { return getNetObjCounts(partLeafRecs); }
	/** @return array of Wire counts. One array element per Circuit */
    public int[] getWireCounts() { return getNetObjCounts(wireLeafRecs); }
	/** @return array of Port counts. One array element per Circuit */
    public int[] getPortCounts() { return getNetObjCounts(portLeafRecs); }
    /** @return true if user wants to abort */
    public boolean userWantsToAbort() {return aborter.userWantsToAbort();}

    /** Get result of benchmarking performance counters*/
	public BenchmarkResults getBenchmarkResults() {
		return benchResults;
	}
}
