/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: Global.java
 * Written by: Dmitry Nadezhin, Sun Microsystems.
 *
 * 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.database.network;

import com.sun.electric.database.prototype.PortCharacteristic;
import com.sun.electric.database.text.Name;

import com.sun.electric.util.TextUtils;

import java.util.Arrays;
import java.util.Iterator;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * A Global define global signal in a network tool.
 */
public class Global {

    /** the name key */
    private Name name;
    /** the 0-based index of this Global. */
    private int index;
    /** All Globals. */
    private static Global[] allGlobals = new Global[0];
    /** Map String -> Global. */
    private static Map<String, Global> globalsByName = new HashMap<String, Global>();
    /** Map canonic String -> Global. */
    private static Map<String, Global> globalsByCanonicName = new HashMap<String, Global>();
    /** global signal ground. */
    public static final Global ground = newGlobal("gnd");
    /** global signal power. */
    public static final Global power = newGlobal("vdd");

    /**
     * Constructs Global signal with given name key.
     * @param name name key
     */
    private Global(Name name) {
        this.name = name;
        index = allGlobals.length;
        Global[] newGlobals = new Global[index + 1];
        for (int i = 0; i < index; i++) {
            newGlobals[i] = allGlobals[i];
        }
        newGlobals[index] = this;
        allGlobals = newGlobals;
        globalsByName.put(name.toString(), this);
        String canonicString = TextUtils.canonicString(name.toString());
        if (globalsByCanonicName.containsKey(canonicString)) {
            System.out.println("!!! " + globalsByCanonicName.get(canonicString) + " and " + this + " are not connected now !!!");
        }
        globalsByCanonicName.put(canonicString, this);
    }

    /**
     * Returns name of this Global signal.
     * @return name of this Global signal.
     */
    public String getName() {
        return name.toString();
    }

    /**
     * Returns name key of this Global signal.
     * @return name key of this Global signal.
     */
    public Name getNameKey() {
        return name;
    }

    /**
     * Returns a printable version of this Global.
     * @return a printable version of this Global.
     */
    public String toString() {
        return "Global " + name;
    }

    /**
     * Finds or creates global signal with given name.
     * @param name name
     * @return Global signal with given name.
     */
    public static Global newGlobal(String name) {
        Name nameKey = Name.findName(name);
        Global g = globalsByName.get(nameKey.toString());
//		Global g = globalsByName.get(nameKey.canonicString());
        if (g == null) {
            g = new Global(nameKey);
        }
        return g;
    }

    /**
     * A Global.Set defines a set of Global signals.
     */
    public static class Set implements Comparable<Set> {

        /** bitmap which defines set. */
        private PortCharacteristic[] elemMap;
        /** map localInd->globalInd. */
        private Global[] elems;
        /** map globalInd->localInd. */
        private int[] indexOf;
        /** Map Set->Set of all Global.Sets. */
        private static Map<Set, Set> allSets = new TreeMap<Set, Set>();
        /** A private set for search in allSets. */
        private static Global.Set fakeSet = new Global.Set(new PortCharacteristic[0]);
        /** A private set for search in allSets. */
        public static final Global.Set empty = newSet(new PortCharacteristic[0]);

        /**
         * Constructs Global.Set with specified bitmap of Globals.
         * @param elemMap bitmap which specifies which elemenst are in set.
         */
        private Set(PortCharacteristic[] elemMap) {
            int maxElem = -1;
            int numElem = 0;

            for (int i = 0; i < elemMap.length; i++) {
                if (elemMap[i] == null) {
                    continue;
                }
                maxElem = i;
                numElem++;
            }

            this.elemMap = new PortCharacteristic[maxElem + 1];
            elems = new Global[numElem];
            indexOf = new int[maxElem + 1];
            Arrays.fill(indexOf, -1);
//			List elemList = new ArrayList(numElem);

            int local = 0;
            for (int i = 0; i < elemMap.length; i++) {
                if (elemMap[i] == null) {
                    continue;
                }
                this.elemMap[i] = elemMap[i];
                elems[local] = allGlobals[i];
                indexOf[i] = local;
                local++;
            }
        }

        /**
         * Returns <tt>true</tt> if this Global.Set contains a specified Global.
         * @param global The Global whose presence in this set is to be tested
         * @return <tt>true</tt> if this Global Set contains a specified Global.
         */
        public final boolean contains(Global global) {
            return global.index < elemMap.length && elemMap[global.index] != null;
        }

        /**
         * Returns characteristic of a specified Global in a set.
         * Returns null if a specified Global not in a set..
         * @param global The Global whose presence in this set is to be tested
         * @return Characteristic of specified Global or null.
         */
        public PortCharacteristic getCharacteristic(Global global) {
            return global.index < elemMap.length ? elemMap[global.index] : null;
        }

        /**
         * Returns the number of Globals in this Global.Set.
         * @return the number of Globals in this Global.Set.
         */
        public final int size() {
            return elems.length;
        }

        /**
         * Returns a Global from this set by specified index.
         * @param i specified index of global.
         * @return a Global from this set by specified index.
         */
        public final Global get(int i) {
            return elems[i];
        }

        /**
         * Returns an index of specified Global in this set.
         * Returns -1 if Global is not in set.
         * @param g specified global.
         * @return a Global from this set by specified index.
         */
        public final int indexOf(Global g) {
            return g.index < indexOf.length ? indexOf[g.index] : -1;
        }

