/* $Id: UsersNoteTree.java 506 2012-06-01 08:23:43Z ohura $ */
package smart_gs.logical;

import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.dnd.DragSource;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;

import javax.swing.Icon;
import javax.swing.JOptionPane;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import smart_gs.logical.visitor.CreateXMLElementVisitor;
import smart_gs.reasoning_web.logical.LinkRemover;
import smart_gs.swingui.WorkspaceWindow;
import smart_gs.util.GSStandardPath;


@SuppressWarnings("serial")
public class UsersNoteTree extends JTree implements DragSourceListener, DropTargetListener, DragGestureListener {

	private static UsersNoteDirectory rootUsersNoteDirectory = new UsersNoteDirectory();
	private static UsersNoteTree singleton = new UsersNoteTree(rootUsersNoteDirectory);
	private List<UsersNote> registeredUsersNotes;
	private List<UsersNoteDirectory> registeredUsersNoteDirectories;
	private TreePath[] selectedPaths;
	private UsersNoteDirectory root;
	private DefaultMutableTreeNode rootNode;

	private static final String NAME = "UsersNoteTree";
	private static final DataFlavor localObjectFlavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType, NAME);
	private static final DataFlavor[] supportedFlavors = { localObjectFlavor };

	protected DragSource dragSource;
	protected DropTarget dropTarget;
	TreeNode dropTargetNode = null;
	TreeNode draggedNode = null;
	TreePath dragsourceTreePath = null;

	protected HashMap<String,String>  allowImageMap = new HashMap<String, String>();

	private UsersNoteTree(UsersNoteDirectory rootUsersNoteDirectory) {
		super(new DefaultMutableTreeNode(rootUsersNoteDirectory));

		rootUsersNoteDirectory.setName("root");
		
		this.root = rootUsersNoteDirectory;
		this.rootNode = (DefaultMutableTreeNode)super.getModel().getRoot();
		
		this.registeredUsersNotes = new ArrayList<UsersNote>();
		this.registeredUsersNoteDirectories = new ArrayList<UsersNoteDirectory>();
		this.registeredUsersNoteDirectories.add(rootUsersNoteDirectory);

		this.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
		setCellRenderer(new UsersNoteTreeCellRenderer());
		dragSource = new DragSource();
		dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this);
		dropTarget = new DropTarget(this, this);
	}

	public static UsersNoteTree getInstance() {
		return singleton;
	}

	public List<UsersNote> getUsersNotes() {
		return this.registeredUsersNotes;
	}

	public void registorUsersNote(UsersNote usersNote) {
		this.registeredUsersNotes.add(usersNote);
	}
	
	public TreePath[] getSelectedPaths() {
		return this.getSelectionModel().getSelectionPaths();
	}
	
	public UsersNoteDirectory getRootUsersNoteDirectory(){
		return root;
	}

	public UsersNoteDirectory getCurrentDirectory() {
		TreePath path = this.getSelectionModel().getSelectionPath();
		if (path == null) {
			return (UsersNoteDirectory) ((DefaultMutableTreeNode) this.getModel()
					.getRoot()).getUserObject();
		}
		Object selectedObject = ((DefaultMutableTreeNode) path
				.getLastPathComponent()).getUserObject();
		while (!(selectedObject instanceof UsersNoteDirectory)) {
			path = path.getParentPath();
			selectedObject = ((DefaultMutableTreeNode) path
					.getLastPathComponent()).getUserObject();
		}
		return (UsersNoteDirectory) selectedObject;
	}

	public DefaultMutableTreeNode getSelectedNode() {
		TreePath path = this.getSelectionModel().getSelectionPath();
		if (path == null) {
			return this.rootNode;
		}
		return (DefaultMutableTreeNode) path.getLastPathComponent();
	}

