/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: StratPortName.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.strategy;
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.tool.ncc.NccGlobals;
import com.sun.electric.tool.ncc.lists.LeafList;
import com.sun.electric.tool.ncc.lists.RecordList;
import com.sun.electric.tool.ncc.netlist.NetObject;
import com.sun.electric.tool.ncc.netlist.Port;
import com.sun.electric.tool.ncc.netlist.Wire;
import com.sun.electric.tool.ncc.trees.Circuit;
import com.sun.electric.tool.ncc.trees.EquivRecord;

/**
 * StratPortName sorts all Wire EquivRecords on the frontier by
 * size. Starting with the smallest first, it finds the first
 * EquivRecord that splits due to Export names. It divides that
 * EquivRecord and returns the offspring EquivRecords.
 */
public class StratPortName extends Strategy {
	private int numWiresProcessed;
	private int numEquivProcessed;
	private Map<Wire,Integer> theMap;
	private boolean doneOne;
	
	private StratPortName(NccGlobals globals) {super(globals);}
	
	public static LeafList doYourJob(NccGlobals globals) {
		// if no Wires suppress all StratPortName messages
		if (globals.getWires()==null) return new LeafList(); 
		
		StratPortName wn = new StratPortName(globals);
		wn.preamble();
        //LeafList front = StratFrontier.doYourJob(globals.getWires(), globals);
		Iterator<EquivRecord> frontier = globals.getWireLeafEquivRecs().getNotMatched();
		LeafList ll = new LeafList();
		while (frontier.hasNext()) ll.add(frontier.next());

		LeafList el = wn.doFor(ll);
		wn.summary(el);
		return el;
	}
		
	private void preamble() {
		startTime("StratPortName", "all active Wires");
	}
	
	private void summary(LeafList offspring){
		globals.status2("StratPortName processed " +
							numWiresProcessed + " Wires from " +
							numEquivProcessed + " EquivRecords");
		globals.status2(offspringStats(offspring));

		globals.status2(offspring.sizeInfoString());
		elapsedTime();
	}
	
	/** 
	 * printTheMap is a debug routine that exhibits the map.
	 * @param m the Map to exhibit
	 */
	private void printTheMap(Map<Wire,Integer> m){
		globals.status2("  printing an EquivRecord map of size= " + m.size());
		if(m.size() == 0)return;
		for (Wire w : m.keySet()) {
			Object oo= m.get(w);
			if(oo == null){
				globals.status2(" "+w.instanceDescription() + " maps to null");
			} else {
				Integer i= (Integer)oo;
				globals.status2(" "+w.instanceDescription() + " maps to " + i.intValue());
			}
		}
	}
	private Map<String,Wire> getMapFromExportNamesToWires(Circuit wires){
		Map<String,Wire> out = new HashMap<String,Wire>();
		for (Iterator<NetObject> it=wires.getNetObjs(); it.hasNext();) {
			NetObject n= it.next();
			error(!(n instanceof Wire), "getExportMap expects only Wires");
			Wire w= (Wire)n;
			Port p = w.getPort();
			if (p!=null && !p.getToBeRenamed()) {
				for (Iterator<String> ni=p.getExportNames(); ni.hasNext();) {
					String exportNm = ni.next();
					error(out.containsKey(exportNm),
						  "different wires have the same export name?");
					out.put(exportNm, w);
				}
			}
		}
		return out;
	}

	/** 
	 * getWireExportMap produces a map of Wires to arbitrary Integers
	 * based on matching export names.
	 * @return a map of Wires to Integers
	 */
	private Map<Wire,Integer> getWireExportMap(EquivRecord er){
		//step 1 - get the string maps from the circuits
		List<Map<String,Wire>> mapPerCkt = new ArrayList<Map<String,Wire>>(); //to hold the circuit's maps
		Set<String> keys = new HashSet<String>();
		for (Iterator<Circuit> ci=er.getCircuits(); ci.hasNext();) {
			Map<String,Wire> exportToWire = getMapFromExportNamesToWires(ci.next());
			mapPerCkt.add(exportToWire);
			keys.addAll(exportToWire.keySet());
		}
		//keys now holds all possible Strings that are names
		if (keys.size()==0)  return new HashMap<Wire,Integer>(); //no ports
		HashMap<Wire,Integer> out = new HashMap<Wire,Integer>();
		int i= 0;
		for (String key : keys) {
			//check that all maps have this key
			List<Wire> wires = new ArrayList<Wire>();
			for (Map<String,Wire> map : mapPerCkt) {
				if(map.containsKey(key)){
					Wire w= map.get(key);
					wires.add(w);
				}
			}
			//does wires contain enough records?
			if(wires.size() == mapPerCkt.size()){
				//yes it does
				i++;
				for (Wire w : wires) {
					out.put(w, Integer.valueOf(i));
				}
			}
		}
		printTheMap(out);
		return out;
	}

    public LeafList doFor(RecordList g){
		LeafList gg= (LeafList)g;
		gg.sortByIncreasingSize();
		return super.doFor(gg);
    }
	
    public LeafList doFor(EquivRecord g){
		LeafList out;
		if(g.isLeaf()){
			EquivRecord er= (EquivRecord)g;
			if (doneOne) return new LeafList();
			numEquivProcessed++;
			theMap = getWireExportMap(er);
			if (theMap.size()==0) return new LeafList();
			doneOne = true; 
			
			out = super.doFor(g);
			globals.status2(" processed "+g.nameString()+
							" with map size= "+theMap.size()+" yields " 
							+out.size()+" offspring ");
		} else {
			out = super.doFor(g);
		}
		return out;
    }
    
	public Integer doFor(NetObject n){
		error(!(n instanceof Wire), "StratPortName expects only Wires");
		numWiresProcessed++;
		Wire w= (Wire)n;
		Integer i= theMap.get(w);
		return i!=null ? i : Integer.valueOf(0);
	}
	
}
