package com.ramussoft.gui.branches;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import org.jdesktop.swingx.JXTreeTable;
import org.jdesktop.swingx.tree.TreeModelSupport;
import org.jdesktop.swingx.treetable.DefaultTreeTableModel;
import org.jdesktop.swingx.treetable.TreeTableNode;

import com.ramussoft.common.Branch;
import com.ramussoft.common.Engine;
import com.ramussoft.common.event.BranchAdapter;
import com.ramussoft.common.event.BranchEvent;
import com.ramussoft.gui.common.AbstractUniqueView;
import com.ramussoft.gui.common.GUIFramework;
import com.ramussoft.gui.common.GlobalResourcesManager;
import com.ramussoft.gui.common.UniqueView;
import com.ramussoft.gui.common.event.ActionChangeEvent;
import com.ramussoft.gui.common.event.ActionChangeListener;
import com.ramussoft.gui.qualifier.Commands;

public class BranchView extends AbstractUniqueView implements UniqueView {

    private Engine engine;
    private BranchModel branchModel;
    private AddChildBranch addChildBranch = new AddChildBranch();
    private ActivateBranch activateBranch = new ActivateBranch();
    private BranchEditCommment editCommment = new BranchEditCommment();
    private JXTreeTable table;
    private DateFormat timeFormat = DateFormat.getDateTimeInstance(
            DateFormat.MEDIUM, DateFormat.MEDIUM);
    private BranchAdapter branchListener;

    private long actualBranch;

/*	private ImageIcon branch = new ImageIcon(getClass().getResource(
            "/com/ramussoft/gui/branch-down.png"));
	private ImageIcon branchTree = new ImageIcon(getClass().getResource(
			"/com/ramussoft/gui/branch-down-right.png"));*/

    public BranchView(GUIFramework framework) {
        super(framework);
        this.engine = framework.getEngine();
    }

    public class Node implements TreeTableNode, Comparable<Node> {

        private List<Node> children = new ArrayList<BranchView.Node>();

        Branch branch;

        private Node parent;

        public Node(Branch branch) {
            this.branch = branch;
        }

        @Override
        public int getChildCount() {
            return children.size();
        }

        @Override
        public int getIndex(TreeNode node) {
            return children.indexOf(node);
        }

        @Override
        public boolean getAllowsChildren() {
            return true;
        }

        @Override
        public boolean isLeaf() {
            return children.size() == 0;
        }

        @Override
        public Enumeration<? extends TreeTableNode> children() {
            return Collections.enumeration(children);
        }

        @Override
        public Object getValueAt(int column) {
            switch (column) {
                case 0:
                    String text = branch.getReason();
                    if (parent == null) {
                        text = GlobalResourcesManager.getString("BranchesRoot");
                        return text;
                    }
                    if (branch.getChildren().size() == 0) {
                        text += " ("
                                + GlobalResourcesManager
                                .getString("BranchEditable") + ")";
                    }
                    return text;
                case 1:
                    return timeFormat.format(branch.getCreationTime());
                case 2:
                    return branch.getModule();
                case 3: {
                    if (branch.getType() == 0)
                        return GlobalResourcesManager.getString("Branch.Main");
                    return GlobalResourcesManager.getString("Branch.Child");
                }

            }
            return null;
        }

        @Override
        public TreeTableNode getChildAt(int childIndex) {
            return children.get(childIndex);
        }

        @Override
        public int getColumnCount() {
            return 2;
        }

        @Override
        public Node getParent() {
            return parent;
        }

        @Override
        public boolean isEditable(int column) {
            return false;
        }

        @Override
        public void setValueAt(Object aValue, int column) {
        }

        @Override
        public Object getUserObject() {
            return branch;
        }

        @Override
        public void setUserObject(Object userObject) {
            this.branch = (Branch) userObject;
        }

        public void sort() {
            Collections.sort(children);
            for (Node node : children)
                node.sort();
        }

        @Override
        public int compareTo(Node o) {
            if (branch.getBranchId() < o.branch.getBranchId())
                return -1;
            if (branch.getBranchId() > o.branch.getBranchId())
                return 1;
            return 0;
        }

    }

    ;

    private class AddChildBranch extends AbstractAction {

        /**
         *
         */
        private static final long serialVersionUID = 7924659449991534686L;

        public AddChildBranch() {
            putValue(ACTION_COMMAND_KEY, "Action.AddBranchChild");
            putValue(
                    SMALL_ICON,
                    new ImageIcon(getClass().getResource(
                            "/com/ramussoft/gui/branch-down.png")));
            setEnabled(false);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            addChildBranch();
        }
    }

    ;

    private class ActivateBranch extends AbstractAction {

