/* $Id: SpreadSelectionTree.java 893 2014-10-03 17:37:32Z shayashi $ */
/* TODO 
 * Spread Selection List �́A 2, 12-18, 3, 6 �̂悤�ȕ\���B�N���X�Ƃ��ẮA
 * ���O�t���̂��������f�[�^�̃N���X�B�����scope�Ɏw�肵�ăT�[�`����΂悢�B
 * ����́A���� SpreadSelectionList ����邽�߂� GUI editor �Ƃ��Ĉʒu�t����B
 * �N�������� SpreadTree �� editor �� disable �ɁB
 * Make �Ƃ��̃{�^���� spreadSelectionList ��f���ASpreadTree �� editor �͌��̏�ԂɁB
 * ���̑�������Ƃ��ɂ͌��ʂ�f���Ȃ����ASpreadTree �� editor �͌��̏�ԂɁB -> �����F���� event �͂ǂ��Ƃ�H
 * usersNote �ɂ��K�p�ł���悤�ɂ���B
 * Edit �@�\��ǉ��F SpreadSelectionList �� load, folder �őI�ׂ�B�����őI�ׂ�B
 * folder �̕\���́H�F folder �ȉ��̈ꕔ���I�΂�Ă�����A�C�R����ς��邩�H��肷���H
 * SpraedTree ��ҏW�����ۂ�, �Ή����� SpreadSelectionList ��
 * �����I�ɕύX�����悤�ɂ���B
 * �܂��ASpreadSelectionTreee�� singleton �ł���K�v�͂Ȃ��B���́A
 * ActionListener ���A�K�؂� tree �� get ���邽�߂ɂ�������Ă��邪�AActionListener ���A
 * �������Atree �ɂ�����΁A�K�v�͂Ȃ��B
 */

package smart_gs.spread_selection;

/*
 * 
 Made from a source in
 Definitive Guide to Swing for Java 2, Second Edition
 By John Zukowski     
 ISBN: 1-893115-78-X
 Publisher: APress
 */

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.EventObject;
import java.util.Iterator;

import javax.swing.AbstractCellEditor;
import javax.swing.JCheckBox;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;

import smart_gs.logical.Spread;
import smart_gs.logical.SpreadDirectory;
import smart_gs.logical.SpreadTree;
import smart_gs.util.GSLog;

public class SpreadSelectionTree extends JTree {
	private DefaultMutableTreeNode rootNode;
	protected int zoneStartIndex = -1;
	protected EventObject current_event = null;
	private ArrayList<CheckBoxNode> checkBoxNodes = new ArrayList<CheckBoxNode>();

	public SpreadSelectionTree() {
		DefaultMutableTreeNode spreadTreeRootNode = SpreadTree.getInstance().getRootNode();
		rootNode = selectionTreeRootNode(spreadTreeRootNode);
		JTree tmpTree = new JTree(rootNode);
		this.setModel(tmpTree.getModel());
		((DefaultTreeModel)((JTree)this).getModel()).reload();
		CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
		this.setCellRenderer(renderer);
		this.setCellEditor(new CheckBoxNodeEditor(this));
		this.setEditable(true);
	}

//	returns the list of the indexes of the checked nodes
	public ArrayList<Integer> selectionList () {
		return (new SelectionListCollector()).collect();}
	
	public int getZoneStartIndex () {return this.zoneStartIndex;}
	public void setZoneStartIndex (int x) {this.zoneStartIndex = x;}
	public EventObject getCurrent_event () {return this.current_event;}
	public void setCurrent_event (EventObject x) {this.current_event = x;}
	protected void addCheckBoxNode(CheckBoxNode cbn) {checkBoxNodes.add(cbn);}

	class SelectionListCollector {
		ArrayList<Integer> selectionList = new ArrayList<Integer>();

		ArrayList<Integer> collect() {
			selectionList.clear();
			collectIndexsOfCheckedSpreads(rootNode);
			return selectionList;
		}

		void collectIndexsOfCheckedSpreads(DefaultMutableTreeNode parentNode) {
			Enumeration<DefaultMutableTreeNode> enumeration = parentNode.children();
			while (enumeration.hasMoreElements()) {
				DefaultMutableTreeNode childrenNode = (DefaultMutableTreeNode) enumeration
				.nextElement();
				Object usrObj = childrenNode.getUserObject();
				if (usrObj instanceof CheckBoxNode) {
					if (((CheckBoxNode) usrObj).isSelected()) {
						Integer index = ((CheckBoxNode) usrObj).getIndex();
						selectionList.add(index);
					}
				} else {
					collectIndexsOfCheckedSpreads(childrenNode);
				}
			}
		}
	}
	

