package IR2Vis;

import IR2.*;
import java.awt.*;
import java.awt.event.*;

/**
 * IRVisWin
 *
 * is a simple Intermediate Representation Tree visualization gadget. 
 * It displays IR trees made of IR.Debuggable objects on an AWT window.
 */

public class IRVisWin extends Frame
{
    boolean tree_mode;      // tree mode or list mode

    static int windowCount = 0; // count the windows so we know 
                    // when to terminate the whole process

    IRVisWinListener the_listener;  // mouse and command action listeners

    TextArea descriptionWindow; // description

    ScrollPane sc;      // scroll pane - must have this to 
                // contain drawingBoard for setSize
                // of components of drawingBoard to work
    IRVisPanel drawingBoard;    // place to draw the nodes

    IRVisTreeNode root; // root node
    IRVisTreeNode selectedNode; // currently selected node
    FontMetrics fm;     // for calculation of label width

    int list_posy, list_posx;

    private void initWin() 
    {
        setLayout(new BorderLayout());

        // menu bar
        MenuBar mb = new MenuBar();

        // file menu
        Menu m = new Menu("File");

        MenuItem mi = new MenuItem("Exit");
        mi.addActionListener(the_listener);
        mi.setActionCommand("Exit");
        m.add(mi);

        mb.add(m);

        // display menu
        m = new Menu("Display");

        mi = new MenuItem("Tree Mode");
        mi.addActionListener(the_listener);
        mi.setActionCommand("TreeMode");
        m.add(mi);

        mi = new MenuItem("List Mode");
        mi.addActionListener(the_listener);
        mi.setActionCommand("ListMode");
        m.add(mi);

        mi = new MenuItem("Redraw Subtree");
        mi.addActionListener(the_listener);
        mi.setActionCommand("RedrwCld");
        m.add(mi);

        mb.add(m);
        setMenuBar(mb);

        // drawing board
        sc = new ScrollPane();
        this.add("Center",sc);
        drawingBoard = new IRVisPanel(this);
        sc.add(drawingBoard);

        // description window
        descriptionWindow = new TextArea(14,80);
        descriptionWindow.setEditable(false);
        this.add("South",descriptionWindow);

        setVisible(false);
        show();
        setSize(600,800);
    } 

    /**
     * Constructor. Call this constructor to create a window
     * displaying the tree rooted at the specified Debuggable object.
     *
     * @param n Root of the IR tree
     */
    public IRVisWin(Walkable n)
    {
        super("IR Visualization Window");

        the_listener = new IRVisWinListener(this);
        selectedNode = null;
        tree_mode = true;

        root = new IRVisTreeNode(n);
        root.addMouseListener(the_listener);

        initWin();

        fm = getFontMetrics(getFont());
        int winHeight = getSize().height;
        int winWidth = getSize().width;
        int drawingBoardWidth = drawingBoard.getPreferredSize().width;

        drawingBoard.add(root);
        root.setBounds(new Rectangle((drawingBoardWidth/2), 300,
                   (int)(fm.stringWidth(root.getTitle())*1.2),
                   (int)(fm.getHeight()*1.2)));
        sc.setScrollPosition((drawingBoardWidth-winWidth)/2,0);
        System.out.println("dbwidth: "+drawingBoardWidth+"\nwinWidth: "+winWidth+"\n");

        IRVisWin.windowCount++; // increment window count
    }

    /**
     * Pop the window up. Upon initialization, the window is by
     * default poped down.
     */
    public void popup() { setVisible(true); }

    /**
     * Pop the window down.
     */
    public void popdown() { setVisible(false); }

    /**
     * Redraw the screen.
     */
    public void paint(Graphics g)
    {
        Graphics drwg = drawingBoard.getGraphics();

        // first we erase all the previous drawn lines
        // by filling the window with background color
        Color c = drwg.getColor();
        drwg.setColor(this.getBackground());
        drwg.fillRect(0,0,
                  drawingBoard.getSize().width,
                  drawingBoard.getSize().height);
        drwg.setColor(c);

        // then we draw lines
        drawToChildren(root, drwg);
    }