//	private UsersNoteDirectory getDirectory(File file) {
//		for (int i = 0; i < this.registeredUsersNoteDirectories.size(); i++) {
//			if (this.registeredUsersNoteDirectories.get(i).getFile().equals(file)) {
//				return this.registeredUsersNoteDirectories.get(i);
//			}
//		}
//		return null;
//	}

	public void updateModel() {
		this.root.setTree(this.rootNode);
		((DefaultTreeModel) this.getModel()).setRoot(rootNode);
		((DefaultTreeModel) this.getModel()).reload();
	}

	public DefaultMutableTreeNode getRootNode(){
		return rootNode;
	}

	public void restore(Element element) {
		NodeList list = element.getElementsByTagName("usersNoteDirectory");
		Element rootElement = (Element) list.item(0);
		this.registeredUsersNotes.clear();
		this.registeredUsersNoteDirectories.clear();
		this.rootNode.removeAllChildren();
		this.root = new UsersNoteDirectory();
		String name = rootElement.getAttribute("name");
		this.root.setName(name);
		this.rootNode.setUserObject(this.root);
		this.root.restore(rootElement);
		registeredUsersNotes = root.getUsersNotes();
		registeredUsersNoteDirectories = root.getUsersNoteDirectories();
		this.updateModel();
	}

	public void save(Document document, Element parent) {
		Element element = document.createElement("usersNoteTree");
		parent.appendChild(element);
		CreateXMLElementVisitor visitor = new CreateXMLElementVisitor(document);
		this.root.accept(visitor);
		element.appendChild(visitor.getLastElement());
	}

	public void registorUsersNoteDirectory(UsersNoteDirectory directory) {
		this.registeredUsersNoteDirectories.add(directory);
	}

	public UsersNote getUsersNoteByURI(String uri) {
		for (int i = 0; i < this.registeredUsersNotes.size(); i++) {
			UsersNote note = this.registeredUsersNotes.get(i);
			if (uri.equals(note.getURI())){
				return this.registeredUsersNotes.get(i);
			}
		}
		return null;
	}

	public UsersNote getUsersNoteByURI2(String uri) {
		for (int i = 0; i < this.registeredUsersNotes.size(); i++) {
			String srcurl = registeredUsersNotes.get(i).getURI();
			srcurl = GSStandardPath.toStandardOnCurrentOS(srcurl);
			if (srcurl.indexOf(uri) != -1) {
				return registeredUsersNotes.get(i);
			}
		}
		return null;
	}
	
	

	
	public void export(Writer writer) throws IOException{
		for (int i = 0; i < this.registeredUsersNotes.size(); i++) {
			UsersNote usersNote = this.registeredUsersNotes.get(i);
			boolean isBr = false;
//			// FirstIDAT
//			{
//				String xmlstr = usersNote.getFirstIDAT().getSource();
//				xmlstr = StringUtils.replaceString(xmlstr, "<br>", "\n");
//				Pattern pattern = Pattern.compile("<.+?>", Pattern.DOTALL);
//				Matcher matcher = pattern.matcher(xmlstr);
//				xmlstr = matcher.replaceAll("");
//				xmlstr = xmlstr.trim();
//				if(!xmlstr.trim().equals("")){
//					writer.write(xmlstr);
//					writer.write("\n");
//					isBr = true;
//				}
//			}
//			// SecondIDAT
//			{
//				String xmlstr = usersNote.getSecondIDAT().getSource();
//				xmlstr = StringUtils.replaceString(xmlstr, "<br>", "\n");
//				Pattern pattern = Pattern.compile("<.+?>", Pattern.DOTALL);
//				Matcher matcher = pattern.matcher(xmlstr);
//				xmlstr = matcher.replaceAll("");
//				xmlstr = xmlstr.trim();
//				if(!xmlstr.trim().equals("")){
//					writer.write(xmlstr);
//					writer.write("\n");
//					isBr = true;
//				}
//			}
//			// ThirdIDAT
//			{
//				String xmlstr = usersNote.getThirdIDAT().getSource();
//				xmlstr = StringUtils.replaceString(xmlstr, "<br>", "\n");
//				Pattern pattern = Pattern.compile("<.+?>", Pattern.DOTALL);
//				Matcher matcher = pattern.matcher(xmlstr);
//				xmlstr = matcher.replaceAll("");
//				xmlstr = xmlstr.trim();
//				if(!xmlstr.trim().equals("")){
//					writer.write(xmlstr);
//					writer.write("\n");
//					isBr = true;
//				}
//			}
//			if(isBr){
//				writer.write("\n");
//			}
		}
	}
	// ADD NorthGrid 以下D&Dイベント
	/**
	 * ドラッグ＆ドロップ操作が完了.
	 * @param dsde
	 */
	public void dragDropEnd(DragSourceDropEvent dsde) {
		dropTargetNode = null;
		draggedNode = null;
		dragsourceTreePath = null;
		// 再表示
		WorkspaceWindow.setUpdated(true);
		repaint();
	}
	/**
	 * カーソルのホットスポットがプラットフォーム依存のドロップサイトに入力された.
	 * @param dsde
	 */
	public void dragEnter(DragSourceDragEvent dsde) {
		dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
	}
	/**
	 * カーソルのホットスポットがプラットフォーム依存のドロップサイトを終了したとき.
	 * @param dse
	 */
	public void dragExit(DragSourceEvent dse) {
		dse.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
	}
	/**
	 * ドロップサイトに移動.
	 * @param dsde
	 */
	public void dragOver(DragSourceDragEvent dsde) {
		//shayashi 2011.03.18
		selectedPaths = null;
		selectedPaths = getSelectedPaths();

	}
	/**
	 * ジェスチャーを変更.
	 * @param dsde
	 */
	public void dropActionChanged(DragSourceDragEvent dsde) {}
	/**
	 * ドラックが受け入れたとき.
	 * @param dtde
	 */
	public void dragEnter(DropTargetDragEvent dtde) {
		dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
	}
	/**
	 * ドラッグ終了.
	 * @oaram dte
	 */
	public void dragExit(DropTargetEvent dte) {}
	/**
	 * ドラッグ中の処理.
	 * @prama dtde
	 */
	public void dragOver(DropTargetDragEvent dtde) {
		DataFlavor[] f = dtde.getCurrentDataFlavors();
		boolean isDataFlavorSupported = f[0].getHumanPresentableName().equals(NAME);
		if(!isDataFlavorSupported) {
			//サポートされていないDataFlavorである(例えばデスクトップからファイルなど)
			rejectDrag(dtde);
			return;
		}
		Point pt = dtde.getLocation();
		TreePath path = getPathForLocation(pt.x, pt.y);
		Rectangle r2 = new Rectangle(pt.x, pt.y, 10, 10);
		Rectangle r1 = new Rectangle(pt.x-10, pt.y-10, 10, 10);
		scrollRectToVisible(r1);
		scrollRectToVisible(r2);
		//      end
		if(path==null) {
			//ノード以外の場所である(例えばJTreeの余白など)
			rejectDrag(dtde);
			return;
		}
		Object droppedObject;
		try {
			droppedObject = dtde.getTransferable().getTransferData(localObjectFlavor);
		}catch(Exception ex) {
			rejectDrag(dtde);
			return;
		}
		// 別ディレクトリへの移動は禁止
		if(path==null || !path.getParentPath().toString().equals(dragsourceTreePath.getParentPath().toString())){
			rejectDrag(dtde);
			return;
		}

		
		selectedPaths = null;
		selectedPaths = getSelectedPaths();
//		2011.03.20, 27 shaysahi: checking if all the selected items are in the same directory.
		if (! areSelectedPathsInSameNode(selectedPaths)) {
			rejectDrag(dtde);
			return;
		}
		

		DefaultMutableTreeNode droppedNode = (DefaultMutableTreeNode) droppedObject;
		DefaultMutableTreeNode parentOfDroppedNode = (DefaultMutableTreeNode) droppedNode.getParent();
		DefaultMutableTreeNode targetNode = (DefaultMutableTreeNode) path.getLastPathComponent();
		DefaultMutableTreeNode parentOfTargetNode = (DefaultMutableTreeNode) targetNode.getParent();


		while(parentOfTargetNode!=null) {
			if(droppedNode.equals(parentOfTargetNode)) {
				//親ノードを子ノードにドロップしようとしている
				rejectDrag(dtde);
				return;
			}
			parentOfTargetNode = (DefaultMutableTreeNode)parentOfTargetNode.getParent();
		}
		dropTargetNode = targetNode;
		dtde.acceptDrag(dtde.getDropAction());
		repaint();
	}

	private boolean areSelectedPathsInSameNode(TreePath[] selectedPaths2) {
		if (selectedPaths != null){
			int size = selectedPaths.length;
			if (size != 0){
				DefaultMutableTreeNode parentOfFirstSelectedNode = 
					(DefaultMutableTreeNode) selectedPaths[0].getParentPath().getLastPathComponent();
				for (int i = 1; i<size;i++){
					DefaultMutableTreeNode parentOfSelectedNode = 
						(DefaultMutableTreeNode) selectedPaths[i].getParentPath().getLastPathComponent();
					if (!parentOfSelectedNode.equals(parentOfFirstSelectedNode)){
						return false;
					}
				}
			}
		}
		return true;
	}

	/**
	 *  DropTarget へのドロップで、ドラッグ操作が終了.
	 *  @param dtde
	 */
	public void drop(DropTargetDropEvent dtde) {
		Object droppedObject;
		//shayashi 2011.03.18, 03.20
		try {
			droppedObject = dtde.getTransferable().getTransferData(localObjectFlavor);
		}catch(Exception e) {
			e.printStackTrace();
			dtde.dropComplete(false);
			return;
		}
		DefaultTreeModel model = (DefaultTreeModel) getModel();
		Point p = dtde.getLocation();
		TreePath targetPath = getPathForLocation(p.x, p.y);
		if(targetPath==null || !(droppedObject instanceof MutableTreeNode)) {
			dtde.dropComplete(false);
			return;
		}
		DefaultMutableTreeNode droppedNode = (DefaultMutableTreeNode) droppedObject;

		DefaultMutableTreeNode targetNode = (DefaultMutableTreeNode) targetPath.getLastPathComponent();
		DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) targetNode.getParent();
		if(targetNode.equals(droppedNode)) {
			//自分を自分にはドロップ不可
			dtde.dropComplete(false);
			return;
		}
		
		// accept to drop
		dtde.acceptDrop(DnDConstants.ACTION_MOVE);
		
