import java.awt.*;
import java.io.*;
import java.util.*;

public class Lay {
    public final static String BUTTON_STRING    = "BUTTON";
    public final static String LABEL_STRING     = "LABEL";
    public final static String TEXTFIELD_STRING = "TEXTFIELD";
    public final static String TEXTAREA_STRING  = "TEXTAREA";
    public final static String CHECKBOX_STRING  = "CHECKBOX";
    public final static String LIST_STRING      = "LIST";
    public final static String CHOICE_STRING    = "CHOICE";

    public final static String FORMAT_STRING    = "FORMAT";

    public final static String LEFT_STRING      = "LEFT";
    public final static String CENTER_STRING    = "CENTER";
    public final static String RIGHT_STRING     = "RIGHT";    

    public final static String ALIGN_STRING     = "ALIGN";
    public final static String HORIZ_STRING     = "HORIZ";
    public final static String VERT_STRING      = "VERT";    

    public final static String FILL_STRING      = "FILL";
    public final static String WIDTH_STRING     = "WIDTH";
    public final static String HEIGHT_STRING    = "HEIGHT";    

    public final static String SPACE_STRING     = "SPACE";

    
    public final static int LEFT                = 0;    
    public final static int CENTER              = 1;
    public final static int RIGHT               = 2;    

    

    protected Container parent_;
    protected Hashtable components_;
    protected int insetX_, insetY_;

    private static boolean isPC_ =
         Toolkit.getDefaultToolkit().getClass().getName().
         indexOf("win32") != -1;

    private static boolean isUNIX_ =
         Toolkit.getDefaultToolkit().getClass().getName().
         indexOf("motif") != -1;
    
    private final static int TEXT_FUDGE = 11;
    private final static int CHECKBOX_FUDGE = 9;    
    
    public Lay(Container parent, int width, int height) {
        parent_ = parent;
        components_ = new Hashtable();

        parent_.setLayout(null);
        parent_.resize(width, height);

        insetX_ = parent_.insets().left;
        insetY_ = parent_.insets().top;        
    }

    
    public Component get(Object o) {
        if (o instanceof Component)
            return (Component)o;
        else if (o instanceof String)
            return (Component)components_.get((String)o);
        else
            return null;
    }

    public void add(DataInputStream input) throws IOException {
        String line = null;
        do {
            line = input.readLine();
            if (line != null) {
                line = line.trim();
                if (line.length() > 0 && !line.startsWith("//"))
                    add(line);
            }
        } while (line != null);
    }
    
    protected int end_;
    protected String line_;
    protected int length_;    

    public Component add(String line) {
        end_ = -1;
        line_ = line;
        length_ = line_.length();

        int type = getType();

        if (type == FORMAT)
            readFormat();
        
        String name = getString();
        Font font   = getFont();
        
        int x       = getInt();
        int y       = getInt();
        int width   = getInt();
        int height  = getInt();        

        Color fore  = getColor();
        Color back  = getColor();

        switch (type) {
              case BUTTON: 
                return addButton(name, font, x, y, width, height,
                                 fore, back, getString());
               case LABEL:
                return addLabel(name, font, x, y, width, height,
                                fore, back, getString());
               case TEXTFIELD:
                return addTextField(name, font, x, y, width, height,
                                    fore, back, getString());
               case CHECKBOX:
                String label = getString();
                boolean state = getBoolean();
                return addCheckbox(name, font, x, y, width, height,
                                   fore, back, label, state);
               case LIST:
                return addList(name, font, x, y, width, height,
                               fore, back, getList());
               case CHOICE:
                Vector items = getList();
                return addChoice(name, font, x, y, width, height,
                                 fore, back, items, getString());
        }
        return null;
    }