        /**
         *
         */
        private static final long serialVersionUID = 7924659449991534686L;

        public ActivateBranch() {
            putValue(ACTION_COMMAND_KEY, "Action.ActivateBranch");
            putValue(
                    SMALL_ICON,
                    new ImageIcon(getClass().getResource(
                            "/com/ramussoft/gui/branch-actual.png")));
            setEnabled(false);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            int row = table.getSelectedRow();
            if (row < 0)
                return;
            Node node = (Node) table.getPathForRow(row).getLastPathComponent();
            engine.setActiveBranch(node.branch.getBranchId());
        }
    }

    ;

    private class BranchEditCommment extends AbstractAction {

        /**
         *
         */
        private static final long serialVersionUID = 7924659449991534686L;

        public BranchEditCommment() {
            putValue(ACTION_COMMAND_KEY, "Action.EditBranchComment");
            putValue(
                    SMALL_ICON,
                    new ImageIcon(getClass().getResource(
                            "/com/ramussoft/gui/table/edit-comment-branch.png")));
            setEnabled(false);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            int row = table.getSelectedRow();
            if (row < 0)
                return;
            Node node = (Node) table.getPathForRow(row).getLastPathComponent();
            String s = (String) JOptionPane.showInputDialog(framework
                            .getMainFrame(), GlobalResourcesManager
                            .getString("BranchCreationReason"), GlobalResourcesManager
                            .getString("Action.EditBranchComment"),
                    JOptionPane.QUESTION_MESSAGE, null, null, node.branch
                            .getReason());
            if (s != null) {
                node.branch.setReason(s);
                engine.updateBranch(node.branch);
                branchModel.getBranchModelSupport().firePathChanged(
                        nodeToTreePath(node));
            }
        }

        private TreePath nodeToTreePath(Node node) {
            List<Node> list = new ArrayList<Node>();
            while (node != null) {
                list.add(0, node);
                node = node.getParent();
            }

            TreePath parentPath = new TreePath(list.toArray());
            return parentPath;
        }
    }

    ;

    @Override
    public Action[] getActions() {
        return new Action[]{activateBranch, addChildBranch, editCommment};
    }

    public void addChildBranch() {
        int row = table.getSelectedRow();
        if (row < 0)
            return;
        Node node = (Node) table.getPathForRow(row).getLastPathComponent();
        String message = GlobalResourcesManager
                .getString("BranchCreationWarning");
        if (node.branch.getChildren().size() > 0)
            message = GlobalResourcesManager
                    .getString("BranchNodeCreationWarniong");
        if (JOptionPane.showConfirmDialog(framework.getMainFrame(), message,
                UIManager.getString("OptionPane.titleText"),
                JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) != JOptionPane.YES_OPTION)
            return;
        String s = JOptionPane.showInputDialog(framework.getMainFrame(),
                GlobalResourcesManager.getString("BranchCreationReason"),
                GlobalResourcesManager.getString("BranchCreationReason"),
                JOptionPane.QUESTION_MESSAGE);
        if (s == null)
            return;
        int type = node.branch.getType() - 1;
        if (node.branch.getChildren().size() > 0) {
            type = getMaxType(type, (Node) branchModel.getRoot());
        }
        engine.createBranch(node.branch.getBranchId(), s, type + 1, "core");
    }

    private int getMaxType(int type, Node node) {
        if (node.branch.getType() > type)
            type = node.branch.getType();
        for (Node n : node.children) {
            int t = getMaxType(type, n);
            if (type < t)
                type = t;
        }
        return type;
    }