//		2011.03.16 shayashi changed BEGIN
//		Process for the drop
//		Check if the droppedNode is selected or not, and memorize the smaller and larger node's indexes.
		TreePath pathOfDroppedNode = new TreePath(droppedNode.getPath());
		int size=-1;
		int droppedNodeIndexInSelection;
		boolean droppedNodeIsSelected;
		if (selectedPaths == null) {
			droppedNodeIsSelected = false;
		} else {
			size= selectedPaths.length;
			droppedNodeIsSelected = false;
			for (int i=0;i<size;i++){
				if (pathOfDroppedNode.equals(selectedPaths[i])) {
					droppedNodeIsSelected = true;
					droppedNodeIndexInSelection = i;
					break;
				}
			}
		}
		List<GSResource> resources = ((UsersNoteDirectory)parentNode.getUserObject()).getResources();
		int droppedNodeIndex = model.getIndexOfChild(parentNode, droppedNode);
		int targetNodeIndex = model.getIndexOfChild(parentNode, targetNode);
		if (droppedNodeIsSelected == false || size == 1){
			model.removeNodeFromParent(droppedNode);
			GSResource elm = resources.get(droppedNodeIndex);
			resources.remove(droppedNodeIndex);
//					0 is a meaningless default value.
			int insertionPointIndex = 0;
			if (droppedNodeIndex < targetNodeIndex) {
				insertionPointIndex = targetNodeIndex-1;
			} else if (droppedNodeIndex > targetNodeIndex){
				insertionPointIndex = targetNodeIndex;
			} 
			resources.add(insertionPointIndex,elm);
			model.insertNodeInto(droppedNode, parentNode, insertionPointIndex);
//			Select droppedNode to make it stand out.
			setSelectionPath(pathOfDroppedNode);
//			finally set the new UsersNoteDirectory
			((UsersNoteDirectory)parentNode.getUserObject()).setResources(resources);
		} else {
			int insertPointIndex=targetNodeIndex;
			MutableTreeNode[] tmpForJTree = new MutableTreeNode[size];
			GSResource[] tmpForUsersNoteTree = new GSResource[size];
//			copy selected nodes to tmpForTree/tmpForUsersNoteTree buffers.
			for (int i=0;i<size;i++){
				MutableTreeNode selectedNode = (MutableTreeNode)selectedPaths[i].getLastPathComponent();
				tmpForJTree[i] = selectedNode;
				tmpForUsersNoteTree[i] = resources.get(model.getIndexOfChild(parentNode, selectedNode));
			}
			int insertionPointIndex=-1;
//			remove selected items from children of parentNode and resource, and adjust the insertionPointIndex.
			int offset =0;
			for (int i=0;i<size;i++){
				insertionPointIndex = targetNodeIndex;
				MutableTreeNode selectedNode = (MutableTreeNode)selectedPaths[i].getLastPathComponent();
				int selectedNodeIndex = 
					model.getIndexOfChild(selectedPaths[i].getParentPath().getLastPathComponent(),selectedNode);	
				GSResource elm = resources.get(selectedNodeIndex);
				model.removeNodeFromParent(selectedNode);
				resources.remove(selectedNodeIndex);
				if (selectedNodeIndex < insertionPointIndex) {
					offset++;
				} else if (selectedNodeIndex >= insertionPointIndex){
					//insertionPointIndex = insertionPointIndex;
				} 
			}
			insertionPointIndex = insertionPointIndex-offset;
//			for debug 2011.03.23 shayashi
//			System.out.printf("insertPointIndex:%d\n",insertionPointIndex);
//			int bound = model.getChildCount(selectedPaths[0].getParentPath().getLastPathComponent());
//			for (int i = 0; i<bound;i++) System.out.printf("%d:%s\n", i,model.getChild(selectedPaths[0].getParentPath().getLastPathComponent(),i));
//			System.out.printf("%d\n",insertionPointIndex);
			//			insert items of two buffers to JTree and UsersNote/UsersNoteDirectory graph-tree.
			for (int i=size-1;i>=0;i--){
				resources.add(insertionPointIndex,tmpForUsersNoteTree[i]);
				model.insertNodeInto(tmpForJTree[i], parentNode ,insertionPointIndex);
			}
//			Select the dropped(dragged) nodes to make them stand out.
			setSelectionPaths(selectedPaths);
//			finally set the new UsersNoteDirectory
			((UsersNoteDirectory)parentNode.getUserObject()).setResources(resources);
		}

		// MoveTooBarをアップデートする
		WorkspaceWindow.getInstance().updateMoveToolBar();
        // updated is marked
		WorkspaceWindow.setUpdated(true);
		repaint();
        //		drop event ends
		dtde.dropComplete(true);
	}