    protected void readFormat() {
        Vector v = new Vector();
        String instruct = getString().toUpperCase();

        String item;

        do {
            item = getString();
            if (item != null)
                v.addElement(item);
        } while (item != null);
        
        if (instruct.indexOf(ALIGN_STRING) != -1) {
            int justify = LEFT;
            if (instruct.indexOf(CENTER_STRING) != -1)
                justify = CENTER;
            else if (instruct.indexOf(RIGHT_STRING) != -1)
                justify = RIGHT;

            if (instruct.indexOf(HORIZ_STRING) != -1)
                alignHorizontal(justify, v);
            else if (instruct.indexOf(VERT_STRING) != -1)
                alignVertical(justify, v);
            else
                throw new IllegalArgumentException("Bad alignment: " +
                                                   instruct);
        }
        else if (instruct.indexOf(FILL_STRING) != -1) {
            if (instruct.indexOf(WIDTH_STRING) != -1)
                fillWidth(v);
            else if (instruct.indexOf(HEIGHT_STRING) != -1)
                fillHeight(v);
            else
                throw new IllegalArgumentException("Bad fill: " + instruct);
        }
        else if (instruct.indexOf(SPACE_STRING) != -1) {
            if (instruct.indexOf(WIDTH_STRING) != -1)
                spaceWidth(v);
            else if (instruct.indexOf(HEIGHT_STRING) != -1)
                spaceHeight(v);
            else
                throw new IllegalArgumentException("Bad fill: " + instruct);
        }
        else
            throw new IllegalArgumentException("Bad format: " + instruct);
    }
    
    protected String getString() {
        if (end_ >= length_)
            return null;
        
        int start = end_ + 1;
        end_ = line_.indexOf(',', start);
        if (end_ == -1)    end_ = length_;
        
        String result = line_.substring(start, end_).trim();
        if (result.length() >= 2)
            if (result.charAt(0) == '"')
                result = result.substring(1, result.length() - 1);
        
        if (result.equalsIgnoreCase("null"))
            return null;
        else
            return result;
    }

    protected int getType() {
        String typeString = getString();
        if (typeString == null)
            return UNKNOWN;
        
        if (typeString.equalsIgnoreCase(FORMAT_STRING))
            return FORMAT;
        else if (typeString.equalsIgnoreCase(BUTTON_STRING))
            return BUTTON;
        else if (typeString.equalsIgnoreCase(LABEL_STRING))
            return LABEL;
        else if (typeString.equalsIgnoreCase(TEXTFIELD_STRING))
            return TEXTFIELD;
        else if (typeString.equalsIgnoreCase(TEXTAREA_STRING))
            return TEXTAREA;
        else if (typeString.equalsIgnoreCase(CHECKBOX_STRING))
            return CHECKBOX;
        else if (typeString.equalsIgnoreCase(LIST_STRING))
            return LIST;
        else if (typeString.equalsIgnoreCase(CHOICE_STRING))
            return CHOICE;
        else
            return UNKNOWN;
    }
    
    protected int getInt() {
        String result = getString();
        if (result == null)
            return -1;
        else
            return Integer.parseInt(result);
    }

    protected boolean getBoolean() {
        String result = getString();
        if (result == null)
            return false;
        else if (result.equalsIgnoreCase("true"))
            return true;
        else if (result.equalsIgnoreCase("false"))
            return false;
        else
            return Integer.parseInt(result) != 0;
    }

    protected Font getFont() {
        Vector v = getList();
        if (v == null)
            return null;
        
        String name = (String)v.elementAt(0);
        String styles = ((String)v.elementAt(1)).toUpperCase();
        int style = Font.PLAIN;
        if (styles.indexOf("BOLD") != -1)
            style += Font.BOLD;
        if (styles.indexOf("ITALIC") != -1)
            style += Font.ITALIC;
        int size = Integer.parseInt((String)v.elementAt(2));
        return new Font(name, style, size);
    }

