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

import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.hierarchy.Nodable;
import com.sun.electric.database.id.PortProtoId;
import com.sun.electric.database.network.Global;
import com.sun.electric.database.network.Netlist;
import com.sun.electric.database.network.Network;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.Geometric;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.tool.Job;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.math.FixpTransform;

import java.awt.geom.Rectangle2D;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/** The HierarchyEnumerator can help programs that need to "flatten"
 * the design hierarchy. Examples of such programs include the logical
 * effort engine and routers.
 *
 * <p>The HierarchyEnumerator performs a recursive descent of
 * the "completely expanded" design hierarchy. The HierarchyEnumerator
 * brings the Visitor along with it during the excursion.
 * The HierarchyEnumerator doesn't build a flattened data structure,
 * that's the prerogative of the Visitor. The HierarchyEnumerator simply
 * invokes Visitor methods for each Cell instance and NodeInst.
 *
 * <p>The following example illustrates the notion of "completely
 * expanded". Suppose the root Cell instantiates Cell A twice, and
 * Cell A instantiates Cell B twice. Then the HierarchyEnumerator
 * visits two instances of Cell A and four instances of Cell B.
 */
public final class ImmutableLayoutHierarchyEnumerator {

//    /** Stores the information necessary to generate an instance name for a Part
//     * It is sometimes important not to store the instance name as a String.
//     * When I stored instance names as strings in NCC profiles indicated that
//     * almost 50% of the storage space was used in these strings and 70% of the
//     * execution time was spent generating these Strings!!! */
//    public static abstract class NameProxy implements Serializable {
//
//        private VarContext context;
//        private String sep;
//
//        private String makePath(VarContext context, String sep) {
//            String path = context.getInstPath(sep);
//            if (!path.equals("")) {
//                path += sep;
//            }
//            return path;
//        }
//
//        protected NameProxy(VarContext context, String sep) {
//            this.context = context;
//            this.sep = sep;
//        }
//
//        abstract public String leafName();
//
//        abstract public Cell leafCell();
//
//        public VarContext getContext() {
//            return context;
//        }
//
//        public String toString() {
//            return makePath(context, sep) + leafName();
//        }
//
//        public String toString(int numRemoveParents) {
//            VarContext localContext = context.removeParentContext(numRemoveParents);
//            String ret = makePath(localContext, sep) + leafName();
//            return ret;
//        }
//    }
//
//    public static class NetNameProxy extends NameProxy {
//
//        static final long serialVersionUID = 0;
//        private Network net;
//
//        public String leafName() {
//            Iterator<String> it = net.getNames();
//            if (it.hasNext()) {
//                return it.next();
//            }
//            return "netIndex" + net.getNetIndex();
//        }
//
//        public Iterator<String> leafNames() {
//            return net.getNames();
//        }
//
//        public NetNameProxy(VarContext context, String sep, Network net) {
//            super(context, sep);
//            this.net = net;
//        }
//
//        public Cell leafCell() {
//            return net.getParent();
//        }
//
//        public Network getNet() {
//            return net;
//        }
//    }
//
//    public static class NodableNameProxy extends NameProxy {
//
//        static final long serialVersionUID = 0;
//        private Nodable nodable;
//
//        public String leafName() {
//            return nodable.getName();
//        }
//
//        public NodableNameProxy(VarContext context, String sep, Nodable node) {
//            super(context, sep);
//            this.nodable = node;
//        }
//
//        public Cell leafCell() {
//            return nodable.getParent();
//        }
//
//        public Nodable getNodable() {
//            return nodable;
//        }
//    }
    // --------------------- private data ------------------------------
    private Visitor visitor;
    private boolean caching;
    private int curNetId = 0;
//    private int cellCnt = 0; // For statistics
//    private int instCnt = 0; // For statistics
    private Map<Integer, NetDescription> netIdToNetDesc =
            new HashMap<Integer, NetDescription>();
    private HashMap<Cell, int[]> cellExternalIds = new HashMap<Cell, int[]>();

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