//		2011.03.16 shayashi changed END

	/**
	 * ジェスチャーを変更.
	 * @param dtde
	 */
	public void dropActionChanged(DropTargetDragEvent dtde) {
       // NOP 
	}

	/**
	 * D&Dの操作を開始する。
	 * @param dge ドラッグ開始ジェスチャーを検出イベント
	 */
	public void dragGestureRecognized(DragGestureEvent dge) {
		Point clickPoint = dge.getDragOrigin();
		TreePath path = getPathForLocation(clickPoint.x, clickPoint.y);
		if(path==null || path.getParentPath()==null) {
			return;
		}
		dragsourceTreePath = path;
		draggedNode = (TreeNode) path.getLastPathComponent();
		if(draggedNode instanceof DefaultMutableTreeNode){
			// TODO the packege would not be correct check SpreaTreeTransferble
			Transferable trans = new UsersNoteTreeTransferable(draggedNode);
			dragSource.startDrag(dge,Cursor.getDefaultCursor(),	trans, this);
		}
	}
	/**
	 * ドラックの停止.
	 * @param dtde
	 */
	private void rejectDrag(DropTargetDragEvent dtde) {
		dtde.rejectDrag();
		// dropTargetNode(flag)をnull
		dropTargetNode = null;
		//JTree描画
		repaint();
	}
	/**
	 * 
	 * データ変換クラス.
	 * アプリ以外からのドラックをチェックする.
	 */
	class UsersNoteTreeTransferable implements Transferable {
		Object object;
		public UsersNoteTreeTransferable(Object o) {
			object = o;
		}
		//@Override
		public Object getTransferData(DataFlavor df) throws UnsupportedFlavorException, IOException {
			if(isDataFlavorSupported(df)) {
				return object;
			}else{
				throw new UnsupportedFlavorException(df);
			}
		}
		//@Override
		public boolean isDataFlavorSupported(DataFlavor df) {
			return (df.getHumanPresentableName().equals(NAME));
		}
		//@Override
		public DataFlavor[] getTransferDataFlavors() {
			return supportedFlavors;
		}
	}
	/**
	 * 
	 * Rendererクラス
	 * D&D中の操作をラインを付加して表示する.
	 *
	 */
	class UsersNoteTreeCellRenderer
	extends DefaultTreeCellRenderer {
		boolean isTargetNode;
		boolean isTargetNodeLeaf;
		boolean isLastItem;
		Insets normalInsets, lastItemInsets;
		int BOTTOM_PAD = 30;
		public UsersNoteTreeCellRenderer() {
			super();
			normalInsets = super.getInsets();
			lastItemInsets =
				new Insets(normalInsets.top,
						normalInsets.left,
						normalInsets.bottom + BOTTOM_PAD,
						normalInsets.right);
		}
		public Component getTreeCellRendererComponent(JTree tree,
				Object value,
				boolean isSelected,
				boolean isExpanded,
				boolean isLeaf,
				int row,
				boolean hasFocus) {
			isTargetNode = (value == dropTargetNode);
			isTargetNodeLeaf = (isTargetNode &&
					((TreeNode)value).isLeaf());
			// isLastItem = (index == list.getModel().getSize()-1);
			boolean showSelected = isSelected & (dropTargetNode == null);
			DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer)super.getTreeCellRendererComponent(tree, value, isSelected, isExpanded,isLeaf, row, hasFocus);
			if(value != null){
				if(value instanceof DefaultMutableTreeNode){
					DefaultMutableTreeNode defaultMutableTreeNode = (DefaultMutableTreeNode)value;
					Object obj = defaultMutableTreeNode.getUserObject();
					renderer.setText(((GSResource)obj).getName());
					if(obj != null){
						if(obj instanceof UsersNoteDirectory){
							Icon icon = renderer.getOpenIcon();
							renderer.setIcon(icon);
						}
					}
				}
			}
			return renderer;
		}

