package pbl2011.gui;

import java.awt.Component;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

import pbl2011.common.CommonConst;
import pbl2011.common.CommonConst.TreeType;
import pbl2011.common.Util;
import pbl2011.model.Attribute;
import pbl2011.model.ClassNode;
import pbl2011.model.Method;
import pbl2011.model.PackageManager;
import pbl2011.mvc.Controller;
import pbl2011.mvc.Model;
import pbl2011.mvc.View;

/**
 * ツリー描画クラス
 * <p>
 *
 * @author 10745104 Y.Ishii
 *
 */
public class TreeInfo extends View {

	protected JTree tree;
	DefaultMutableTreeNode rootNode;
	protected DefaultTreeModel treeModel;
	protected ArrayList<ClassNode> currClassNodeList;

	OriginalTreeNode selectNode = null; // 選択したノード
	ClassNode currNodeCopy = null;	//　選択したClassNodeのコピー
	OriginalTreeNode selectPackage = null; // (前回)選択したパッケージ

	TreeInfo(Model m) {
		super(m);

		setLayout(new GridLayout());
		initialize();
	}

	@Override
	protected void draw() {
		ArrayList<ClassNode> cList = myModel.getAllActiveClassData();
		if (!needDraw(cList)) {
			return;
		}

		changeTreeModel(cList);

		changeSelectionPath();
	}

	private boolean needDraw(ArrayList<ClassNode> cList){
		if(cList.equals(currClassNodeList)){
			ClassNode cnode = cuMgr.getCurrentNode();
			if(cnode == null){
				if(currNodeCopy == null){
					//前回も選択されなかった場合
					return false;
				} else {
					//選択した状態から選択なし状態へ
					currNodeCopy = null;
					return true;
				}
			} else if(!cnode.equals(currNodeCopy)){
				//選択したクラスの詳細が変わった場合
				return true;
			}
			//何も変わっていなかった場合
			return false;
		} else {
			//クラス構成が変わった場合
			currClassNodeList = cList;
			return true;
		}
	}

	private void changeTreeModel(ArrayList<ClassNode> cList){
		Map<Integer, DefaultMutableTreeNode> map = new HashMap<Integer, DefaultMutableTreeNode>();
		rootNode.removeAllChildren();

		for (ClassNode c : cList) {
			if (!c.isBuiltInClass) {
				DefaultMutableTreeNode treeNode = new OriginalTreeNode(
						c.className, c.classId, TreeType.CLASS);
				DefaultMutableTreeNode pNode = getPackageNode(rootNode, map,
						c.packageId);
				pNode.add(treeNode);
				checkSelected(c.className, treeNode);
				for (Attribute a : c.attributeList) {
					DefaultMutableTreeNode node = new OriginalTreeNode(
							a.attributeName, c.classId, TreeType.ATTR);
					treeNode.add(node);
					checkSelected(a.attributeName, treeNode);
				}
				for (Method m : c.methodList) {
					DefaultMutableTreeNode node = new OriginalTreeNode(
							m.methodName, c.classId, TreeType.METHOD);
					treeNode.add(node);
					checkSelected(m.methodName, treeNode);
				}
			}
		}

		treeModel.reload();
	}

	private void changeSelectionPath() {
		ArrayList<ClassNode> cList = cuMgr.getCurrentNodeList();
		ClassNode cNode = cuMgr.getCurrentNode();
		if (cList != null && cList.size() == 1 && cNode != null) {
			if (selectNode != null) {
				if (selectNode.getClassId() == cNode.classId) {
					return;
				}
				selectPackage = (OriginalTreeNode) selectNode.getParent();
			}
			OriginalTreeNode newNode = trySelectTreeNode(cNode);
			if (newNode != null && newNode.getPath() != null) {
				OriginalTreeNode newParrent = (OriginalTreeNode) newNode
						.getParent();
				expandSelectPackage(newParrent);

				TreePath path = new TreePath(newNode.getPath());
				tree.setSelectionPath(path);
				selectNode = newNode;
				currNodeCopy = (ClassNode)cNode.clone();
			}
		} else {
			tree.clearSelection();
			collapseAll();
		}
	}