    // Prevent anyone from instantiating HierarchyEnumerator.
    private ImmutableLayoutHierarchyEnumerator() {
    }

    private int[] getExternalIds(Cell cell, Netlist netlist) {
        int[] externalIds = cellExternalIds.get(cell);
        if (externalIds != null) {
            return externalIds;
        }

        externalIds = new int[netlist.getNumExternalNetworks()];
        cellExternalIds.put(cell, externalIds);
        return externalIds;
    }

    //private int nextNetID() { return netIdToNetDesc.size(); }
    private int[] numberNets(Cell cell, Netlist netlist,
            int[][] portNdxToNetIDs, CellInfo info) {
        int numNets = netlist.getNumNetworks();
        int[] externalIds = getExternalIds(cell, netlist);
        int[] netNdxToNetID = new int[numNets];
        int baseId = curNetId;
        Arrays.fill(externalIds, -1);
        if (portNdxToNetIDs != null) {
            assert portNdxToNetIDs.length == cell.getNumPorts() + 1;
            Global.Set globals = netlist.getGlobals();
            assert portNdxToNetIDs[0].length == globals.size();
            for (int i = 0; i < globals.size(); i++) {
                Global global = globals.get(i);
                int netIndex = netlist.getNetwork(global).getNetIndex();
                externalIds[netIndex] = portNdxToNetIDs[0][i];
            }
            for (int i = 0, numPorts = cell.getNumPorts(); i < numPorts; i++) {
                Export export = cell.getPort(i);
                int[] ids = portNdxToNetIDs[i + 1];
                assert ids.length == export.getNameKey().busWidth();
                for (int j = 0; j < ids.length; j++) {
                    int netIndex = netlist.getNetwork(export, j).getNetIndex();
                    externalIds[netIndex] = ids[j];
                }
            }
            for (int i = 0; i < externalIds.length; i++) {
                assert externalIds[i] >= 0;
            }
            baseId -= externalIds.length;
        }
        for (int i = 0; i < numNets; i++) {
            Network net = netlist.getNetwork(i);
            int localId = i;
            assert baseId + localId <= curNetId;
            if (baseId + localId == curNetId) {
                if (portNdxToNetIDs == null && localId < externalIds.length) {
                    externalIds[localId] = localId;
                }
                assert curNetId == baseId + localId;
                netIdToNetDesc.put(curNetId++, new NetDescription(net, info));
            } else if (localId >= externalIds.length || portNdxToNetIDs == null) {
                NetDescription nd = netIdToNetDesc.get(baseId + localId);
                int cmp = !net.isUsernamed() ? 1 : nd.net.isUsernamed() ? 0 : -1;
                if (cmp == 0 && net.isExported() != nd.net.isExported()) {
                    cmp = net.isExported() ? -1 : 1;
                }
                if (cmp == 0) {
                    cmp = TextUtils.STRING_NUMBER_ORDER.compare(net.getName(), nd.net.getName());
                }
                if (cmp < 0) {
                    nd.net = net;
                }
            }
            int id = localId < externalIds.length ? externalIds[localId] : baseId + localId;
            netNdxToNetID[i] = id;
        }
        return netNdxToNetID;
    }

    private static int[] getGlobalNetIDs(Nodable no, Netlist netlist, int[] netNdxToNetID) {
        Global.Set gs = netlist.getNetlist(no).getGlobals();
        int[] netIDs = new int[gs.size()];
        for (int i = 0; i < gs.size(); i++) {
            int netIndex = netlist.getNetwork(no, gs.get(i)).getNetIndex();
            int netID = netNdxToNetID[netIndex];
            error(netID < 0, "no netID for net");
            netIDs[i] = netID;
        }
        return netIDs;
    }

