package leaf.ui;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

import com.evolute.utils.strings.StringPlainer;

public class TreeTools
{

	private static final Comparator<DefaultMutableTreeNode> comparator = new Comparator<DefaultMutableTreeNode>()
	{
		
		private int compareObjects( Object o1, Object o2 )
		{
			if( o1 == null )
			{
				return o2 == null ? 0 : -1;
			}
			else
			{
				return o2 == null ? 1 : StringPlainer.convertString( o1.toString() ).compareTo( StringPlainer.convertString( o2.toString() ) );
			}
		}
		
		@Override
		public int compare( DefaultMutableTreeNode o1, DefaultMutableTreeNode o2 )
		{
			if( o1 == null )
			{
				return o2 == null ? 0 : -1;
			}
			else
			{
				return o2 == null ? 1 : compareObjects( o1.getUserObject(), o2.getUserObject() );
			}
		}
	};
	
	public static void refreshTree( JTree tree, DefaultMutableTreeNode node, boolean expand )
	{
		((DefaultTreeModel) tree.getModel()).nodeStructureChanged( node );
		if( expand )
		{
			for( int i = 0; i < tree.getRowCount(); ++i)
			{
				tree.expandPath( tree.getPathForRow( i ) );
			}
		}
	}
	
	public static boolean userObjectExistsOn( Object object, DefaultMutableTreeNode on )
	{
		boolean result = false;
		if( object != null && on != null )
		{
			result = object.equals( on.getUserObject() );
			for( int i = 0; i < on.getChildCount() && !result; ++i )
			{
				result |= userObjectExistsOn( object, (DefaultMutableTreeNode) on.getChildAt( i ) );
			}
		}
		return result;
	}

	public static DefaultMutableTreeNode findNodeWithUserObject( Object object, DefaultMutableTreeNode on )
	{
		DefaultMutableTreeNode result = null;
		if( object != null && on != null )
		{
			result = object.equals( on.getUserObject() ) ? on : null;
			for( int i = 0; i < on.getChildCount() && result == null; ++i )
			{
				result = object.equals(((DefaultMutableTreeNode) on.getChildAt( i )).getUserObject()) ? (DefaultMutableTreeNode) on.getChildAt( i ) : findNodeWithUserObject( object, (DefaultMutableTreeNode) on.getChildAt( i ) );
			}
		}
		return result;
	}

	public static DefaultMutableTreeNode cloneFullNode( DefaultMutableTreeNode node )
	{
		DefaultMutableTreeNode result = (DefaultMutableTreeNode) node.clone();
		for( int i = 0; i < node.getChildCount(); ++i )
		{
			DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt( i );
			result.add( cloneFullNode(child) );
		}
		return result;
	}
	
	public static void merge( DefaultMutableTreeNode with, DefaultMutableTreeNode what )
	{
		if( what != null )
		{
			for( int i = 0; i < what.getChildCount(); ++i )
			{
				DefaultMutableTreeNode child = (DefaultMutableTreeNode) what.getChildAt( i );
				DefaultMutableTreeNode exists = TreeTools.findNodeWithUserObject( child.getUserObject(), with );
				if( exists == null )
				{
					with.add( TreeTools.cloneFullNode( child ) );
				}
				else
				{
					merge( exists, child );
				}
			}
		}
	}

	public static void removeAll( DefaultMutableTreeNode from, DefaultMutableTreeNode what )
	{
		for( int i = 0; i < what.getChildCount(); ++i )
		{
			DefaultMutableTreeNode child = (DefaultMutableTreeNode) what.getChildAt( i );
			removeAll( from, child );
		}
		DefaultMutableTreeNode exists = TreeTools.findNodeWithUserObject( what.getUserObject(), from );
		if( exists != null )
		{
			exists.removeFromParent();
		}
	}

	public static void remove( DefaultMutableTreeNode from, DefaultMutableTreeNode what )
	{
		for( int i = 0; i < what.getChildCount(); ++i )
		{
			DefaultMutableTreeNode child = (DefaultMutableTreeNode) what.getChildAt( i );
			DefaultMutableTreeNode exists = TreeTools.findNodeWithUserObject( child.getUserObject(), from );
			if( exists != null )
			{
				exists.removeFromParent();
			}
		}
	}

	public static void removeAllLeafs( DefaultMutableTreeNode from, DefaultMutableTreeNode what )
	{
		for( int i = 0; i < what.getChildCount(); ++i )
		{
			DefaultMutableTreeNode child = (DefaultMutableTreeNode) what.getChildAt( i );
			removeAllLeafs( from, child );
		}
		DefaultMutableTreeNode exists = TreeTools.findNodeWithUserObject( what.getUserObject(), from );
		if( exists != null && exists.getChildCount() == 0 )
		{
			exists.removeFromParent();
		}
	}

	public static void sort( DefaultMutableTreeNode result )
	{
		if( result != null )
		{
			List<DefaultMutableTreeNode> children = new ArrayList<DefaultMutableTreeNode>(result.getChildCount());
			for( int i = 0; i < result.getChildCount(); ++i )
			{
				children.add( (DefaultMutableTreeNode) result.getChildAt( i ) );
			}
			result.removeAllChildren();
			Collections.sort( children, comparator );
			for( DefaultMutableTreeNode child : children )
			{
				sort( child );
				result.add( child );
			}
		}
	}

	public static TreePath getPathFor( DefaultMutableTreeNode node )
	{
		List<Object> path = getObjectPathFor( node );
		return new TreePath( path.toArray( new Object[path.size()] ) );
	}
	
	private static List<Object> getObjectPathFor( DefaultMutableTreeNode node )
	{
		LinkedList<Object> path = new LinkedList<Object>();
		if( node != null )
		{
			if( node.getParent() != null )
			{
				path.addAll( getObjectPathFor( (DefaultMutableTreeNode) node.getParent() ) );
			}
			path.add( node );
		}
		return path;
	}
	
}