	private OriginalTreeNode trySelectTreeNode(ClassNode cNode) {
		if (cNode == null)
			return null;
		OriginalTreeNode child = null;
		Enumeration<?> e = rootNode.breadthFirstEnumeration();
		while (e.hasMoreElements()) {
			child = (OriginalTreeNode) e.nextElement();
			if (child.getKind().isCLASS()) {
				if (cNode.classId == child.getClassId()) {
					return child;
				}
			}
		}
		return null;
	}

	private void collapseAll() {
		int row = tree.getRowCount() - 1;
		while (row >= 0) {
			tree.collapseRow(row);
			row--;
		}
	}

	private void expandSelectPackage(OriginalTreeNode newParrent) {
		if (selectPackage != newParrent) {
			collapseAll();
			tree.expandPath(new TreePath(newParrent.getPath()));
		}
	}


	protected DefaultMutableTreeNode getPackageNode(DefaultMutableTreeNode root,
			Map<Integer, DefaultMutableTreeNode> map, int packageId) {
		DefaultMutableTreeNode pNode = map.get(packageId);
		if ( pNode == null) {
			String pName = PackageManager.getPackageName(packageId);
			pNode = new OriginalTreeNode(pName,
					packageId, TreeType.PACKAGE);
			map.put(packageId, pNode);
		}
		root.add(pNode);
		return pNode;
	}

	protected void checkSelected(String name, DefaultMutableTreeNode treeNode) {
		if (selectNode== null )return;
		if (name.equals((String)selectNode.getUserObject())){
			selectNode = (OriginalTreeNode) treeNode;
		}
	}
	protected void checkSelectedNode() {
		if (selectNode== null ||  cuMgr.getCurrentNode() == null)return;

		if (selectNode.getClassId() != cuMgr.getCurrentNode().classId) {
			selectNode = null;
		}

	}


	@Override
	protected void initialize() {
		rootNode = new OriginalTreeNode("", -1, TreeType.ROOT);
		treeModel = new DefaultTreeModel(rootNode);
		tree = new JTree(treeModel);
		tree.setCellRenderer(new TreeCellRenderer());
		tree.setRootVisible(false); // ルート非表示
		tree.addTreeSelectionListener(new TreeSelectionListener() {

			@Override
			public void valueChanged(TreeSelectionEvent e) {

				selectNode = (OriginalTreeNode) tree
						.getLastSelectedPathComponent();
				if (selectNode == null || !selectNode.getKind().isCLASS())
					return;

				ClassNode cnode = myModel.getClassData(selectNode.getClassId());
				if (cnode == null || cnode == cuMgr.getCurrentNode())
					return;
				cuMgr.setCurrentNode(cnode);
				currNodeCopy = (ClassNode)cnode.clone();

			}
		});

		add(tree);
		validate();
		draw();
	}

	@Override
	protected Controller makeController() {
		return null;

	}

}

class OriginalTreeNode extends DefaultMutableTreeNode {
	private TreeType kind;
	private int classId;

	public OriginalTreeNode(Object obj, int classId, TreeType kind) {
		super(obj);

		this.kind = kind;
		this.classId = classId;
	}

	TreeType getKind() {
		return kind;
	}

	int getClassId() {
		return classId;
	}

}

class TreeCellRenderer extends DefaultTreeCellRenderer implements CommonConst {

	public Component getTreeCellRendererComponent(JTree tree, Object value,
			boolean sel, boolean expanded, boolean leaf, int row,
			boolean hasFocus) {

		Component co = super.getTreeCellRendererComponent(tree, value, sel,
				expanded, leaf, row, hasFocus);

		if (value instanceof OriginalTreeNode) {
			TreeType kind = ((OriginalTreeNode) value).getKind();

			Icon icon = null;
		    ClassLoader cloader = Util.getClassLoader(this.getClass());
		    switch (kind) {
		    case CLASS:
				icon = new ImageIcon(cloader.getResource("img/" + "li_bl_b_c.png"));
				break;
		    case METHOD:
				icon = new ImageIcon(cloader.getResource("img/" + "li_b_b_m.png"));
				break;
		    case ATTR:
				icon = new ImageIcon(cloader.getResource("img/" + "li_r_b_a.png"));
				break;
		    case PACKAGE:
				icon = new ImageIcon(cloader.getResource("img/" + "PACKAGE.png"));
				break;
		    }

			((DefaultTreeCellRenderer) co).setIcon(icon);

		}
		return co;

	}

}