    private static int[] getPortNetIDs(Nodable no, PortProto pp,
            Netlist netlist, int[] netNdxToNetID) {
        int busWidth = pp.getNameKey().busWidth();
        int[] netIDs = new int[busWidth];
        for (int j = 0; j < busWidth; j++) {
            Network net = netlist.getNetwork(no, pp, j);
            error(net == null, "no network for net " + pp.getNameKey());
            int netIndex = net.getNetIndex();
            int netID = netNdxToNetID[netIndex];
            error(netID < 0, "no netID for net " + pp.getNameKey());
            netIDs[j] = netID;
        }
        return netIDs;
    }

    private static int[][] buildPortMap(Netlist netlist, Nodable ni,
            int[] netNdxToNetID) {
        Cell cell = (Cell) ni.getProto();
        int numPorts = cell.getNumPorts();
        int[][] portNdxToNetIDs = new int[numPorts + 1][];
        portNdxToNetIDs[0] = getGlobalNetIDs(ni, netlist, netNdxToNetID);
        for (int i = 0; i < numPorts; i++) {
            PortProto pp = cell.getPort(i);
            portNdxToNetIDs[i + 1] = getPortNetIDs(ni, pp, netlist, netNdxToNetID);
        }
        return portNdxToNetIDs;
    }

    /** portNdxToNetIDs translates an Export's index to an array of NetIDs */
    private void enumerateCell(Nodable parentInst, Cell cell,
            VarContext context, Netlist netlist,
            int[][] portNdxToNetIDs,
            FixpTransform xformToRoot, CellInfo parent, Rectangle2D bounds) {
        CellInfo info = visitor.newCellInfo();

        int firstNetID = curNetId;
        int[] netNdxToNetID = numberNets(cell, netlist, portNdxToNetIDs, info);
        int lastNetIDPlusOne = curNetId;
//        cellCnt++;
        info.init(parentInst, cell, context, netlist, netNdxToNetID,
                portNdxToNetIDs, xformToRoot, netIdToNetDesc, parent);

        boolean enumInsts = visitor.enterCell(info);
        if (!enumInsts) {
            return;
        }

        if (bounds != null) {
            for (Iterator<Geometric> it = cell.searchIterator(bounds); it.hasNext();) {
                Geometric geom = it.next();
                if (!(geom instanceof NodeInst)) {
                    continue;
                }
                NodeInst ni = (NodeInst) geom;
                visitThisNode(ni, context, netlist, info, netNdxToNetID, xformToRoot);
            }
        } else {
            for (Iterator<Nodable> it = netlist.getNodables(); it.hasNext();) {
                Nodable ni = it.next();
                visitThisNode(ni, context, netlist, info, netNdxToNetID, xformToRoot);
            }
        }

        visitor.exitCell(info);

        // release storage associated with VarContext variable cache
        context.deleteVariableCache();

        // remove entries in netIdToNetDesc that we'll never use again
        for (int i = firstNetID; i < lastNetIDPlusOne; i++) {
            netIdToNetDesc.remove(i);
        }
    }

    private void visitThisNode(Nodable ni, VarContext context, Netlist netlist, CellInfo info,
            int[] netNdxToNetID, FixpTransform xformToRoot) {
//        instCnt++;
        boolean descend = visitor.visitNodeInst(ni, info);
        NodeProto np = ni.getProto();
        if (descend && ni.isCellInstance() && !((Cell) np).isIcon()) {
            int[][] portNmToNetIDs2 = buildPortMap(netlist, ni, netNdxToNetID);
            FixpTransform xformToRoot2 = xformToRoot;
            if (ni instanceof NodeInst) {
                // add transformation from lower level
                xformToRoot2 = new FixpTransform(xformToRoot);
                xformToRoot2.concatenate(((NodeInst) ni).rotateOut());
                xformToRoot2.concatenate(((NodeInst) ni).translateOut());
            }
            enumerateCell(ni, (Cell) np,
                    caching ? context.pushCaching(ni) : context.push(ni),
                    netlist.getNetlist(ni),
                    portNmToNetIDs2, xformToRoot2, info, null);
        }
    }

