package cnp.ew.layout;

import java.util.*;
import java.awt.*;

import cnp.ew.lightweight.*;

public class CpAttachmentsLayout implements CpLayoutManager {

    Hashtable attachmentsDict = new Hashtable(10);
    Dimension preferredLayoutSize = new Dimension(100, 100);
    boolean shouldDumpRects=false;

    public void addLayoutLc(String name, CpLightweightComponent lc) {
    }

    public void removeLayoutLc(CpLightweightComponent lc) {
        attachmentsDict.remove(lc);
    }

/**
 * TBD: Come up with a better strategy for this; it's flawed
 */
    public Dimension minimumLayoutSize(CpLightweightComponent lc)
    {
        Rectangle rect;
        int minLeft = 10000;
        int maxRight = -10000;
        int minTop = 10000;
        int maxBottom = -10000;

        Enumeration e = computeRects(lc).elements();

        while (e.hasMoreElements()) {
            rect = (Rectangle)e.nextElement();
            minLeft = Math.min(rect.x, minLeft);
            maxRight = Math.max(rect.x + rect.width, maxRight);
            minTop = Math.min(rect.y, minTop);
            maxBottom = Math.max(rect.y + rect.height, maxBottom);
        }
        Insets insets = lc.insets();
        return new Dimension(maxRight - minLeft + insets.left + insets.right, maxBottom - minTop + insets.top + insets.bottom);
    }

    public Dimension preferredLayoutSize(CpLightweightComponent lc)
    {
        return minimumLayoutSize(lc);
    }

    public CpAttachments attachmentsFor(CpLightweightComponent lc)
    {
        return (CpAttachments)attachmentsDict.get(lc);
    }

    public void setAttachments(CpLightweightComponent lc, CpAttachments attachments)
    {
        attachmentsDict.put(lc, attachments);
    }

    public void setShouldDumpRects(boolean newShouldDumpRects)
    {
        shouldDumpRects = newShouldDumpRects;
    }

    public void layoutLc(CpLightweightComponent lc) {
        if (shouldDumpRects) {
            dumpRects(lc, computeRects(lc));
        }

        Hashtable computedRects = computeRects(lc);
        reshapeComponents(lc, computedRects);
    }

    void dumpRects(CpLightweightComponent targetLc, Hashtable rects)
    {
        CpLightweightComponent lc;
        Rectangle rect;
        Enumeration e = rects.keys();

        System.out.println("===========");
        System.out.println("Dumping child rects for parent " + targetLc);
        while (e.hasMoreElements()) {
            lc = (CpLightweightComponent)e.nextElement();
            rect = (Rectangle)rects.get(lc);
            System.out.println("    " + lc.toString());
            System.out.println("         rect: " + rect.toString());
        }
    }

    void reshapeComponents(CpLightweightComponent target, Hashtable computedRects)
    {
        Rectangle rect;
        Enumeration e = computedRects.keys();
        CpLightweightComponent lc;
        Insets insets = target.insets();

        while (e.hasMoreElements()) {
            lc = (CpLightweightComponent)e.nextElement();
            rect = (Rectangle)computedRects.get(lc);
            lc.reshape(rect.x + insets.left, rect.y + insets.top, rect.width, rect.height);
        }
    }

    /**
     * Answer a hashtable where the keys are the components, and the
     * values are the rectangles for each component.
     */
    Hashtable computeRects(CpLightweightComponent target) {

        Enumeration e;
        Rectangle rect;
        Insets insets = target.insets();
        Dimension targetExtent = target.size();
        targetExtent.width -= insets.left + insets.right;
        targetExtent.height -= insets.top + insets.bottom;

        // Accumulates the rects for those components whose rect has
        // already been calculated.
        Hashtable computedRects = new Hashtable(attachmentsDict.size());

        // Compute the rects for the components, using a depth first recursive
        // visitation of each of the prerequisite components for each component.
        e = attachmentsDict.keys();
        while (e.hasMoreElements()) {
            CpLightweightComponent curElement = (CpLightweightComponent)e.nextElement();
            computeRect(curElement, targetExtent, computedRects, new Hashtable());
        }

        return computedRects;
    }

    void computeRect(CpLightweightComponent lc, Dimension parentExtent, Hashtable computedRects, Hashtable visitedDict)
    {
        Point leftSiblingLeftAndRight, rightSiblingLeftAndRight, topSiblingTopAndBottom, bottomSiblingTopAndBottom;
        Rectangle rect;
        Enumeration e;
        CpLightweightComponent prereq;
        CpAttachments attachments = (CpAttachments)attachmentsDict.get(lc);

        // already computed in another pass.
        if (computedRects.get(lc) != null) {
            return;
        }

        if (visitedDict.get(lc) != null) {
            throw new IllegalArgumentException("Attachments have circular references");
        }

        visitedDict.put(lc, lc);

        if (attachments == null) {
            throw new IllegalArgumentException("Prerequisite sibling component has no attachments of its own");
        }

        if (attachments.leftSibling != null) {
            computeRect(attachments.leftSibling, parentExtent, computedRects, visitedDict);
        }

        if (attachments.rightSibling != null) {
            computeRect(attachments.rightSibling, parentExtent, computedRects, visitedDict);
        }

        if (attachments.topSibling != null) {
            computeRect(attachments.topSibling, parentExtent, computedRects, visitedDict);
        }

        if (attachments.bottomSibling != null) {
            computeRect(attachments.bottomSibling, parentExtent, computedRects, visitedDict);
        }

        leftSiblingLeftAndRight = null;
        prereq = attachments.getLeftSibling();
        if (prereq != null) {
            rect = (Rectangle)computedRects.get(prereq);
            if (rect != null) {
                leftSiblingLeftAndRight = new Point(rect.x, rect.x + rect.width);
            }
        }

        rightSiblingLeftAndRight = null;
        prereq = attachments.getRightSibling();
        if (prereq != null) {
            rect = (Rectangle)computedRects.get(prereq);
            if (rect != null) {
                rightSiblingLeftAndRight = new Point(rect.x, rect.x + rect.width);
            }
        }

        topSiblingTopAndBottom = null;
        prereq = attachments.getTopSibling();
        if (prereq != null) {
            rect = (Rectangle)computedRects.get(prereq);
            if (rect != null) {
                topSiblingTopAndBottom = new Point(rect.y, rect.y + rect.height);
            }
        }


        bottomSiblingTopAndBottom = null;
        prereq = attachments.getBottomSibling();
        if (prereq != null) {
            rect = (Rectangle)computedRects.get(prereq);
            if (rect != null) {
                bottomSiblingTopAndBottom = new Point(rect.y, rect.y + rect.height);
            }
        }
        rect = attachments.getRectangle(
            lc.preferredSize(),
            lc.bounds(),
            parentExtent,
            leftSiblingLeftAndRight,
            rightSiblingLeftAndRight,
            topSiblingTopAndBottom,
            bottomSiblingTopAndBottom);
        computedRects.put(lc, rect);
    }
}


