/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: HierarchyInfo.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/>.
 */
package com.sun.electric.tool.ncc.processing;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.tool.ncc.basic.CellContext;
import com.sun.electric.tool.ncc.basic.CompareList;
import com.sun.electric.tool.ncc.basic.CompareLists;
import com.sun.electric.tool.Job;

/** Information needed to perform hierarchical netlist comparison.
 * NCC usually performs comparisons hierarchically. NCC starts with
 * leaf Cells and works towards the root Cells. After NCC checks
 * Cells that are lower in the design hierarchy, NCC saves information
 * that allows it abstract those cells when it compare Cells 
 * higher in the design hierarchy. The information necessary to abstract
 * lower level cells is stored in HierarchyInfo.  */
public class HierarchyInfo {
	/** name of the current CompareList (usually CellGroup name) */ 
	private String subcktName;
	/** unique int assigned to the current compareList */
	private int compareListID=0;
	/** the Cells we've added to the current compareList */
	private List<Cell> cellsInCompareList = new ArrayList<Cell>();
	/** flag indicating that we must remove SubcircuitInfo for all Cells in the 
	 * current Cell Group. */
	private boolean purgeCurrentCompareList;
	/** information for all Cells in all the compareLists we've encountered 
	 * so far */
	private Map<Cell,SubcircuitInfo> cellToSubcktInfo = new HashMap<Cell,SubcircuitInfo>();
	private Set<Cell> cellsInSharedCellGroups;

	// ----------------------------- public methods ---------------------------
	/** You must call this before you begin comparing Cells in a new 
	 * compareList. Then for each Cell in the compareList you must call 
	 * addSubcircuitInfo(). However, if a comparison reveals an Export name 
	 * mismatch then you must call purgeCompareList() after which it 
	 * doesn't matter what you do for the rest of the compareList. */
	public void beginNextCompareList(String subcktName) {
		this.subcktName = subcktName;
		compareListID++;
		purgeCurrentCompareList = false;
		cellsInCompareList.clear();
		cellsInSharedCellGroups = null;
	}
	/** Restrict subcircuit detection. We only want to detect a subcircuit if
	 * its corresponding CellGroup is instantiated by both Cells begin compared.
	 * When we perform a hierarchical comparison we need to rescan the 
	 * sub-hierarchies for each pair of Cells being compared. 
	 * <p> 
	 * Also, we should not detect a subcircuit if the Cell is in the current
	 * compare list. Here's the weird case that motivates this.  Suppose B{lay}
	 * instantiates A{lay}. Suppose the designer compares A{sch} with B{lay}. 
	 * NCC builds a compare list with A{sch}, A{lay}, and B{lay}. NCC might 
	 * first compare A{sch} with A{lay}. Then when NCC compares A{sch} with
	 * B{lay} it will fail because A gets treated as a subcircuit. However,
	 * if NCC first compares A{sch} with B{lay} then the second comparison:
	 * A{sch} with A{lay} will pass. Unfortunately, NCC's choice is random.
	 * Successive runs produce different results.
	 * <p>  
	 * In general the compare list might have n schematics and m layouts and 
	 * the schematics may instantiate one-another and the layouts might 
	 * instantiate one another. If we prevent all the schematics and layouts
	 * from being treated as subcells then any order of comparison should 
	 * pass. */
	public void restrictSubcktDetection(CellContext cc1, CellContext cc2,
			                            Set compareListCells) {
		List<CompareList> compareLists = CompareLists.getCompareLists(cc1, cc2);
		cellsInSharedCellGroups = new HashSet<Cell>();
		for (CompareList compareList : compareLists) {
			for (Iterator<CellContext> it2=compareList.iterator(); it2.hasNext();) {
				Cell c = it2.next().cell;
				if (!compareListCells.contains(c)) cellsInSharedCellGroups.add(c);
			}
		}
	}
	/** Add new subcircuit information for each Cell in the compareList so it
	 * can be treated as a subcircuit primitive in a future 
	 * hierarchical comparison at a higher level. This method must be called
	 * for each Cell in the compareList. 
	 * <p>However, if a comparison reveals an Export name mismatch then you must
	 * call purgeCurrentCompareList() after which it doesn't matter what 
	 * you do. */
	public void addSubcircuitInfo(Cell c, SubcircuitInfo subcktInfo) {
		if (purgeCurrentCompareList) return;
		Job.error(cellToSubcktInfo.containsKey(c),
						"SubcircuitInfo already exists for Cell");
		cellsInCompareList.add(c);
		cellToSubcktInfo.put(c, subcktInfo);
	}
	/** You must call this method if a Cell comparison reveals an Export name 
	 * mismatch. In that case we can no longer treat cells in compareList as a 
	 * subcircuit primitives at higher levels. Instead we must purge all 
	 * information related to Cells in this compareList so that we will flatten 
	 * through them when comparing from higher levels in the hierarchy. */
	public void purgeCurrentCompareList() {
		purgeCurrentCompareList = true;
		for (Cell c : cellsInCompareList) {
			Job.error(!cellToSubcktInfo.containsKey(c), "Cell not in map?");
			cellToSubcktInfo.remove(c);
		}
		cellsInCompareList.clear();
	}

	/** name of the subcircuit being compared */
	public String currentSubcircuitName() {return subcktName;}
	/** unique int ID of subcircuit being compared */
	public int currentSubcircuitID() {return compareListID;}
	/** should I treat an instance of this cell as a subcircuit primitive 
	 * in the current comparison? */
	public boolean treatAsPrimitive(Cell c) {
		return cellToSubcktInfo.containsKey(c) &&
		       (cellsInSharedCellGroups==null || 
		       	cellsInSharedCellGroups.contains(c));
	}
	/** get me information I need to treat an instance of this Cell as a
	 * subcircuit primitive */ 
	public SubcircuitInfo getSubcircuitInfo(Cell c) {
		return (SubcircuitInfo) cellToSubcktInfo.get(c);
	}
}