    //  Set up everything for the root cell and then initiate the
    //  hierarchical traversal.
    private void doIt(Cell root, VarContext context, Netlist netlist,
            Visitor visitor, boolean cache, Rectangle2D bounds) {
        this.visitor = visitor;
        this.caching = cache;
        if (context == null) {
            context = VarContext.globalContext;
        }
        int[][] exportNdxToNetIDs = null;
        enumerateCell(null, root, context, netlist, exportNdxToNetIDs,
                new FixpTransform(), null, bounds);

//		System.out.println("A total of: " + curNetId + " nets were numbered");
//		System.out.println("A total of: " + cellCnt + " Cells were visited");
//		System.out.println("A total of: " + instCnt + " NodeInsts were visited");
    }

    // ------------------------ public types ---------------------------
    /** Perform useful work while the HierarchyEnumerator enumerates
     * the design. Whereas the HierarchyEnumerator is responsible for
     * enumerating every Cell and NodeInst in the flattened design,
     * the Visitor object is responsible for performing useful work
     * during the enumeration.
     *
     * <p>The HierarchyEnumerator performs a recursive descent of the
     * design hierarchy starting with the root Cell. When the
     * HierarchyEnumerator enters a Cell instance it calls the Visitor's
     * enterCell() method to let the Visitor know that it's just started
     * working on a new Cell instance. Then the HierarchyEnumerator calls
     * the Visitor's visitNodeInst() method for each NodeInst in that
     * Cell. Finally, after all the NodeInsts have been visited, the
     * HierarchyEnumerator calls the Visitor's exitCell() method to
     * inform the Visitor that the HierarchyEnumerator is done with that
     * Cell.
     *
     * <p>The Visitor's visitNodeInst() method controls whether the
     * HierarchyEnumerator descends recursively into the Cell
     * instantiated by that NodeInst. If the visitNodeInst() method
     * returns true, then the HierarchyEnumerator enumerates the contents
     * of that NodeInst's child Cell before it continues enumerating the
     * NodeInsts of the current Cell.
     */
    public static abstract class Visitor {

        /** A hook to allow the user to add additional information to
         * a CellInfo. The newCellInfo method is a "Factory"
         * method. If the user wishes to record additional application
         * specific information for each Cell, the user should extend
         * the CellInfo class and then override newCellInfo to return
         * an instance of that derived class. */
        public CellInfo newCellInfo() {
            return new CellInfo();
        }

        /** The HierarchyEnumerator is about to begin enumerating the
         * contents of a new Cell instance. That instance has just
         * become the new "current" Cell instance.
         * @param info information about the Cell instance being
         * enumerated
         * @return a boolean indicating if the HierarchyEnumerator
         * should enumerate the contents of the current Cell. True
         * means enumerate the current cell */
        public abstract boolean enterCell(CellInfo info);

        /** The HierarchyEnumerator has finished enumerating the
         * contents of the current Cell instance. It is about to leave
         * it, never to return.  The CellInfo associated with the
         * current Cell instance is about to be abandoned.
         * @param info information about the Cell instance being
         * enumerated */
        public abstract void exitCell(CellInfo info);

        /** The HierarchyEnumerator is visiting Nodable ni.
         * @param ni the Nodable that HierarchyEnumerator is visiting.
         * @return a boolean indicating whether or not the
         * HierarchyEnumerator should expand the Cell instantiated by
         * ni. True means expand. If ni instantiates a PrimitiveNode
         * then the return value is ignored by the
         * HierarchyEnumerator. */
        public abstract boolean visitNodeInst(Nodable ni, CellInfo info);
        /** Using visitNodeInst implements a Top-Down traversal. If one
         * wishes to implement Bottom-Up traversal, use this method instead,
         * or in conjunction with visitNodeInst. */
        //public abstract void visitNodeInstBottomUp(Nodable ni, CellInfo info);
    }

