package ac.essex.ooechs.treeanimator;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Vector;

public class TreeDisplayPanel extends JPanel {

    private static final int gap = 10;

    private static final int totalSize = AnimatedNode.size + gap;

    private AnimatedNode root;

    protected int width;
    protected int height;

    public TreeDisplayPanel(AnimatedNode root, int width, int height) {
        
        this.root = root;

        this.width = width;
        this.height = height;

        calculateStartPositions();
        calculateEndPositions();

        addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getButton() == MouseEvent.BUTTON1) {
                    if (currentlyRunning == null)  {
                        calculateStartPositions();
                        calculateEndPositions();
                        start();
                    } else {
                        currentlyRunning.stopped = true;
                    }
                } else {
                    //reset
                    calculateStartPositions();
                    calculateEndPositions();
                    repaint();
                }
            }
        });

    }

    public Vector<AnimatedNode> startNodes;

    /**
     * Arranges all the nodes in a line along the top
     */
    public void calculateStartPositions() {

        int x = 50;
        int y = 50;

        startNodes = new Vector<AnimatedNode>(10);

        for (int i = 0; i < AnimatedNode.nodes.size(); i++) {
            AnimatedNode node = AnimatedNode.nodes.elementAt(i);
            AnimatedNode existing = getStartNode(node.getName());
            if (existing == null) {
                node.currentX = x;
                node.currentY = y;
                x += totalSize;
                startNodes.add(node);
            } else {
                // hide node
                node.currentX = existing.currentX;
                node.currentY = existing.currentY;
            }
            node.drawConnection = false;
        }

    }

    private AnimatedNode getStartNode(String name) {
        for (int i = 0; i < startNodes.size(); i++) {
            AnimatedNode node = startNodes.elementAt(i);
            if (node.getName().equals(name)) return node;
        }
        return null;
    }

    public void calculateEndPositions() {

        // Figure out how many nodes there are on each row
        root.calculateDepths();

        // find the deepest nodes
        int deepest = AnimatedNode.getDeepestNodeDepth();

        // get the nodes on the bottom row first
        Vector<AnimatedNode> leafNodes = AnimatedNode.getLeafNodes();

        int topX = (width - ((leafNodes.size() * totalSize) - gap))  / 2;
        int topY = (height - ((deepest * totalSize) - gap)) / 2;

        // arrange the leaf nodes
        int currentX = topX;
        for (int i = 0; i < leafNodes.size(); i++) {
            AnimatedNode node = leafNodes.elementAt(i);
            node.x = currentX;
            node.y = topY + (node.depth * totalSize);
            currentX += totalSize;
        }

        // now work backwards for each level
        for (int d = deepest - 1; d > 0; d--) {

            Vector<AnimatedNode> nodes = AnimatedNode.getNodesByDepth(d);

            for (int i = 0; i < nodes.size(); i++) {
                AnimatedNode node =  nodes.elementAt(i);
                if (!node.isLeafNode()) {
                    // put me in the middle of my children
                    node.x = (node.getFirstChild().x + node.getLastChild().x) / 2;
                    node.y = topY + (node.depth * totalSize);
                }
            }

        }          

    }

    public void paintComponent(Graphics g) {
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, getWidth(), getHeight());

        ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        draw(g, root);
    }

    public void draw(Graphics g, AnimatedNode parent) {

        Vector<AnimatedNode> children = parent.getChildren();

        for (int i = 0; i < children.size(); i++) {
            AnimatedNode child = children.elementAt(i);
            if (child.drawConnection) {
                g.setColor(Color.BLACK);
                g.drawLine(parent.x, parent.y, (int) child.currentX, (int) child.currentY);
            }
            draw(g, child);
        }

        parent.draw(g);

    }

    public void start() {
        new AnimatorThread().start();
    }

    private AnimatorThread currentlyRunning = null;

    private class AnimatorThread extends Thread {

        protected boolean stopped = false;

        protected int currentLevel;
        protected Vector<AnimatedNode> nodesOnLevel;

        public void run() {

            currentlyRunning = this;

            while (!stopped) {
                try {
                    animate();
                    repaint();
                    Thread.sleep(15);
                } catch (InterruptedException e) {
                    // do nothing
                }
            }

            currentlyRunning = null;

        }

        public void animate() {

            if (nodesOnLevel == null) {
                nodesOnLevel = AnimatedNode.getNodesByDepth(currentLevel);
                for (int i = 0; i < nodesOnLevel.size(); i++) {
                    AnimatedNode animatedNode = nodesOnLevel.elementAt(i);
                        if (currentLevel == 1) {
                            animatedNode.moveToPosition();
                        } else {
                            animatedNode.moveToParentPosition();
                        }
                    animatedNode.drawConnection = true;
                    animatedNode.visible = true;
                    animatedNode.calculateVector(50);
                }
            }

            boolean allInPosition = true;

            for (int i = 0; i < nodesOnLevel.size(); i++) {
                AnimatedNode node = nodesOnLevel.elementAt(i);
                node.move();
                if (!node.inPosition()) {
                    allInPosition = false;
                }
            }

            if (allInPosition) {
                nodesOnLevel = null;
                currentLevel++;
                if (currentLevel > AnimatedNode.getDeepestNodeDepth()) stopped = true;
            }                                                

        }

    }

}