//		2011.03.16 shayashi changed: moving items always underlined
		public void paintComponent(Graphics g) {
			super.paintComponent(g);
			if (isTargetNode) {
				g.setColor(Color.black);
				g.drawLine(0, 0, getSize().width, 0);
			}
		}
	}

//	class UsersNoteFileComparator implements Comparator<File>{
//
//		public int compare(File o1, File o2) {
//			return o1.compareTo(o2);
//		}
//	}

	public void sortSelectedItemsByName() {
		DefaultTreeModel model = (DefaultTreeModel) this.getModel();
		selectedPaths = null;
		selectedPaths = getSelectedPaths();
		int size = selectedPaths.length;
		if (size == 0) return;
//		TODO remove this condition!!!!!!!!!!!!!!!
		if (! areSelectedPathsInSameNode(selectedPaths)){
			JOptionPane.showMessageDialog(singleton, "Items must be in the same directory");
			return;
		}
		DefaultMutableTreeNode parentNode = 
			(DefaultMutableTreeNode) selectedPaths[0].getParentPath().getLastPathComponent();
		List<GSResource> resources = ((UsersNoteDirectory)parentNode.getUserObject()).getResources();
		
//		get all selected items' indexes, sort them, and its smallest, which is insertionPointIndex.
		int[] indexes = new int[size];
		for (int i=0;i<size;i++) {
			indexes[i] = parentNode.getIndex((DefaultMutableTreeNode) selectedPaths[i].getLastPathComponent());				
		}
		Arrays.sort(indexes);
		for (int i=0;i<size-1;i++) {
			if (indexes[i+1] > indexes[i]+1){
				JOptionPane.showMessageDialog(singleton, "Items must be consective:"+indexes[i]+","+indexes[i+1]+"\n");
				return;
			}
		}
		int insertionPointIndex = indexes[0];
		
//		Two buffers
		MutableTreeNode[] tmpForJTree = new MutableTreeNode[size];
		GSResource[] tmpForUsersNoteTree = new GSResource[size];
//		copy selected nodes to tmpForTree/tmpForUsersNoteTree buffers, and sort them.
		for (int i=0;i<size;i++){
			MutableTreeNode selectedNode = (MutableTreeNode)selectedPaths[i].getLastPathComponent();
			tmpForJTree[i] = selectedNode;
			tmpForUsersNoteTree[i] = resources.get(model.getIndexOfChild(parentNode, selectedNode));
		};
		size = size;
		Arrays.sort(tmpForJTree,new DefaultMutableTreeNodeNameComparator());
		Arrays.sort(tmpForUsersNoteTree,new GSResourceNameComparator());
		
		//		remove selected items from the children of parentNode and resource.
		for (int i=size-1;i>=0;i--){
			parentNode.remove(insertionPointIndex+i);
			resources.remove(insertionPointIndex+i);
		}

//		for debug 2011.03.23 shayashi
//		System.out.printf("insertPointIndex:%d\n",insertionPointIndex);
//		int bound = model.getChildCount(selectedPaths[0].getParentPath().getLastPathComponent());
//		for (int i = 0; i<bound;i++) System.out.printf("%d:%s\n", i,model.getChild(selectedPaths[0].getParentPath().getLastPathComponent(),i));
//		System.out.printf("%d\n",insertionPointIndex);
		//			insert items of two buffers to JTree and UsersNote/UsersNoteDirectory graph-tree.
		for (int i=size-1;i>=0;i--){
			resources.add(insertionPointIndex,tmpForUsersNoteTree[i]);
			model.insertNodeInto(tmpForJTree[i], parentNode ,insertionPointIndex);
		}
//		finally set the new UsersNoteDirectory
		((UsersNoteDirectory)parentNode.getUserObject()).setResources(resources);
		
//		redraw
		WorkspaceWindow.setUpdated(true);
		updateUI();
		repaint();
	}

	