    /** The NetDescription object provides a Network and the level of
     * hierarchy in which the Network occurs. The visitor can use
     * NetDescription to formulate, for example, the name of
     * the net */
    public static class NetDescription {

        private Network net;
        private CellInfo info;

        NetDescription(Network net, CellInfo info) {
            this.net = net;
            this.info = info;
        }

        public Network getNet() {
            return net;
        }

        public CellInfo getCellInfo() {
            return info;
        }
    }

    /** The CellInfo object is used to pass information to the Visitor
     * during the enumeration. The CellInfo object contains many methods
     * that describe the Cell currently being enumerated and the state
     * of the enumeration.
     *
     * <p>The HierarchyEnumerator creates a new CellInfo for a Cell
     * instance just before it begins enumerating the contents of that
     * Cell instance.  The HierarchyEnumerator abandons the CellInfo once
     * it is done enumerating the contents of that Cell instance. Once
     * the CellInfo is abandoned the garbage collector may reclaim the
     * CellInfo's storage.
     *
     * <p>Each CellInfo has a reference to the CellInfo of the parent of
     * the current Cell instance. Thus the Visitor is able to get
     * information about all the ancestors of the current Cell
     * instance.
     *
     * <p>In most cases, the user will need to store additional
     * information in the CellInfo. In those cases the user should
     * extend the CellInfo class and override the Visitor.newCellInfo()
     * method to return an instance of the derived class.
     */
    public static class CellInfo {

        private Nodable parentInst;
        private Cell cell;
//        private CellTree cellTree;
        private VarContext context;
        private Netlist netlist;
        private int[] netNdxToNetID;
        private int[][] exportNdxToNetIDs;
        private FixpTransform xformToRoot;
//        private Map<Integer, NetDescription> netIdToNetDesc;
        private CellInfo parentInfo;

        // package private
        void init(Nodable parentInst, Cell cell, VarContext context, Netlist netlist,
                int[] netToNetID, int[][] exportNdxToNetIDs,
                FixpTransform xformToRoot, Map<Integer, NetDescription> netIdToNetDesc,
                CellInfo parentInfo) {
            this.parentInst = parentInst;
            this.cell = cell;
            /* this.cellTree = */ cell.tree();
            this.context = context;
            this.netlist = netlist;
            this.netNdxToNetID = netToNetID;
            this.exportNdxToNetIDs = exportNdxToNetIDs;
            this.xformToRoot = xformToRoot;
//            this.netIdToNetDesc = netIdToNetDesc;
            this.parentInfo = parentInfo;
        }

        /** The Cell currently being visited. */
        public final Cell getCell() {
            return cell;
        }

        /** The Cell that is the root of the traversal */
        public final boolean isRootCell() {
            return parentInfo == null;
        }

        /** The VarContext to use for evaluating all variables in the
         * current Cell. */
        public final VarContext getContext() {
            return context;
        }

//        /** The Netlist of the current Cell. */
//        public final Netlist getNetlist() {
//            return netlist;
//        }
        /** Get the CellInfo for the current Cell's parent.  If the
         * current Cell is the root then return null. */
        public final CellInfo getParentInfo() {
            return parentInfo;
        }

        /** Get the NodeInst that instantiates the Current
         * instance. If the current Cell is the root then return
         * null. */
        public final Nodable getParentInst() {
            return parentInst;
        }

        /** Get the CellInfo for the root Cell */
        public final CellInfo getRootInfo() {
            CellInfo i = this;
            while (i.getParentInfo() != null) {
                i = i.getParentInfo();
            }
            return i;
        }