        /**
         * Returns the maximal index of Globals in this Set.
         * @return the maximal index of Globals in this Set or (-1) for empty Set.
         */
        public final int maxElement() {
            return elemMap.length - 1;
        }

        /**
         * Compares this Global.Set with the specified Global.Set for order.  Returns a
         * negative integer, zero, or a positive integer as this object is less
         * than, equal to, or greater than the specified object.<p>
         * @param   set the Global.Set to be compared.
         * @return  a negative integer, zero, or a positive integer as this object
         *		is less than, equal to, or greater than the specified object.
         */
        public int compareTo(Global.Set set) {
            int minLen = Math.min(this.elemMap.length, set.elemMap.length);
            int i;
            for (i = 0; i < minLen; i++) {
                if (this.elemMap[i] != set.elemMap[i]) {
                    break;
                }
            }
            if (i < minLen) {
                if (this.elemMap[i] == null) {
                    return -1;
                }
                if (set.elemMap[i] == null) {
                    return 1;
                }
                return this.elemMap[i].getOrder() - set.elemMap[i].getOrder();
            }
            for (; i < this.elemMap.length; i++) {
                if (this.elemMap[i] != null) {
                    return 1;
                }
            }
            for (; i < set.elemMap.length; i++) {
                if (set.elemMap[i] != null) {
                    return -1;
                }
            }
            return 0;
        }

        /**
         * Compares this Global.set with the specified object for order.  Returns a
         * negative integer, zero, or a positive integer as this object is less
         * than, equal to, or greater than the specified object.<p>
         * @param   o the Object to be compared.
         * @return  a negative integer, zero, or a positive integer as this object
         *		is less than, equal to, or greater than the specified object.
         *
         * @throws ClassCastException if the specified object's type prevents it
         *         from being compared to this Object.
         */
//4*/	public int compareTo(Object o)
//4*/	{
//4*/		return compareTo((Global.Set)o);
//4*/	}
        /**
         * Returns a printable version of this GlobalSet.
         * @return a printable version of this GlobalSet.
         */
        public String toString() {
            String s = "Global.Set {";
// 			for (int i = 0; i < size(); i++) {
// 				Global g = elems[i];
// 				s += " " + g + ":" + elemMap[g.index].getName();
// 			}
            for (int i = 0; i < elemMap.length; i++) {
                if (elemMap[i] == null) {
                    continue;
                }
                s += " " + allGlobals[i] + ":" + elemMap[i].getName();
            }
            s += "}";
            return s;
        }

        /**
         * Returns this Set with removed all globals given by the iterator.
         * @param gs iterator with globals
         * @return Set with removed globals.
         */
        public Global.Set remove(Iterator<Global> gs) {
            Buf buf = new Buf(this);
            for (; gs.hasNext();) {
                Global g = gs.next();
                if (g.index < buf.buf.length) {
                    buf.buf[g.index] = null;
                }
            }
            return buf.getBuf();
        }

        /**
         * Finds or creates Global.Set with specified bitmap of Globals.
         * @param elemMap bitmap which specifies which elemenst are in set.
         * @return set of specified Globals.
         */
        private static Global.Set newSet(PortCharacteristic[] elemMap) {
            fakeSet.elemMap = elemMap;
            Global.Set set = allSets.get(fakeSet);
            fakeSet.elemMap = null;
            if (set == null) {
                set = new Global.Set(elemMap);
                allSets.put(set, set);
            }
            return set;
        }
    }

    /**
     * Class for constructing sets of Global.
     */
    public static class Buf {

        /** Buffer for construction Global.Set. */
        private PortCharacteristic[] buf = new PortCharacteristic[allGlobals.length];

        /**
         * Constructs buffer.
         */
        public Buf() {
        }

        /**
         * Constructs buffer from Set.
         * @param set set of globals.
         */
        Buf(Global.Set set) {
            buf = set.elemMap.clone();
        }

        /**
         * Add specified Global to the buffer.
         * @param g specified Global.
         */
        public String addToBuf(Global g, PortCharacteristic characteristic) {
            String errorMsg = null;
            if (buf.length <= g.index) {
                PortCharacteristic[] newBuf = new PortCharacteristic[g.index + 1];
                System.arraycopy(buf, 0, newBuf, 0, buf.length);
                buf = newBuf;
            }
            PortCharacteristic oldCharacteristic = buf[g.index];
            if (oldCharacteristic == null) {
                buf[g.index] = characteristic;
            } else if (oldCharacteristic != characteristic) {
                errorMsg = g.getName() + "(" + oldCharacteristic.getName() + "->" + characteristic.getName() + ")";
            }
            return errorMsg;
        }

        /**
         * Add specified Global.Set to the buffer.
         * @param set specified Global.Set.
         */
        public String addToBuf(Global.Set set) {
            String errorMsg = null;
            for (int i = 0; i < set.elems.length; i++) {
                int index = set.elems[i].index;
                String msg = addToBuf(allGlobals[index], set.elemMap[index]);
                if (msg != null) {
                    if (errorMsg != null) {
                        errorMsg += " ";
                    }
                    errorMsg += msg;
                }
            }
            return errorMsg;
        }

        /**
         * Returns Global.Set of element in the buffer.
         * @return set of Globals in the buffer.
         */
        public Global.Set getBuf() {
            return Global.Set.newSet(buf);
        }
    }
}
