package relative;

import java.util.ArrayList;
import java.awt.Font;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.LayoutManager2;
import java.awt.Toolkit;

/**
 * The only layout manager that should ever have existed - thanks for nothing
 * Sun! Lays out components using dimensions that are in thousandths of the
 * area being laid out. Also changes font sizes for components as the area
 * increases or decreases.
 */
public class RelativeLayout implements LayoutManager2
{
    private ArrayList<RelativePlacement> layoutRegister;
    private ArrayList<RelativeFontScale> fontChangeRegister;

    private static ArrayList<FontList> fontLists = new ArrayList<FontList>();
    
    private static Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
    private static Component masterComponent = null;

    /**
     * Constructs a new layout manager ready to do scaling layouts.
     */
    public RelativeLayout(Component masterComp)
    {
        if (masterComp != null) masterComponent = masterComp;

        if (fontLists.size() == 0)
        {
            fontLists.add(new FontList("", Font.PLAIN));
        }

        layoutRegister = new ArrayList<RelativePlacement>();
        fontChangeRegister = new ArrayList<RelativeFontScale>();

        // Get the size of the default screen
        screenDim = Toolkit.getDefaultToolkit().getScreenSize();
    }

    public void addLayoutComponent(Component comp, Object obj)
    {
        if (obj == null) return;

        if (obj instanceof RelativePlacement)
        {
            RelativePlacement rp = (RelativePlacement) obj;
            rp.component = comp;
            layoutRegister.add(rp);
        }
    }

    public void addLayoutComponent(String name, Component comp) {}

    public void removeLayoutComponent(Component comp)
    {
        int index = -1;

        for (int x = 0; x < layoutRegister.size(); x++)
        {
            RelativePlacement rp = layoutRegister.get(x);
            if (rp.component == comp) index = x;
        }

        if (index >= 0) layoutRegister.remove(index);

        index = -1;

        for (int x = 0; x < fontChangeRegister.size(); x++)
        {
            RelativeFontScale rp = fontChangeRegister.get(x);
            if (rp.component == comp) index = x;
        }

        if (index >= 0) fontChangeRegister.remove(index);
    }

    public float getLayoutAlignmentX(Container parent) { return 0.5f; }

    public float getLayoutAlignmentY(Container parent ) { return 0.5f; }

    public void invalidateLayout(Container parent) {}

    public Dimension preferredLayoutSize(Container parent) { return new Dimension(100, 100); }

    public Dimension minimumLayoutSize(Container parent) { return new Dimension(10, 10); }

    public Dimension maximumLayoutSize(Container parent) { return new Dimension(1000, 1000); }

    public void registerForFontSizeChanges(Component c)
    {
        registerForFontSizeChanges(c, 1.0f);
    }

    public void registerForFontSizeChanges(Component comp, float scale)
    {
        registerForFontSizeChanges(comp, scale, "", Font.PLAIN);
    }

    public void registerForFontSizeChanges(Component comp, float scale, String name, int style)
    {
        fontChangeRegister.add(new RelativeFontScale(comp, scale, name, style));
    }

    public void layoutContainer(Container parent)
    {
        // Layout components with scaling of dimensions.
        int currWidth = parent.getWidth();
        int currHeight = parent.getHeight();

        if (currWidth < 1) currWidth = 1;
        if (currHeight < 1) currHeight = 1;

        int nComps = layoutRegister.size();

        for (int i = 0 ; i < nComps ; i++)
        {
            RelativePlacement c = layoutRegister.get(i);

            if (c.component.isVisible())
            {
                // Set the component's size and position.
                c.component.setBounds(c.xLoc * currWidth / 1000 + 1,
                            c.yLoc * currHeight / 1000 + 1,
                            c.width * currWidth / 1000 - 2,
                            c.height * currHeight / 1000 - 2 );
            }
        }


        // Next we change the fonts on all the components that have asked us
        // to do it.
        if (masterComponent != null)
        {
            currWidth = masterComponent.getWidth();
            currHeight = masterComponent.getHeight();
        }
        float fontScale = Math.min(currWidth, currHeight)/60.0f;
        if (fontScale < 7) fontScale = 7;

        nComps = fontChangeRegister.size();

        for (int i = 0; i < nComps; i++)
        {
            RelativeFontScale c = fontChangeRegister.get(i);

            FontList list = null;
            for (int x = 0; x < fontLists.size(); x++)
            {
                FontList aList = fontLists.get(x);
                if (aList.fontName.equals(c.name) && aList.fontStyle == c.style)
                {
                    list = aList;
                    x = fontLists.size();
                }
            }

            if (list == null)
            {
                list = new FontList(c.name, c.style);
                fontLists.add(list);
            }

            int fontSize = (int) (fontScale * c.scale);
            if (fontSize > 255) fontSize = 255;

            c.component.setFont(list.fontList[fontSize]);
        }
    }

    public static Dimension getSuggestedSize(float x, float y)
    {
        return new Dimension((int) (screenDim.width * x), (int) (screenDim.height * y));
    }
}

class RelativeFontScale
{
    public float scale;
    public Component component;
    public String name;
    public int style;

    public RelativeFontScale(Component c, float s, String n, int t)
    {
        scale = s;
        component = c;
        name = n;
        style = t;
    }
}

class FontList
{
    public Font[] fontList;
    public String fontName;
    public int fontStyle;

    public FontList(String name, int style)
    {
        fontName = name;
        fontStyle = style;
        fontList = new Font[256];

        for (int x = 0; x < 256; x++)
        {
            fontList[x] = new Font(name, style, x+1);
        }
    }
}