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

import com.sun.electric.database.EditingPreferences;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.prototype.PortCharacteristic;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.technology.Technology;
import com.sun.electric.tool.Job;
import com.sun.electric.util.math.DBMath;

/**
 *
 */
public class StdCellParams350 extends StdCellParams {

    private static final double minGateWid = 0.6;
    private static final double maxMosWidth = Double.POSITIVE_INFINITY;
	private static final double m1OverhangsDiffCont = 0.15;
	private static final double m1Space = 0.45;


    private TechType tech;

	private static void error(boolean pred, String msg) {
		Job.error(pred, msg);
	}

    public static StdCellParams invParams(Technology technology, EditingPreferences ep) {
        StdCellParams stdCell = new StdCellParams350(technology, ep);
        stdCell.setSizeQuantizationError(0);
        stdCell.setMaxMosWidth(1000);
        stdCell.setVddY(5.5);
        stdCell.setGndY(-4.1);
        stdCell.setPmosWellHeight(6.4);
        stdCell.setNmosWellHeight(5.0);
//        stdCell.setVddExportName("VDD");
//        stdCell.setGndExportName("VSS");
        stdCell.setSimpleName(true);
        stdCell.enableNCC("purpleFour");
        return stdCell;
    }

    private StdCellParams350(Technology tech, EditingPreferences ep) {
        super(tech, ep);
        this.tech = getTechType();
        setGndWidth(1.4);
        setVddWidth(1.4);
    }

	/** Calculate the number of folds and the width of a MOS
	 * transistor. Given that there is a limited physical height into
	 * which a MOS transistor must fit, divide the total required width:
	 * totWid into fingers.  Each finger must have width less than or
	 * equal to spaceAvailWid.
	 *
	 * <p> If it is possible, allocate an even number of fingers so that
	 * the left most and right most diffusion contacts may be connected
	 * to power or ground to reducing the capacitance of the inner
	 * switching diffusion contacts.
	 * @param spaceAvailWid the height in the standard cell that is
	 * available for the diffusion of the MOS transistor.
	 * @param totWid the total electrical width required.
	 * @param groupSz This method creates fingers in multiples of
	 * groupSz. For example, if groupSz is 2, then only even numbers of
	 * fingers are created. This is needed when one FoldedMos is
	 * actually going to be wired up as 2 identical, independent
	 * transistors, for example the 2 PMOS pullups for a 2-input NAND
	 * gate. */
	public FoldsAndWidth calcFoldsAndWidth(
		double spaceAvailWid,
		double totWid,
		int groupSz) {
		if (totWid == 0)
			return null;
		double maxAvailWid = Math.min(spaceAvailWid, maxMosWidth);
		int nbGroups = calcNbGroups(maxAvailWid, totWid, groupSz);

		double gateWid = roundGateWidth(totWid / groupSz / nbGroups);

		// If we're unfortunate, rounding up gate width causes gate's width
		// to exceed space available.
		if (gateWid > maxAvailWid) {
			nbGroups = calcNbGroups(maxAvailWid - .5, totWid, groupSz);
			gateWid = roundGateWidth(totWid / groupSz / nbGroups);
		}

		double physWid = Math.max(tech.getDiffContWidth(), gateWid);
		if (gateWid < minGateWid)
			return null;
		return new FoldsAndWidth(nbGroups * groupSz, gateWid, physWid);
	}

	private int calcNbGroups(double maxAvailWid, double totWid, int groupSz) {
		int nbGroups = (int) Math.ceil(totWid / maxAvailWid / groupSz);

		// If groupSz is even then we always create an even number of
		// fingers and we don't have to add more fingers to reduce
		// diffusion capacitance.
		if (groupSz % 2 == 0)
			return nbGroups;

		// If nbGroups is even then we always create an even number of
		// fingers and we don't have to add more fingers to reduce
		// diffusion capacitance
		if (nbGroups % 2 == 0)
			return nbGroups;

        // if totWid is less than maxAvailWid and groupSz is 1, just
        // use 1 gate (no fingers)
        if ((totWid < maxAvailWid) && (groupSz == 1))
            return 1;

		// try adding one more group to get an even number of fingers
		int roundupGroups = nbGroups + 1;
		double wid = totWid / groupSz / roundupGroups;

		// Don't fold if gate width is less than width of diffusion
		// contact.
		if (wid >= tech.getDiffContWidth())
			nbGroups = roundupGroups;

		return nbGroups;
	}