    @Override
    public JComponent createComponent() {
        this.branchModel = new BranchModel(createRoot(engine.getRootBranch()));

        this.table = new BranchTreeTable(branchModel, this);
        table.getSelectionModel().addListSelectionListener(
                new ListSelectionListener() {

                    @Override
                    public void valueChanged(ListSelectionEvent e) {
                        boolean b = table.getSelectedRow() >= 0;
                        Node node = null;
                        if (b) {
                            node = (Node) table.getPathForRow(
                                    table.getSelectedRow())
                                    .getLastPathComponent();
                            if (node.branch.getChildren().size() > 0)
                                addChildBranch.setEnabled(false);
                                //addChildBranch.putValue(Action.SMALL_ICON,
                                //		branchTree);
                            else
                                addChildBranch.setEnabled(true);
                            //addChildBranch.putValue(Action.SMALL_ICON,
                            //		branch);
                            ActionChangeEvent event = new ActionChangeEvent(
                                    new Action[]{addChildBranch, editCommment});
                            for (ActionChangeListener l : getActionChangeListeners())
                                l.actionsRemoved(event);
                            for (ActionChangeListener l : getActionChangeListeners())
                                l.actionsAdded(event);

                        } else
                            addChildBranch.setEnabled(false);

                        activateBranch.setEnabled(b
                                && node.branch.getBranchId() != actualBranch);
                        editCommment.setEnabled(b && node.parent != null);

                    }
                });

        table.setRootVisible(true);
        branchListener = new BranchAdapter() {

            @Override
            public void branchDeleted(BranchEvent event) {
                branchModel.setRoot(createRoot(engine.getRootBranch()));
            }

            @Override
            public void branchCreated(final BranchEvent event) {
                branchModel.setRoot(createRoot(engine.getRootBranch()));
                SwingUtilities.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        table.expandAll();
                        for (int i = 0; i < table.getRowCount(); i++) {
                            Node n = (Node) table.getPathForRow(i)
                                    .getLastPathComponent();
                            if (n.branch.getBranchId() == event.getBranchId()) {
                                table.getSelectionModel().addSelectionInterval(
                                        i, i);
                                break;
                            }
                        }
                    }
                });
            }

            @Override
            public void branchActivated(BranchEvent event) {
                framework.propertyChanged(Commands.FULL_REFRESH);
                actualBranch = event.getBranchId();
                SwingUtilities.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        table.repaint();
                    }
                });
            }
        };
        engine.addBranchListener(branchListener);

        table.expandAll();
        actualBranch = engine.getActiveBranch();
        return new JScrollPane(table);
    }

    protected Node createRoot(Branch rootBranch) {
        Node root = new Node(rootBranch);
        for (Branch b : rootBranch.getChildren()) {
            if (b.getType() == root.branch.getType()) {
                Node e = new Node(b);
                e.parent = root;
                root.children.add(e);
                addNode(root, b);
            } else {
                Node e = createRoot(b);
                Node p = null;
                Node x = root;
                while (x != null) {
                    for (Node n : x.children)
                        if (n.branch.getBranchId() == b.getParentBranchId())
                            p = n;
                    if (p != null)
                        break;
                    x = x.parent;
                }
                if (p == null
                        && root.branch.getBranchId() == b.getParentBranchId())
                    p = root;
                e.parent = p;
                p.children.add(e);
                for (int i = e.children.size() - 1; i >= 0; i--) {
                    Node n = e.children.get(i);
                    if (n.branch.getType() == b.getType()) {
                        n.parent.children.remove(n);
                        n.parent = p;
                        p.children.add(n);
                    }
                }
            }
        }
        return root;
    }

    private void addNode(Node root, Branch br) {
        for (Branch b : br.getChildren()) {
            if (b.getType() == root.branch.getType()) {
                Node e = new Node(b);
                e.parent = root;
                root.children.add(e);
                addNode(root, b);
            } else {
                Node e = createRoot(b);
                Node p = null;
                for (Node n : root.children)
                    if (n.branch.getBranchId() == b.getParentBranchId())
                        p = n;
                e.parent = p;
                p.children.add(e);
                for (int i = e.children.size() - 1; i >= 0; i--) {
                    Node n = e.children.get(i);
                    if (n.branch.getType() == b.getType()) {
                        n.parent.children.remove(n);
                        n.parent = p;
                        p.children.add(n);
                    }
                }
            }
        }
    }

    @Override
    public void close() {
        super.close();
        engine.removeBranchListener(branchListener);
    }

    private class BranchModel extends DefaultTreeTableModel {
        public BranchModel(TreeTableNode root) {
            super(root);
        }

        @Override
        public void setRoot(TreeTableNode root) {
            Node n = (Node) root;
            n.sort();
            super.setRoot(root);
        }

        @SuppressWarnings("unused")
        public Node findNode(long branchId) {
            return findNode(branchId, (Node) root);
        }

        private Node findNode(long branchId, Node node) {
            if (node.branch.getBranchId() == branchId)
                return node;
            for (Node n : node.children) {
                Node t = findNode(branchId, n);
                if (t != null)
                    return t;
            }
            return null;
        }

        public TreeModelSupport getBranchModelSupport() {
            return modelSupport;
        }

        @Override
        public String getColumnName(int column) {
            switch (column) {
                case 0:
                    return GlobalResourcesManager.getString("BranchCreationReason");
                case 1:
                    return GlobalResourcesManager.getString("BranchCreationTime");

                default:
                    break;
            }
            return super.getColumnName(column);
        }
    }

    ;

    @Override
    public String getId() {
        return "BranchesList";
    }

    @Override
    public String getDefaultWorkspace() {
        return "Workspace.Qualifiers";
    }

    @Override
    public String getDefaultPosition() {
        return BorderLayout.WEST;
    }

    public long getActualBranch() {
        return actualBranch;
    }
}