        /** Get netIDs for the Export: e.
         * @return an array of net numbers. */
        public final int[] getExportNetIDs(Export e) {
            if (isRootCell()) {
                // exportNdxToNetIDs is invalid for the root Cell because
                // no mapping from net index to netID is performed for Exports
                // of the root Cell.
                int width = netlist.getBusWidth(e);
                int[] netIDs = new int[width];
                for (int i = 0; i < width; i++) {
                    netIDs[i] = netlist.getNetwork(e, i).getNetIndex();
                }
                return netIDs;
            }
            return exportNdxToNetIDs[e.getPortIndex() + 1];
        }

//        /** Map any net inside the current cell to a net
//         * number. During the course of the traversal, all nets that
//         * map to the same net number are connected. Nets that map to
//         * different net numbers are disconnected.
//         *
//         * <p>If you want to generate a unique name for the net use
//         * getUniqueNetName().
//         */
//        public final int getNetID(Network net) {
//            return getNetID(net.getNetIndex());
//        }
        /**
         * Map any arc inside the current cell to a net
         * number. During the course of the traversal, all nets that
         * map to the same net number are connected. Nets that map to
         * different net numbers are disconnected.
         *
         * <p>If you want to generate a unique name for the net use
         * getUniqueNetName().
         */
        public final int getNetID(ArcInst ai) {
            Network net = netlist.getNetwork(ai, 0);
            return net != null ? getNetID(net.getNetIndex()) : -1;
        }

        /**
         * Map any port instance inside the current cell to a net
         * number. During the course of the traversal, all nets that
         * map to the same net number are connected. Nets that map to
         * different net numbers are disconnected.
         *
         * <p>If you want to generate a unique name for the net use
         * getUniqueNetName().
         */
        public final int getNetID(NodeInst ni, PortProto p) {
            Network net = netlist.getNetwork(parentInst, p, 0);
            return net != null ? getNetID(net.getNetIndex()) : -1;
        }

        /** Map a net index from the current cell to a net ID.
         * number. During the course of the traversal, all nets that
         * map to the same net number are connected. Nets that map to
         * different net numbers are disconnected.
         * @param netIndex net index within current cell
         * @return net ID that is unique over entire net list.
         */
        private int getNetID(int netIndex) {
            return netNdxToNetID[netIndex];
        }

//		public final boolean isGlobalNet(int netID) {
//			error(netID<0, "negative netIDs are illegal");
//			return netID <= largestGlobalNetID;
//		}
        /**
         * Get the set of netIDs that are connected to the specified port of
         * the specified Nodable.
         */
        public final int[] getPortNetIDs(Nodable no, PortProto pp) {
            return ImmutableLayoutHierarchyEnumerator.getPortNetIDs(no, pp, netlist,
                    netNdxToNetID);
        }

//        /** Get a unique, flat net name for the network.  The network
//         * name will contain the hierarchical context as returned by
//         * VarContext.getInstPath() if it is not a top-level network.
//         * @param sep the context separator to use if needed.
//         * @return a unique String identifier for the network */
//        public final String getUniqueNetName(Network net, String sep) {
//            NameProxy proxy = getUniqueNetNameProxy(net, sep);
//            return proxy.toString();
//        }
//
//        /** Same as getUniqueNetName except it returns a NameProxy instead of a
//         * String name */
//        public final NetNameProxy getUniqueNetNameProxy(Network net, String sep) {
//            return getUniqueNetNameProxy(getNetID(net), sep);
//        }
//
//        /** Get a unique, flat net name for the network.  The network
//         * name will contain the hierarchical context as returned by
//         * VarContext.getInstPath() if it is not a top-level network.
//         * @param sep the hierarchy separator to use if needed.
//         * @return a unique String identifier for the network */
//        public final String getUniqueNetName(int netID, String sep) {
//            NameProxy proxy = getUniqueNetNameProxy(netID, sep);
//            return proxy.toString();
//        }
//
//        /** Same as getUniqueNetName except it returns a NameProxy instead of a
//         * String name */
//        public final NetNameProxy getUniqueNetNameProxy(int netID, String sep) {
//            NetDescription ns = netIdToNetDesc.get(netID);
//            VarContext netContext = ns.getCellInfo().getContext();
//            return new NetNameProxy(netContext, sep, ns.getNet());
//        }
//
//        /** Get a unique, flat instance name for the Nodable.
//         * @param no
//         * @param sep the hierarchy separator to use if needed
//         * @return a unique String identifer for the Nodable */
//        public final String getUniqueNodableName(Nodable no, String sep) {
//            return getUniqueNodableNameProxy(no, sep).toString();
//        }
//
//        /** Same as getUniqueNodableName except that it returns a NameProxy
//         *  instead of a String name. */
//        public final NodableNameProxy getUniqueNodableNameProxy(Nodable no, String sep) {
//            return new NodableNameProxy(getContext(), sep, no);
//        }
//
//        /** Get the Network that is closest to the root in the design
//         * hierarchy that corresponds to netID. */
//        public final NetDescription netIdToNetDescription(int netID) {
//            return netIdToNetDesc.get(netID);
//        }
//
//        /**
//         * Get the Network in the parent that connects to the specified
//         * Network in this cell.  Returns null if no network in parent connects
//         * to this network (i.e. network is not connected to export), or null
//         * if no parent.
//         * @param network the network in this cell
//         * @return the network in the parent that connects to the
//         * specified network, or null if no such network.
//         */
//        public Network getNetworkInParent(Network network) {
//            if (parentInfo == null) {
//                return null;
//            }
//            if (network == null) {
//                return null;
//            }
//            if (network.getNetlist() != netlist) {
//                return null;
//            }
//            return parentInfo.getNetlist().getNetwork(context.getNodable(), network);
//        }
        /**
         * Method to get the transformation from the current location to the root.
         * If this is at the top cell, the transformation is identity.
         * @return the transformation from the current location to the root.
         */
        public FixpTransform getTransformToRoot() {
            return xformToRoot;
        }
    }