//	Making or adding UsersNotes, UsersNoteDirectories.
	
	// Make a node&UsersNoteDirectory under the parent pair of parentTreeNode&parentUsersNoteDir with the name "name".
	// It returns the "node" made. If the given name is already used in the parent node, it returns null.
	public DefaultMutableTreeNode makeUsersNoteDirectory(DefaultMutableTreeNode parentTreeNode,String name){
		Enumeration<DefaultMutableTreeNode> enm = parentTreeNode.children();
		while (enm.hasMoreElements()){
			String tmp = ((GSResource)(enm.nextElement()).getUserObject()).getName();
			if (tmp.equals(name)) {
				JOptionPane.showMessageDialog(null, String.format("Error: name %s already used in %s\n.",tmp, makePathString(parentTreeNode.getPath())));
				return null;
			}
		}
		UsersNoteDirectory parentUsersNoteDir = (UsersNoteDirectory) parentTreeNode.getUserObject();

		UsersNoteDirectory directory = new UsersNoteDirectory(parentUsersNoteDir,name);
		directory.setName(name);
		this.registeredUsersNoteDirectories.add(directory);
		parentUsersNoteDir.addResource(directory);
		DefaultMutableTreeNode directoryNode = new DefaultMutableTreeNode(directory);
		parentTreeNode.add(directoryNode);
		WorkspaceWindow.setUpdated(true);
		this.updateUI();
		repaint();
		return directoryNode;
	}
	

	public DefaultMutableTreeNode makeUsersNote(DefaultMutableTreeNode parentTreeNode,String noteName){
		Enumeration<DefaultMutableTreeNode> enm = parentTreeNode.children();
		while (enm.hasMoreElements()){
			String tmp = ((GSResource)(enm.nextElement()).getUserObject()).getName();
			if (tmp.equals(noteName)) {
				JOptionPane.showMessageDialog(null, String.format("Error: name %s already used in %s\n.",tmp, makePathString(parentTreeNode.getPath())));
				return null;
			}
		}
		UsersNoteDirectory parentUsersNoteDir = (UsersNoteDirectory) parentTreeNode.getUserObject();
		UsersNote note = new UsersNote(parentUsersNoteDir,noteName);
		this.registeredUsersNotes.add(note);
		parentUsersNoteDir.addResource(note);
		DefaultMutableTreeNode noteNode = new DefaultMutableTreeNode(note);
		parentTreeNode.add(noteNode);
		this.updateUI();
		repaint();
		WorkspaceWindow.setUpdated(true);
		return noteNode;
	}

	public static String makePathString(TreeNode[] array_path) {
		StringBuffer tmp = new StringBuffer();
		int size = array_path.length;
		for (int i=0; i<size;i++){
			tmp.append("/");
			tmp.append(array_path[i]);
		}
		return tmp.toString();
	}

