/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: Subcircuit.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.ncc.netlist;
import java.util.HashMap;
import java.util.Map;

import com.sun.electric.database.variable.VarContext;
import com.sun.electric.technology.PrimitiveNode.Function;
import com.sun.electric.tool.ncc.NccOptions;
import com.sun.electric.tool.ncc.netlist.NccNameProxy.PartNameProxy;
import com.sun.electric.tool.ncc.processing.SubcircuitInfo;
import com.sun.electric.tool.Job;

/** A Cell instance that is being treated as a primitive circuit component
 * during a hierarchical netlist comparison */
public class Subcircuit extends Part {
	/** presume that no ports are interchangeable */
	public static class SubcircuitPinType implements PinType {
//		private int typeCode;
//		private int portIndex;
		private String description;
		public SubcircuitPinType(int typeCode, int portIndex, String description) {
//			this.typeCode = typeCode;
//			this.portIndex = portIndex;
			this.description = description;
		}
		public String description() {return description;}
	}

	private final int[] pinCoeffs;
	// For each wire, store the sum of the coefficients of the pins attached to
	// that wire
	private final Map<Wire,Integer> wireToCoeffSum;
	private final SubcircuitInfo subcircuitInfo;
	
	private String getPortName(int i) {return subcircuitInfo.getPortName(i);}
	
	private Map<Wire,Integer> computeWireToCoeffSum(Wire[] pins, int[] coeffs) {
		Map<Wire,Integer> wireToCoeffs = new HashMap<Wire,Integer>();
		for (int i=0; i<pins.length; i++) {
			Wire w = pins[i];
			int coeff = pinCoeffs[i];
			Integer coeffSum = wireToCoeffs.get(w);
			if (coeffSum==null)  coeffSum = Integer.valueOf(0);
			coeffSum += coeff;
			wireToCoeffs.put(w, coeffSum);
		}
		return wireToCoeffs;
	}

	@Override
	public String valueDescription() {return "";}
	@Override
	public int[] getPinCoeffs() {return pinCoeffs;}
	@Override
	public boolean parallelMerge(Part p, NccOptions nccOpt) {
		Job.error(true, "we never parallel merge subcircuits so don't call this method");
		return false;
	}
	
	
	/** I never parallel merge subcircuits so this really doesn't matter. 
	 * This method is too slow for subcircuits with tens of thousands of
	 * pins. It's better to avoid calling this method at a higher level.*/
	@Override
	public Integer hashCodeForParallelMerge() {
		Job.error(true, "we never parallel merge subcircuits so don't call this method");
		
		// include how many Wires may be connected
		int hc = pins.length;
		// include what's connected
		for (int i=0; i<pins.length; i++)  
			hc += pins[i].hashCode() * pinCoeffs[i];
		// include the class
		hc += getClass().hashCode();
		// include subcircuit ID
		hc += subcircuitInfo.getID();
		return Integer.valueOf(hc);
	}
	@Override
	public String typeString() {return subcircuitInfo.getName();}
	
	@Override
	public int typeCode() {
		return type().ordinal() + 
		       (subcircuitInfo.getID() << Part.TYPE_FIELD_WIDTH);
	}
	@Override
	public PinType getPinTypeOfNthPin(int n) {
		PinType[] pinTypes = subcircuitInfo.getPinTypes();
		return pinTypes[n];
	}
	@Override
	public String connectionDescription(Wire w) {
		String msg = "";
		for (int i=0; i<pins.length; i++) {
			if (pins[i]==w) {
				if (msg.length()!=0) msg += ',';
				msg += getPortName(i);
			}
		}
		return msg; 
	}
	@Override
	public String connectionDescription(int maxCon) {
		String msg = "";
		for (int i=0; i<maxCon && i<pins.length; i++) {
			if (msg.length()!=0) msg += " ";
			msg += getPortName(i)+"="+pins[i].getName();
		}
		return msg;
	}
	
	/**  Subcircuits can have tens of thousands of pins. Part.getHashFor(Wire)
	 * is O(n) in the number of pins. Since it gets called n times we have
	 * O(n^2) execution time. Subcircuit.getHashFor(Wire) takes more 
	 * storage but executes in constant time.
	 * @param w the Wire for which a hash code is needed
	 * @return an int with the code contribution. */
	@Override
    public int getHashFor(Wire w) {
		Integer coeffSum = wireToCoeffSum.get(w);
		Job.error(coeffSum==null, "Wire not found");
		return coeffSum*getCode();
    }
	
	public Subcircuit(PartNameProxy instName, VarContext cont, SubcircuitInfo subcircuitInfo,
	                  Wire[] pins) {
		super(instName, cont, Function.UNKNOWN, pins);
		this.subcircuitInfo = subcircuitInfo;
		pinCoeffs = subcircuitInfo.getPortCoeffs();
		
		wireToCoeffSum = computeWireToCoeffSum(pins, pinCoeffs);
	}
}