    void drawToChildren(IRVisTreeNode tn, Graphics g)
        // draw all the connections recursively
    {
        // only draw lines to children if they are visible
        if (!tn.expanded) return;

        int children_count = tn.NumberOfChildren();
        int x1 = tn.getLocation().x+(int)(tn.getSize().width/2);
        int y1 = tn.getLocation().y+(int)(tn.getSize().height/2);

        for(int i = 0; i < children_count; i++) {
            IRVisTreeNode child = tn.ChildNode(i);
            int x2 = child.getLocation().x +
                 (int)(child.getSize().width/2);
            int y2 = child.getLocation().y +
                 (int)(child.getSize().height/2);

            if (tree_mode) 
                g.drawLine(x1,y1,x2,y2);
            else {
                g.drawLine(x1,y1,x1,y2);
                g.drawLine(x1,y2,x2,y2);
            }

            // depth first search
            drawToChildren(child, g);
        }
    }


    void expand(IRVisTreeNode tn, boolean redraw) 
        // expand a tree node
    {
        if (tree_mode) expand_tree(tn, redraw);
        else {
            list_posy = -1;
            list_posx = -1;
            expand_list(tn,redraw);
        }
    }


    void expand_tree(IRVisTreeNode tn, boolean redraw) 
    {
        int posy, posx;
        int children_count = tn.NumberOfChildren();

        posy = tn.getLocation().y + 60;
        posx = tn.getLocation().x -
                (int)(children_count*90/2)+45;

        for(int i = 0; i < children_count; i++) {
            IRVisTreeNode child = tn.ChildNode(i);

            // only add each child to window once
            if (!child.added) {
                // System.out.println("adding children");
                child.addMouseListener(the_listener);
                child.added = true;
                drawingBoard.add(child);
            }

            // only calculate position if dirty bit is set

            if (tn.dirty) {
                child.setBounds
                  (new Rectangle(posx, posy,
                   (int)(fm.stringWidth(child.getTitle())*1.2),
                   (int)(fm.getHeight()*1.2)));
            }
            child.setVisible(true);

            // if child marked expanded, expand it
            if (child.expanded) {
                if (redraw) child.dirty = true;
                expand(child, redraw);
            }
            posx += 90;
        }
        tn.dirty = false;
    }


    void expand_list(IRVisTreeNode tn, boolean redraw)
    {
        int posx;
        int children_count = tn.NumberOfChildren();

        if (list_posx < 0) {
            list_posx = tn.getLocation().x+tn.getSize().width;
            list_posy = tn.getLocation().y+tn.getSize().height+10;
        } 

        for(int i = 0; i < children_count; i++) {
            IRVisTreeNode child = tn.ChildNode(i);

            // only add each child to window once
            if (!child.added) {
                child.addMouseListener(the_listener);
                child.added = true;
                drawingBoard.add(child);
            }

            child.setBounds
              (new Rectangle(list_posx, list_posy,
               (int)(fm.stringWidth(child.getTitle())*1.2),
               (int)(fm.getHeight()*1.2)));

            child.setVisible(true);

            list_posy += (tn.getSize().height + 10);

            // if child marked expanded, expand it
            if (child.expanded) {
                if (redraw) child.dirty = true;
                list_posx += child.getSize().width;
                expand_list(child, redraw);
                list_posx -= child.getSize().width;
                int cn = child.NumberOfChildren();
            }
        }
        tn.dirty = false;
    }

    void contract(IRVisTreeNode tn)
        // contract a tree node
        //
        // we never mark anything "contracted" so when we
        // expand again we get back the same states for
        // all sub trees. this tree itself is marked
        // contracted by the caller
    {
        int children_count = tn.NumberOfChildren();

        // contract each child
        for(int i = 0; i < children_count; i++) {
            IRVisTreeNode child = tn.ChildNode(i);
            // contract children of child
            if (child.expanded)
                contract(child);
            // now contract child
            child.setVisible(false);
        }
    }
} 