//	Making UsersNotes, UsersNoteDirectories.
	
//	Making UsersNotes, UsersNoteDirectories..
	
	
//	Removing UsersNotes, UsersNoteDirectories.
	
//	2011.03.15 shayashi, delete selected items in UsersNoteTree using
	//	removeUsersNote and removeUsersNotedirectory
	public void removeSelectedItems() {
		TreePath[] paths = getSelectedPaths();
		int size = paths.length;
		// delete items from ImageTree 
		for (int i=0; i<size; i++){
			GSResource item = (GSResource)((DefaultMutableTreeNode)paths[i].getLastPathComponent()).getUserObject();
			if (item == null) {
				return;
			} else if (item instanceof UsersNote) {
				removeUsersNote(paths[i]) ;
			} else if (item instanceof UsersNoteDirectory) {
				removeDirectory(paths[i]);
			} else {
				System.out.print("Bug in UsersNoteTree");
			}
			//post processing
			WorkspaceWindow.setUpdated(true);
			repaint();	
		}
	}

	//	2011.03.15 shayashi
	//	deletes a UsersNote by removeUsersNote, removeDirectory
	//	this calls AutomaticNumberting and execAndAdjustUsersNoteSelectionLists
	public void removeUsersNote(TreePath path){
		if (path == null) {return;}
		Object obj = path.getLastPathComponent();
		if (obj == null) {return;}
			
		UsersNote deleteUsersNote = (UsersNote)((DefaultMutableTreeNode)obj).getUserObject();
		if (deleteUsersNote == null) {return;}
		String nameStr = deleteUsersNote.toString();
		
		DefaultMutableTreeNode targetNode = (DefaultMutableTreeNode) path.getLastPathComponent();
		DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) targetNode.getParent();

		int smallest = Integer.MAX_VALUE;
		if(nameStr.indexOf(":") != -1){
			String numberStr = nameStr.substring(0,nameStr.indexOf(":"));
			try{
				smallest  = Integer.parseInt(numberStr);
			}catch(NumberFormatException e){}
		}
		
		//	This loop finds the index of the UsersNote to be deleted in the children index list of the parent node.
		//	If it is found, then deleteflg is ture and the index to find is the value of deletecount
		Enumeration enumeration = parentNode.children();
		UsersNoteDirectory UsersNoteDirectory = (UsersNoteDirectory)parentNode.getUserObject();
		int deletecount = 0;
		boolean deleteflg = false;
		while(enumeration.hasMoreElements()){
			DefaultMutableTreeNode childrenNode = (DefaultMutableTreeNode)enumeration.nextElement();
			String childrenStr = childrenNode.toString();
			if(childrenNode.getUserObject() instanceof UsersNote){
				if(childrenStr.equals(nameStr)){
					deleteflg = true;
					removeLink(((UsersNote)childrenNode.getUserObject()).getURI());
					registeredUsersNotes.remove((UsersNote)childrenNode.getUserObject());
					UsersNoteDirectory.remove((UsersNote)childrenNode.getUserObject());
					break;
				}
			}
			deletecount++;
		}
		if(deleteflg){
			parentNode.remove(deletecount);
			WorkspaceWindow.setUpdated(true);
			this.updateUI();
		}else{
			return;
		}
		WorkspaceWindow.setUpdated(true);
		this.repaint();
	}


	public void removeDirectory(TreePath path){
		if (path == null) return;
		Object obj = path.getLastPathComponent();
		if (obj == null) return;
		UsersNoteDirectory deleteDirectory = (UsersNoteDirectory)((DefaultMutableTreeNode)obj).getUserObject();
		if (deleteDirectory == null) return;
		List<UsersNoteDirectory> listdir =  deleteDirectory.getUsersNoteDirectories();
		for(int i=0;i<listdir.size();i++){
			UsersNoteDirectory usersNoteDirectory = (UsersNoteDirectory)listdir.get(i);
			removeLink(usersNoteDirectory.getURI());
			registeredUsersNoteDirectories.remove(usersNoteDirectory);
		}
		List<UsersNote> listnote = deleteDirectory.getUsersNotes();
		for(int i=0;i<listnote.size();i++){
			UsersNote usersNote = (UsersNote)listnote.get(i);
			removeLink(usersNote.getURI());
			registeredUsersNotes.remove(usersNote);
		}
		deleteDirectory.removeAllChildren();
		registeredUsersNoteDirectories.remove(deleteDirectory);
		removeLink(deleteDirectory.getURI());
		DefaultMutableTreeNode targetNode = (DefaultMutableTreeNode) path.getLastPathComponent();
		DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) targetNode.getParent();
		Object  parentObject = parentNode.getUserObject();
		((UsersNoteDirectory)parentObject).removeDirectory(deleteDirectory);
		parentNode.remove(targetNode);
		WorkspaceWindow.setUpdated(true);
		this.updateUI();
		repaint();
	}

	private void removeLink(String url){
		LinkRemover.removeLinksOf(url);
	}

	public void clear() {
		this.registeredUsersNotes.clear();
		this.registeredUsersNoteDirectories.clear();
		this.rootNode.removeAllChildren();
		this.root.clear();
		
		this.updateUI();
		this.repaint();
	}


	public void changeNodeName(DefaultMutableTreeNode node, String newName) {
		((GSResource)(node.getUserObject())).setName(newName);
		WorkspaceWindow.setUpdated(true);
		this.updateUI();
		this.repaint();
	}

	public UsersNote getUsersNoteByTextSegmentURI(String uri) {
		String usersNoteURI = URIObject.getContainerURIFrom(uri);
		for (int i = 0; i < this.registeredUsersNotes.size(); i++) {
			if (this.registeredUsersNotes.get(i).getURI().equals(usersNoteURI)) {
				return this.registeredUsersNotes.get(i);
			}
		}
		return null;
	}



//	End of Removing UsersNotes, UsersNoteDirectories.	
	
	//	End of UsersNoteTree Class
}