    // ----------------------- public methods --------------------------
    /** Begin enumeration of the contents of the Cell root.  You MUST
     * call rebuildNetworks() on the root Cell before calling
     * beginEnumeration().
     * @param root the starting point of the enumeration.
     * @param context the VarContext for evaluating parameters in Cell
     * root. If context is null then VarContext.globalContext is used.
     * @param visitor the object responsible for doing something useful
     * during the enumertion of the design hierarchy. */
    public static void enumerateCell(Cell root, VarContext context, Visitor visitor) {
        enumerateCell(root, context, visitor, Netlist.ShortResistors.NO);
    }

    public static void enumerateCell(Cell root, VarContext context, Visitor visitor, Rectangle2D bounds) {
        (new ImmutableLayoutHierarchyEnumerator()).doIt(root, context, root.getNetlist(Netlist.ShortResistors.NO), visitor, false, bounds);
    }

    public static void enumerateCell(Cell root, VarContext context, Visitor visitor, Netlist.ShortResistors shortResistors) {
        enumerateCell(root.getNetlist(shortResistors), context, visitor);
    }

    public static void enumerateCell(Netlist rootNetlist, VarContext context, Visitor visitor) {
        enumerateCell(rootNetlist, context, visitor, false);
    }

    /** Experimental. Optionally caches results of variable evaluation. */
    public static void enumerateCell(Netlist rootNetlist, VarContext context, Visitor visitor, boolean caching) {
        /* Netlist.ShortResistors shortResistors = */ rootNetlist.getShortResistors();
        (new ImmutableLayoutHierarchyEnumerator()).doIt(rootNetlist.getCell(), context, rootNetlist, visitor, caching, null);
    }

//    private static class ImmutableNetlist {
//
//        private final ImmutableNetLayout impl;
//        private final int[] nm_net;
//
//        private ImmutableNetlist(ImmutableNetLayout impl, Netlist.ShortResistors shortResisors) {
//            this.impl = impl;
//            nm_net = new int[impl.netMap.length];
//        }
//
//        private int getNetIndex(ImmutableArcInst a) {
//            return -1;
//        }
//
//        private int getNetIndex(ImmutableNodeInst n, PortProtoId pp) {
//            return -1;
//        }
//    }
}
