/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: StratCheckSizes.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/>.
 */

/** StratFrontier finds all non-matched EquivRecords */

package com.sun.electric.tool.ncc.strategy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;

import com.sun.electric.tool.ncc.NccGlobals;
import com.sun.electric.tool.ncc.NccOptions;
import com.sun.electric.tool.ncc.basic.NccUtils;
import com.sun.electric.tool.ncc.lists.LeafList;
import com.sun.electric.tool.ncc.netlist.Inductor;
import com.sun.electric.tool.ncc.netlist.Josephson;
import com.sun.electric.tool.ncc.netlist.Mos;
import com.sun.electric.tool.ncc.netlist.NetObject;
import com.sun.electric.tool.ncc.netlist.Part;
import com.sun.electric.tool.ncc.netlist.Resistor;
import com.sun.electric.tool.ncc.netlist.Subcircuit;
import com.sun.electric.tool.ncc.result.SizeMismatch.LengthMismatch;
import com.sun.electric.tool.ncc.result.SizeMismatch.Mismatch;
import com.sun.electric.tool.ncc.result.SizeMismatch.WidthMismatch;
import com.sun.electric.tool.ncc.trees.Circuit;
import com.sun.electric.tool.ncc.trees.EquivRecord;

/** Check that corresponding transistors in the schematic and layout
 * have the same channel widths and lengths.
 */
public class StratCheckSizes extends Strategy {
	// ------------------------------ private data -----------------------------
	private NccOptions options;
	private double minWidth, maxWidth, minLength, maxLength;
	private Part minWidPart, maxWidPart, minLenPart, maxLenPart;
	private List<Mismatch> mismatches = new ArrayList<Mismatch>();
    private int cktNdx, minWidNdx, maxWidNdx, minLenNdx, maxLenNdx;
	
    private StratCheckSizes(NccGlobals globals) {
    	super(globals);
    	options = globals.getOptions();
    }
    
    private void checkWidthMismatch() {
		if (!NccUtils.sizesMatch(maxWidth, minWidth, options)) {
			mismatches.add(new WidthMismatch(minWidth, minWidPart, minWidNdx,  
					                         maxWidth, maxWidPart, maxWidNdx));
		}
    }
    private void checkLengthMismatch() {
    	if (!NccUtils.sizesMatch(maxLength, minLength, options)) {
    		mismatches.add(new LengthMismatch(minLength, minLenPart, minLenNdx,
    				                          maxLength, maxLenPart, maxLenNdx));
    	}
    }
    private static class MismatchComparator implements Comparator<Mismatch> {
    	public int compare(Mismatch m1, Mismatch m2) {
    		double diff = m1.relErr() - m2.relErr();
    		if (diff==0) return 0;
    		return diff<0 ? 1 : -1;
    	}
    }
    private void summary() {
    	if (mismatches.size()==0) return; // no news is good news
    	try
    	{
    		Collections.sort(mismatches, new MismatchComparator());
    	} catch (IllegalArgumentException e)
    	{
    		System.out.println("NCC Error sorting " + mismatches.size() + " mismatches:");
    		for(Mismatch mm : mismatches) System.out.println("    " + mm);
    	}
    	System.out.println("  There are "+mismatches.size()+" size mismatches.");
    	for (Mismatch m : mismatches)
    		System.out.print(m.toString());
        globals.getNccGuiInfo().setSizeMismatches(mismatches);
    }

	private boolean matches() {return mismatches.size()==0;}

	private double getWidth(Part p) {
		if (p instanceof Mos) return ((Mos)p).getWidth();
		if (p instanceof Resistor) return ((Resistor)p).getWidth();
    	return 0;
    }
    
    private double getLength(Part p) {
		if (p instanceof Mos) return ((Mos)p).getLength();
		if (p instanceof Resistor) return ((Resistor)p).getLength();
		if (p instanceof Inductor) return ((Inductor)p).getLength();
		if (p instanceof Josephson) return ((Josephson)p).getLength();
    	return 0;
    }

	public LeafList doFor(EquivRecord j) {
		if(j.isLeaf()) {
			if (j.isMatched()) {
				minWidth = minLength = Double.MAX_VALUE;
				maxWidth = maxLength = Double.MIN_VALUE;
                cktNdx = 0;
				super.doFor(j);
				checkWidthMismatch();
				checkLengthMismatch();
			}
		} else {
			super.doFor(j);
		}
		return new LeafList();
	}
	
    public HashMap<Integer,List<NetObject>> doFor(Circuit c){
        HashMap<Integer,List<NetObject>> result = super.doFor(c);
        cktNdx++;
        return result;
    }    
    
	public Integer doFor(NetObject n) {
		Part p = (Part) n;
		if (p instanceof Subcircuit) {
			minWidth = maxWidth = minLength = maxLength = 1;
		} else {
			globals.error(!(p instanceof Mos) &&
					      !(p instanceof Resistor) &&
					      !(p instanceof Inductor) &&
					      !(p instanceof Josephson), "part with no width & length");
			double w = getWidth(p);
			if (w<minWidth) {minWidth=w;  minWidPart=p; minWidNdx=cktNdx;}
			if (w>maxWidth) {maxWidth=w;  maxWidPart=p; maxWidNdx=cktNdx;}
			double l = getLength(p);
			if (l<minLength) {minLength=l;  minLenPart=p; minLenNdx=cktNdx;}
			if (l>maxLength) {maxLength=l;  maxLenPart=p; maxLenNdx=cktNdx;}
		}
		return CODE_NO_CHANGE;
	}

	// ------------------- intended interface ------------------------
    public static boolean doYourJob(NccGlobals globals) {
    	NccOptions options = globals.getOptions();
    	if (!options.checkSizes) return true;

    	EquivRecord parts = globals.getParts();
    	if (parts==null)  return true;
    	
    	StratCheckSizes jsf = new StratCheckSizes(globals);
    	jsf.doFor(parts);
    	jsf.summary();
    	return jsf.matches(); 
    }
}
