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

import com.sun.electric.database.ImmutableExport;
import com.sun.electric.database.ImmutableNodeInst;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.EDatabase;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.id.CellId;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.technology.PrimitivePort;
import com.sun.electric.util.math.FixpTransform;
import com.sun.electric.util.math.Orientation;

/**
 * The PortOriginal helper class descends the hierarchy from an Export to the bottommost PrimitivePort
 * on a primitive NodeInst.
 * <P>
 * Many parts of the system need to know what is "under" an Export.
 * For example, assume that cell BOT has a contact node that is exported.
 * Further assume that cell MID has an instance of cell BOT, and that the
 * port on BOT is further exported.
 * Finally, assume that cell TOP has an instance of cell MID.
 * To find out information about the port on MID, it is necessary to descend
 * the hierarchy into MID and into BOT.
 * <P>
 * This class takes a PortInst or a NodeInst/PortProto pair (such as the port on MID in cell TOP).
 * It then provides these pieces of information:
 * <UL>
 * <LI>The bottommost PortInst (in this case, the port on the contact node in cell BOT).</LI>
 * <LI>The bottommost NodeInst (in this case, the contact node in cell BOT).</LI>
 * <LI>The bottommost PortProto (in this case, the port on the contact node in cell BOT).</LI>
 * <LI>The transformation to the top.  In this case, a transformation that:
 *     (1) accounts for rotation of the contact node;
 *     (2) accounts for translation between cell BOT and cell MID;
 *     (3) accounts for rotation on the instance of cell BOT in cell MID;
 *     (4) accounts for translation between cell MID and cell TOP; and
 *     (5) accounts for rotation of the instance of cell MID in cell TOP.
 * Thus, it transforms from coordinates on the contact node to the coordinate space of cell TOP.</LI>
 * <LI>The apparent angle of the lowest node when viewed from the top.
 * In this case, it is a combination of the contact, BOT cell instance, and MID cell instance rotations.</LI>
 * </UL>
 */
public class PortOriginal {

    private FixpTransform subrot;
    private Orientation orient;
    private Cell bottomCell;
    private ImmutableNodeInst bottomNode;
    private PortInst bottomPort;
    private NodeInst bottomNi;
    private PrimitivePort bottomPp;

    /**
     * Constructor takes a PortInst and traverses it down to the bottom of the hierarchy.
     * @param startPort the initial PortInst.
     */
    public PortOriginal(PortInst startPort) {
        bottomPort = startPort;
//        subrot = bottomNi.rotateOut();
        traverse(startPort.getNodeInst(), startPort.getPortProto(), null);
    }

    /**
     * Constructor takes a PortInst and traverses it down to the bottom of the hierarchy.
     * Also takes a transformation matrix to include in the final computation.
     * @param startPort the initial PortInst.
     * @param pre the transformation matrix to add to the final transformation.
     */
    public PortOriginal(PortInst startPort, FixpTransform pre) {
        bottomPort = startPort;
        traverse(startPort.getNodeInst(), startPort.getPortProto(), pre);
    }

    /**
     * Constructor takes a NodeInst/PortProto combination and traverses it down to the bottom of the hierarchy.
     * @param ni the initial NodeInst.
     * @param pp the initial PortProto.
     */
    public PortOriginal(NodeInst ni, PortProto pp) {
        traverse(ni, pp, null);
    }

    private void traverse(NodeInst ni, PortProto pp, FixpTransform pre) {
        bottomCell = ni.getParent();
        bottomNode = ni.getD();
        bottomNi = ni;
        EDatabase database;
        if (bottomCell != null) {
            database = bottomCell.getDatabase();
        } else if (ni.isCellInstance()) {
            database = ((Cell) ni.getProto()).getDatabase();
        } else {
            database = null;
        }
        orient = bottomNode.orient;
        subrot = new FixpTransform(bottomNode.anchor, orient);
        if (pre != null) {
            subrot.preConcatenate(pre);
        }
        while (bottomNode.protoId instanceof CellId) {
            bottomCell = database.getCell((CellId) bottomNode.protoId);
            ImmutableExport bottomExport = ((Export) pp).getD();
            bottomNode = bottomCell.backup().cellRevision.getNodeById(bottomExport.originalNodeId);
            bottomPort = null;
            bottomNi = null;
//            bottomPort = bottomExport.getOriginalPort();
//            bottomNi = bottomPort.getNodeInst();
            pp = bottomExport.originalPortId.inDatabase(database);

            orient = orient.concatenate(bottomNode.orient);
            FixpTransform transform = new FixpTransform(bottomNode.anchor, bottomNode.orient);
            subrot.concatenate(transform);
        }
        bottomPp = (PrimitivePort) pp;
        subrot.translateFixp(-bottomNode.anchor.getFixpX(), -bottomNode.anchor.getFixpY());
    }

    /**
     * Method to return the bottommost NodeInst (a primitive)
     * from the initial port information given to the constructor.
     * @return the NodeInst at the bottom of the hierarchy (a primitive).
     */
    public NodeInst getBottomNodeInst() {
        if (bottomNi == null) {
            bottomPort = bottomCell.getPortInst(bottomNode.nodeId, bottomPp.getId());
            bottomNi = bottomPort.getNodeInst();
        }
        return bottomNi;
    }

    /**
     * Method to return the bottommost Cell
     * from the initial port information given to the constructor.
     * @return the Cell at the bottom of the hierarchy.
     */
    public Cell getBottomCell() {
        return bottomCell;
    }

    /**
     * Method to return the bottommost ImmutableNodeInst (a primitive)
     * from the initial port information given to the constructor.
     * @return the ImmutableNodeInst at the bottom of the hierarchy (a primitive).
     */
    public ImmutableNodeInst getBottomImmutableNodeInst() {
        return bottomNode;
    }

    /**
     * Method to return the bottommost PortProto (a PrimitivePort)
     * from the initial port information given to the constructor.
     * @return the PortProto at the bottom of the hierarchy (a PrimitivePort).
     */
    public PrimitivePort getBottomPortProto() {
        return bottomPp;
    }

    /**
     * Method to return the bottommost PortInst (on a primitive NodeInst)
     * from the initial port information given to the constructor.
     * @return the PortInst at the bottom of the hierarchy (on a primitive NodeInst).
     */
    public PortInst getBottomPort() {
        if (bottomPort == null) {
            getBottomNodeInst();
            bottomPort = bottomNi != null ? bottomNi.findPortInstFromProto(bottomPp) : null;
        }
        return bottomPort;
    }

    /**
     * Method to return the transformation matrix from the bottommost NodeInst
     * to the Cell containing the topmost PortInst.
     * The transformation includes any rotation on the node with the topmost PortInst.
     * @return the the transformation matrix from the bottommost NodeInst
     * to the Cell containing the topmost PortInst.
     */
    public FixpTransform getTransformToTop() {
        return subrot;
    }

    /**
     * Method to return the apparent orientation of the lowest node when viewed from the top.
     * The angle can be used to replicate a node at the top level that is in the same
     * orientation of the node at the bottom.
     * @return the apparent orientation of the lowest node when viewed from the top.
     */
    public Orientation getOrientToTop() {
        return orient;
    }
}