		public DefaultMutableTreeNode getRootNode() {
			return rootNode;
		}

//		Construct a new tree model from the root node of a tree by replacing
//		every leaf node by a CheckBoxNode with the same index and name.
//		It returns the root node of the new tree model.
//		The new tree model is deeply copied. Thus it does not share any node with the old one.
		private DefaultMutableTreeNode selectionTreeRootNode(DefaultMutableTreeNode node) {
			Object usrObj = node.getUserObject();
			DefaultMutableTreeNode resultNode = null;
			if (usrObj instanceof Spread) {
				resultNode = new DefaultMutableTreeNode(new CheckBoxNode(this,((Spread) usrObj).toString(), 
						false,((Spread) usrObj).getPageViewIndex()));
			} else if (usrObj instanceof SpreadDirectory) {
				resultNode = new DefaultMutableTreeNode(((SpreadDirectory) usrObj).toString());				
				Enumeration<DefaultMutableTreeNode> enumeration = node.children();
				while(enumeration.hasMoreElements()){
					resultNode.add(selectionTreeRootNode((DefaultMutableTreeNode)(enumeration.nextElement())));
				}
			} else {
				GSLog.getInstance().debug("Illegal Spread Tree");
			}
			return resultNode;		
		}

		public void setAllZone(int startIndex, int endIndex, boolean selected) {
			int min, max;
			if (startIndex > endIndex) { min = endIndex+1;  max = startIndex; }
			else {min = startIndex; max = endIndex-1;}
			
			zoneStartIndex = -1;
			Iterator<CheckBoxNode> it = checkBoxNodes.iterator();
			while (it.hasNext()) {
				CheckBoxNode node = it.next();
				node.setZoneStart(false);
				int index = node.getIndex();
				if (min <= index && index <= max) {
					node.setSelected(selected);
				}
			}
			setCurrent_event(null);
		}
	}

	class CheckBoxNodeRenderer implements TreeCellRenderer {
		private JCheckBox leafRenderer = new JCheckBox();

		private DefaultTreeCellRenderer nonLeafRenderer = new DefaultTreeCellRenderer();

		Color selectionBorderColor, selectionForeground, selectionBackground, zoneStartBackground,	
		textForeground, textBackground;

		protected JCheckBox getLeafRenderer() {
			return leafRenderer;
		}

		public CheckBoxNodeRenderer() {
			Font fontValue;
			fontValue = UIManager.getFont("Tree.font");
			if (fontValue != null) {
				leafRenderer.setFont(fontValue);
			}
			Boolean booleanValue = (Boolean) UIManager
			.get("tree.drawsFocusBorderAroundIcon");
			leafRenderer.setFocusPainted((booleanValue != null)
					&& (booleanValue.booleanValue()));

			selectionBorderColor = UIManager.getColor("tree.selectionBorderColor");
			selectionForeground = UIManager.getColor("tree.selectionForeground");
			selectionBackground = UIManager.getColor("tree.selectionBackground");
			textForeground = UIManager.getColor("tree.textForeground");
			textBackground = UIManager.getColor("tree.textBackground");
			zoneStartBackground= Color.yellow;
		}

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

			if (! (tree0 instanceof SpreadSelectionTree)) {
				GSLog.getInstance().debug("SpreadSelectionTree.java error 1");
				return null;
			}
			
			SpreadSelectionTree tree = (SpreadSelectionTree)tree0;
			Component returnValue;
			
			if (leaf) {

				String stringValue = tree.convertValueToText(value, selected,
						expanded, leaf, row, false);
				leafRenderer.setText(stringValue);
				leafRenderer.setSelected(false);

				leafRenderer.setEnabled(tree.isEnabled());
				
                int index = -2;

				if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
					Object userObject = ((DefaultMutableTreeNode) value)
					.getUserObject();
					if (userObject instanceof CheckBoxNode) {
						CheckBoxNode node = (CheckBoxNode) userObject;
						index = node.getIndex();
						leafRenderer.setText(node.getText());
						leafRenderer.setSelected(node.isSelected());
					}
				}
				
				if (selected) {
					leafRenderer.setForeground(selectionForeground);
					leafRenderer.setBackground(selectionBackground);
				} else {
					leafRenderer.setForeground(textForeground);
					leafRenderer.setBackground(textBackground);
				}

				if (index == tree.zoneStartIndex) leafRenderer.setBackground(zoneStartBackground);
				
				returnValue = leafRenderer;
			} else {
				returnValue = nonLeafRenderer.getTreeCellRendererComponent(tree,
						value, selected, expanded, leaf, row, hasFocus);
			}
			return returnValue;
		}
	}

	class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {

		CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();

		SpreadSelectionTree tree;

		public CheckBoxNodeEditor(SpreadSelectionTree tree) {
			this.tree = tree;
		}

		public Object getCellEditorValue() {
			JCheckBox checkbox = renderer.getLeafRenderer();
			CheckBoxNode checkBoxNode = new CheckBoxNode(tree, checkbox.getText(),
					checkbox.isSelected(), Integer.valueOf(((checkbox.getText()).split(":",2))[0]));
			return checkBoxNode;
		}
		
		public Integer getCellEditorIndex() {
			JCheckBox checkbox = renderer.getLeafRenderer();
			return Integer.valueOf(((checkbox.getText()).split(":",2))[0]);
		}

		public boolean isCellEditable(EventObject event) {
			boolean returnValue = false;
			boolean shiftIsDown = false;

			if (event instanceof MouseEvent) {
				MouseEvent mouseEvent = (MouseEvent) event;
				shiftIsDown = (((MouseEvent) event).isShiftDown());
				TreePath path = tree.getPathForLocation(mouseEvent.getX(),
						mouseEvent.getY());
				if (path != null) {
					Object node = path.getLastPathComponent();
					if ((node != null) && (node instanceof DefaultMutableTreeNode)) {
						DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node;
						Object userObject = treeNode.getUserObject();
						CheckBoxNode checkBoxNode = null;
						if (userObject instanceof CheckBoxNode) {
							checkBoxNode = (CheckBoxNode)userObject;
							if (shiftIsDown) { 
								if (tree.getZoneStartIndex() == -1) {
									((CheckBoxNode)userObject).setZoneStart(true);
									tree.setZoneStartIndex(((CheckBoxNode)userObject).getIndex());
									tree.setCurrent_event(event);
								} else {
									tree.setAllZone(tree.getZoneStartIndex(),((CheckBoxNode)userObject).getIndex(),
											! ((CheckBoxNode)userObject).isSelected());
								}
							}
						} else {
							GSLog.getInstance().debug("Spread Selection List Bug 1");
							return false;
						}
						returnValue = ((treeNode.isLeaf()) && checkBoxNode != null);
					}
				}
			}
			return returnValue;
		}

		public Component getTreeCellEditorComponent(JTree tree, Object value,
				boolean selected, boolean expanded, boolean leaf, int row) {

			Component editor = renderer.getTreeCellRendererComponent(tree, value,
					true, expanded, leaf, row, true);
			
			final JCheckBox checkbox;
			
			if (! (editor instanceof JCheckBox)) {
				GSLog.getInstance().debug("The editor is not a JCheckBox");
				return null;
			} else {
				checkbox = (JCheckBox)editor;
			}

			// editor always selected / focused
			ItemListener itemListener = new ItemListener() {
				public void itemStateChanged(ItemEvent e) {
					if (stopCellEditing()) {
						fireEditingStopped();
					}
				}
			};
			if (editor instanceof JCheckBox) {
				checkbox.addItemListener(itemListener);
			}

			return editor;
		}
	}

	class CheckBoxNode {
		String text;
		boolean selected;
		int index;
		boolean isZoneStart = false;

		public CheckBoxNode(SpreadSelectionTree tree, String text, boolean selected, int index) {
			this.text = text;
			this.selected = selected;
			this.index = index;
			tree.addCheckBoxNode(this);
		}

		public boolean isSelected() {
			return selected;
		}

		public void setSelected(boolean newValue) {
			selected = newValue;
		}

		public boolean isZoneStart() {
			return isZoneStart;
		}

		public void setZoneStart(boolean x) {
			isZoneStart = x;
		}

		public String getText() {
			return text;
		}

		public void setText(String newValue) {
			text = newValue;
		}

		public int getIndex() {
			return index;
		}

		public void setIndex(int newValue) {
			index = newValue;
		}

		public String toString() {
			return getClass().getName() + "[" + text + "/" + selected + "]";
		}
	}