    protected Color getColor() {
        String result = getString();
        
        if (result == null)
            return null;
        else {
            if (result.equalsIgnoreCase("white"))
                return Color.white;
            else if (result.equalsIgnoreCase("lightGray"))
                return Color.lightGray;
            else if (result.equalsIgnoreCase("gray"))
                return Color.gray;
            else if (result.equalsIgnoreCase("darkGray"))
                return Color.darkGray;
            else if (result.equalsIgnoreCase("black"))
                return Color.black;
            else if (result.equalsIgnoreCase("red"))
                return Color.red;
            else if (result.equalsIgnoreCase("pink"))
                return Color.pink;
            else if (result.equalsIgnoreCase("orange"))
                return Color.orange;
            else if (result.equalsIgnoreCase("yellow"))
                return Color.yellow;
            else if (result.equalsIgnoreCase("green"))
                return Color.green;
            else if (result.equalsIgnoreCase("magenta"))
                return Color.magenta;
            else if (result.equalsIgnoreCase("cyan"))
                return Color.cyan;
            else if (result.equalsIgnoreCase("blue"))
                return Color.blue;
            else
                return null;
        }
    }

    protected Vector getList() {
        int start = end_ + 1;
        String result = getString();

        if (result == null)
            return null;

        Vector v = new Vector();
        boolean done = false;
        
        end_ = line_.indexOf('{', start);
        int close = line_.indexOf('}', end_ + 1);

        do {
            start = end_ + 1;
            end_ = line_.indexOf(',', start);
            if (end_ == -1 || end_ > close) {
                done = true;
                end_ = close;
            }
            result = line_.substring(start, end_).trim();
            v.addElement(result);
        } while (!done);
        
        end_ = line_.indexOf(',', end_);
        
        return v;
    }
    
    

    public Component addButton(String name, Font font, int x, int y,
                               int width, int height, Color fore, Color back,
                               String label) {
        Component c = new Button(label);
        addCommon(c, name, font, x, y, width, height, fore, back);
        return c;
    }
    
    public Component addLabel(String name, Font font, int x, int y,
                              int width, int height, Color fore, Color back,
                              String label) {
        Component c = new Label(label, Label.LEFT);
        if (isPC_ && x >= 0)
            x -= TEXT_FUDGE;

        addCommon(c, name, font, x, y, width, height, fore, back);
        return c;
    }
    
    public Component addTextField(String name, Font font, int x, int y,
                                  int width, int height, Color fore,
                                  Color back, String initialText) {
        Component c = new TextField(initialText);
        addCommon(c, name, font, x, y, width, height, fore, back);
        return c;
    }

    
    public Component addCheckbox(String name, Font font, int x, int y,
                                 int width, int height, Color fore,
                                 Color back, String label, boolean state) {
        Checkbox c = new Checkbox(label);
        c.setState(state);
        addCommon(c, name, font, x, y, width, height, fore, back);
        return c;
    }
    
    public Component addList(String name, Font font, int x, int y,
                             int width, int height, Color fore, Color back,
                             Vector items) {
        List c = new List();
        if (items != null) {
            int max = items.size();
            for (int loop = 0; loop < max; ++loop)
                c.addItem(items.elementAt(loop).toString());
        }
        addCommon(c, name, font, x, y, width, height, fore, back);
        return c;
    }

    public Component addChoice(String name, Font font, int x, int y,
                               int width, int height, Color fore, Color back,
                               Vector items, String select) {
        Choice c = new Choice();
        if (items != null) {
            int max = items.size();
            for (int loop = 0; loop < max; ++loop)
                c.addItem(items.elementAt(loop).toString());
        }
        c.select(select);
        addCommon(c, name, font, x, y, width, height, fore, back);
        return c;
    }

    protected void addCommon(Component c, String name, Font font,
                             int x, int y, int width, int height,
                             Color fore, Color back) {
        int type = getType(c);
        parent_.add(c);
        
        if (font != null) c.setFont(font);
        if (fore != null) c.setForeground(fore);
        if (back != null) c.setBackground(back);
        
        if (width < 0) {
            width = c.preferredSize().width;
            if (isPC_ && type == CHECKBOX)
                width -= CHECKBOX_FUDGE;
        }
        
        if (height < 0) {
            if (type != UNKNOWN)
                height = HEIGHTS[type];
            if (height < 0)
                height = c.preferredSize().height;
        }
        c.reshape(x + insetX_, y + insetY_, width, height);
        components_.put(name, c);
    }


    
    public void alignHorizontal(int justify, Vector v) {
        Object a[] = new Object[v.size()];
        v.copyInto(a);
        alignHorizontal(justify, a);
    }
    