	/** Wire pmos or nmos to vdd or gnd, respectively.  Add an export
	 * if there is none. */
	public void wireVddGnd(FoldedMos[] moss, SelectSrcDrn select, Cell p) {
		FoldedMos mos = moss[0];

		PortInst leftDiff = mos.getSrcDrn(0);
		Cell f = leftDiff.getNodeInst().getParent();
		double busWid = mos instanceof FoldedPmos ? getVddWidth() : getGndWidth();
		double busY = mos instanceof FoldedPmos ? getVddY() : getGndY();
		TrackRouter net = new TrackRouterH(tech.m1(), busWid, busY, tech, ep, p);

		String exportNm = mos instanceof FoldedPmos ? getVddExportName() : getGndExportName();
		if (f.findPortProto(exportNm)==null) {
			// The export doesn't yet exist.  Create and export a metal2 pin
			// aligned with the first diffusion.
			double x = leftDiff.getBounds().getCenterX();
			NodeInst pinProt = LayoutLib.newNodeInst(tech.m1pin(), ep, x,
			                                         busY, LayoutLib.DEF_SIZE, LayoutLib.DEF_SIZE, 0, f);
			PortInst pin = pinProt.getOnlyPortInst();
			Export e = Export.newInstance(f, pin, exportNm, ep);
			PortCharacteristic role =
				mos instanceof FoldedPmos ? getVddExportRole() : getGndExportRole();
			e.setCharacteristic(role);

			// Connect the export to itself using a standard width power or
			// ground strap.  The width of this strap serves as a hint to
			// Electric as to the width to use to connect to this export at
			// the next level up.
			LayoutLib.newArcInst(tech.m1(), ep, busWid, pin, pin);

			net.connect(pin);
		}
		double diffY = LayoutLib.roundCenterY(leftDiff);
		double notchLoY = Math.min(busY - busWid / 2, diffY);
		double notchHiY = Math.max(busY + busWid / 2, diffY);
		PortInst lastDiff = null;
		for (int i=0; i<moss.length; i++) {
			for (int j=0; j<moss[i].nbSrcDrns(); j++) {
				if (select.connectThisOne(i, j)) {
					PortInst thisDiff = moss[i].getSrcDrn(j);
					net.connect(thisDiff);

					if (lastDiff!=null) {
						// Check to see if we just created a notch.
						double leftX = LayoutLib.roundCenterX(lastDiff);
						double rightX = LayoutLib.roundCenterX(thisDiff);
						error(leftX>rightX,
							  "wireVddGnd: trans not sorted left to right");
						double deltaX = rightX - leftX;
						if (deltaX>0
							&& deltaX < m1OverhangsDiffCont*2+m1Space) {
							// Fill notches, sigh! (This is starting to lose
							// it's novelty value.)
							//
							// Make height integral number of lambdas so
							// centerY will be on .5 lambda grid and connecting
							// to center won't generate CIF resolution errors.
							double dY = Math.ceil(notchHiY - notchLoY);
							NodeInst patchNode =
							  LayoutLib.newNodeInst(tech.m1Node(), ep, (leftX+rightX)/2, notchLoY+dY/2,
							  deltaX, dY, 0, f);
							PortInst patch = patchNode.getOnlyPortInst();
							LayoutLib.newArcInst(tech.m1(), ep, LayoutLib.DEF_SIZE, patch, thisDiff);
						}
					}
					lastDiff = thisDiff;
				}
			}
		}
	}

	/** essential bounds for cells with both NMOS and PMOS */
	public void addEssentialBounds(double loX, double hiX, Cell cell) {
		LayoutLib.newNodeInst(tech.essentialBounds(), ep, loX, getGndY(), LayoutLib.DEF_SIZE,
		                      LayoutLib.DEF_SIZE, 180, cell);
		LayoutLib.newNodeInst(tech.essentialBounds(), ep, hiX, getVddY(), LayoutLib.DEF_SIZE,
			                  LayoutLib.DEF_SIZE, 0, cell);
	}

    @Override
	public double roundGateWidth(double w) {
        return DBMath.round(Math.ceil(w*10.0)/10.0);
	}

    public double getDRCRingSpacing() {
        return 2.3;
    }


}