class IRVisWinListener implements MouseListener, ActionListener
    // mouse actions and menu bars
{
    IRVisWin parent;
    IRVisTreeNode dragged;
    int dragged_prev_x;
    int dragged_prev_y;

    IRVisWinListener(IRVisWin win) { parent = win; dragged = null; }

    public void actionPerformed(ActionEvent e)
    {
        if (e.getActionCommand().equals("Exit")) {
            parent.popdown();
            parent.dispose();
            java.lang.System.exit(0);
        } else if (e.getActionCommand().equals("Close")) {
            parent.popdown();
            parent.dispose();
            IRVisWin.windowCount--;
            if (IRVisWin.windowCount==0) 
                java.lang.System.exit(0);
        } else if (e.getActionCommand().equals("ListMode")) {
            parent.tree_mode = false;
            if (parent.selectedNode != null)
                selectNode(parent.root);
            parent.root.dirty = true;
            parent.contract(parent.root);
            parent.root.expanded = false;

        } else if (e.getActionCommand().equals("TreeMode")) {
            parent.tree_mode = true;
            if (parent.selectedNode != null)
                selectNode(parent.root);
            parent.root.dirty = true;
            parent.contract(parent.root);
            parent.root.expanded = false;

        } else if (e.getActionCommand().equals("RedrwCld")) {
            if (parent.selectedNode != null &&
                parent.selectedNode.expanded == true) {
                parent.selectedNode.dirty = true;
                parent.expand(parent.selectedNode, true);
            }
        }
    }

    public void mouseClicked(MouseEvent e) 
    { 
        Component c = e.getComponent();
        if (c instanceof IRVisTreeNode) {
            IRVisTreeNode tn = (IRVisTreeNode)c;
            if (e.getClickCount()>1) {
                // System.out.println("more than one click");
                if (!tn.expanded) {
                    if (parent.tree_mode) {
                        parent.expand(tn, false);
                        tn.expanded = true;
                    } else {
                        tn.expanded = true;
                        parent.expand(parent.root, 
                                  false);
                    }
                } else {
                    parent.contract(tn);
                    tn.expanded = false;
                    if (!parent.tree_mode &&
                        !tn.equals(parent.root)) {
                        parent.expand(parent.root,
                                  false);
                    }
                }
                parent.paint(parent.getGraphics());
            }
        } 
    }

    public void mouseEntered(MouseEvent e) 
    { 
    }

    public void mouseExited(MouseEvent e) 
    { 
    }

    public void mouseReleased(MouseEvent e) 
    { 
        if (dragged != null && 
            (parent.tree_mode || dragged.equals(parent.root))) {
            int x=dragged.getLocation().x+e.getX()-dragged_prev_x;
            int y=dragged.getLocation().y+e.getY()-dragged_prev_y;
            if (e.getX() != dragged_prev_x ||
                e.getY() != dragged_prev_y) {
              dragged.setLocation(x,y);
              // dragged.dirty = true;
            }
            dragged = null;
        }
    }

    public void mousePressed(MouseEvent e) 
    { 
        Component c = e.getComponent();
        if (c instanceof IRVisTreeNode) {
            IRVisTreeNode tn = (IRVisTreeNode)c;
            dragged = tn;
            dragged_prev_x = e.getX();
            dragged_prev_y = e.getY();
            selectNode(tn);
        }
    }

    void selectNode(IRVisTreeNode tn)
    {
        if (parent.selectedNode != tn) {
            if (parent.selectedNode != null)
                parent.selectedNode.setBackground
                    (parent.getBackground());
            parent.descriptionWindow.
                setText(tn.Description());
            tn.setBackground(Color.yellow);
            parent.selectedNode = tn;
        }
    }
}


class IRVisPanel extends Panel
    // homebrew pane
{
    Container parent;
    IRVisPanel(Container p) { super(); parent = p; }

    public Dimension getMinimumSize() {
        return new Dimension(400,400);
    }

    public Dimension getPreferredSize() {
        return new Dimension(8000,40000);
    }

    public void paint(Graphics g) { parent.paint(g); }
}