    public void alignVertical(int justify, Vector v) {
        Object a[] = new Object[v.size()];
        v.copyInto(a);
        alignVertical(justify, a);
    }

    public void fillWidth(Vector v) {
        Object a[] = new Object[v.size()];
        v.copyInto(a);
        fillWidth(a);
    }
    
    public void fillHeight(Vector v) {
        Object a[] = new Object[v.size()];
        v.copyInto(a);
        fillHeight(a);
    }

    public void spaceWidth(Vector v) {
        Object a[] = new Object[v.size()];
        v.copyInto(a);
        spaceWidth(a);
    }
    
    public void spaceHeight(Vector v) {
        Object a[] = new Object[v.size()];
        v.copyInto(a);
        spaceHeight(a);
    }
    
    
    
    public void alignHorizontal(int justify, Object v[]) {
        int val;

        Component c = get(v[0]);

        switch (justify) {
            case CENTER: val = c.location().x + c.size().width / 2; break;
            case RIGHT:  val = c.location().x + c.size().width; break;
            default:     val = c.location().x; break;
        }
        val += insetX_;

        int type = getType(c);
        if (type != UNKNOWN)
            val -= HORIZ_ADDS[type];

        if (isPC_ && type == LABEL) {
            switch (justify) {
                case CENTER: val += TEXT_FUDGE / 2; break;
                   case LEFT:   val += TEXT_FUDGE; break;
            }
        }

        int max = v.length;
        for (int loop = 1; loop < max; ++loop) {
            c = get(v[loop]);
            type = getType(c);
            int add = (type == UNKNOWN) ? 0 : HORIZ_ADDS[type];

            if (isPC_ && type == LABEL)
                add -= TEXT_FUDGE;

            switch (justify) {
                case CENTER: c.move(val - c.size().width / 2 + add,
                                    c.location().y + insetY_); break;
                case RIGHT:  c.move(val - c.size().width + add,
                                    c.location().y + insetY_); break;
                default:     c.move(val + add,
                                    c.location().y + insetY_); break;
            }
        }
    }

    
    public void alignVertical(int justify, Object v[]) {
        int val;

        Component c = get(v[0]);

        switch (justify) {
            case CENTER: val = c.location().y + c.size().height / 2; break;
            case RIGHT:  val = c.location().y + c.size().height;     break;
            default:     val = c.location().y;                       break;
        }
        val += insetY_;

        int type = getType(c);
        if (type != UNKNOWN)
            val -= VERT_ADDS[type];

        int max = v.length;
        for (int loop = 1; loop < max; ++loop) {
            c = get(v[loop]);
            type = getType(c);
            int add = (type == UNKNOWN) ? 0 : VERT_ADDS[type];            

            switch (justify) {
                case CENTER: c.move(c.location().x + insetX_,
                                    val - c.size().height / 2 + add); break;
                case RIGHT:  c.move(c.location().x + insetX_,
                                    val - c.size().height + add); break;
                default:     c.move(c.location().x + insetX_,
                                    val + add); break;
            }
        }
    }

    
    public void fillWidth(Object v[]) {
        int fillWidth = 0;

        Component c;
        int max = v.length;
        for (int loop = 0; loop < max; ++loop) {
            c = get(v[loop]);
            if (c.size().width > fillWidth)
                fillWidth = c.size().width;
        }

        for (int loop = 0; loop < max; ++loop) {
            c = get(v[loop]);
            c.resize(fillWidth, c.size().height);
        }
    }


    public void fillHeight(Object v[]) {
        int fillHeight = 0;

        Component c;
        int max = v.length;
        for (int loop = 0; loop < max; ++loop) {
            c = get(v[loop]);
            if (c.size().height > fillHeight)
                fillHeight = c.size().height;
        }

        for (int loop = 0; loop < max; ++loop) {
            c = get(v[loop]);
            c.resize(c.size().width, fillHeight);
        }
    }


    public void spaceWidth(Object v[]) {
        Component c = get(v[0]);
        int x = c.location().x + c.size().width;
        
        int max = v.length;
        for (int loop = 1; loop < max; ++loop) {
            x += 2;
            c = get(v[loop]);
            c.move(x, c.location().y);
            x += c.size().width;
        }
    }

    public void spaceHeight(Object v[]) {
        Component c = get(v[0]);
        int y = c.location().y + c.size().height;
        
        int max = v.length;
        for (int loop = 1; loop < max; ++loop) {
            y += 2;
            c = get(v[loop]);
            c.move(c.location().x, y);
            y += c.size().height;
        }
    }


    public static Vector makeVector() {
        return makeVector(null, null, null, null, null, null);
    }

    public static Vector makeVector(Object o1) {
        return makeVector(o1, null, null, null, null, null);
    }

    public static Vector makeVector(Object o1, Object o2) {
        return makeVector(o1, o2, null, null, null, null);
    }

    public static Vector makeVector(Object o1, Object o2, Object o3) {
        return makeVector(o1, o2, o3, null, null, null);
    }
    
    public static Vector makeVector(Object o1, Object o2, Object o3,
                                    Object o4) {
        return makeVector(o1, o2, o3, o4, null, null);
    }

    public static Vector makeVector(Object o1, Object o2, Object o3,
                                    Object o4, Object o5) {
        return makeVector(o1, o2, o3, o4, o5, null);
    }
    
    public static Vector makeVector(Object o1, Object o2, Object o3,
                                    Object o4, Object o5, Object o6) {
        Vector v = new Vector();
        if (o1 != null) v.addElement(o1);
        if (o2 != null) v.addElement(o2);
        if (o3 != null) v.addElement(o3);
        if (o4 != null) v.addElement(o4);
        if (o5 != null) v.addElement(o5);
        if (o6 != null) v.addElement(o6);
        return v;
    }

    public void circle(Graphics g) {
        Enumeration elements = components_.elements();
        g.setColor(Color.red);
        while (elements.hasMoreElements()) {
            Component c = (Component)elements.nextElement();
            g.drawRect(c.location().x - 1, c.location().y - 1,
                       c.size().width + 2, c.size().height + 2);
        }
    }

    private final static int FORMAT     = -2;        
    private final static int UNKNOWN    = -1;
    
    private final static int BUTTON     = 0;
    private final static int LABEL      = 1;
    private final static int TEXTFIELD  = 2;
    private final static int TEXTAREA   = 3;    
    private final static int CHECKBOX   = 4;
    private final static int LIST       = 5;
    private final static int CHOICE     = 6;

                                              /* BU  LA  TF  TA  CB  LI  CH */
    private final static int PC_HEIGHTS[]    = { 18, 13, 23, 10, 15, -1, -1 };
    private final static int UNIX_HEIGHTS[]  = { 22, 13, -1, 10, -1, -1, -1 };
    
    
    private final static int HEIGHTS[]    = isPC_ ? PC_HEIGHTS : UNIX_HEIGHTS;
    private final static int HORIZ_ADDS[] = {  0,  0,  0,  0,  0,  0,  0 };
    private final static int VERT_ADDS[]  = {  0,  0,  1,  0,  0,  0,  0 };
    
    private static int getType(Component c) {
        if (c == null)
            return UNKNOWN;
        else if (c instanceof Button)
            return BUTTON;
        else if (c instanceof Label)
            return LABEL;
        else if (c instanceof TextField)
            return TEXTFIELD;
        else if (c instanceof TextArea)
            return TEXTAREA;
        else if (c instanceof Checkbox)
            return CHECKBOX;
        else if (c instanceof Choice)
            return CHOICE;
        else
            return UNKNOWN;
    }
    
}
