/*
 *  soapUI, copyright (C) 2004-2012 smartbear.com 
 *
 *  soapUI is free software; you can redistribute it and/or modify it under the 
 *  terms of version 2.1 of the GNU Lesser General Public License as published by 
 *  the Free Software Foundation.
 *
 *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
 *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 *  See the GNU Lesser General Public License for more details at gnu.org.
 */

package com.eviware.soapui.model.tree;

import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;
import java.util.Vector;

import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.event.TreeModelEvent;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import com.eviware.soapui.SoapUI;
import com.eviware.soapui.model.ModelItem;
import com.eviware.soapui.model.settings.SettingsListener;
import com.eviware.soapui.model.testsuite.TestCase;
import com.eviware.soapui.model.testsuite.TestStep;
import com.eviware.soapui.support.action.swing.ActionList;
import com.eviware.soapui.support.action.swing.ActionListBuilder;
import com.eviware.soapui.support.action.swing.ActionSupport;

/**
 * Abstract base class for SoapUITreeNode implementations
 * 
 * @author Ole.Matzura
 */

public abstract class AbstractModelItemTreeNode<T extends ModelItem> implements SoapUITreeNode, PropertyChangeListener
{
	private final T modelItem;
	private final ModelItem parentItem;
	private final SoapUITreeModel treeModel;
	private List<? extends SoapUITreeNode> orderItems;
	private String orderSetting;
	private InternalSettingsListener internalSettingsListener;

	protected AbstractModelItemTreeNode( T modelItem, ModelItem parentItem, SoapUITreeModel treeModel )
	{
		this.modelItem = modelItem;
		this.parentItem = parentItem;
		this.treeModel = treeModel;

		modelItem.addPropertyChangeListener( this );
	}

	public SoapUITreeModel getTreeModel()
	{
		return treeModel;
	}

	public T getModelItem()
	{
		return modelItem;
	}

	public boolean valueChanged( Object newValue )
	{
		return false;
	}

	public boolean isLeaf()
	{
		return getChildCount() == 0;
	}

	public int getChildCount()
	{
		return orderItems == null ? 0 : orderItems.size();
	}

	public SoapUITreeNode getChildNode( int index )
	{
		return orderItems == null ? null : orderItems.get( index );
	}

	public int getIndexOfChild( Object child )
	{
		return orderItems == null ? -1 : orderItems.indexOf( child );
	}

	public String toString()
	{
		if( modelItem instanceof TestStep )
			return ( ( TestStep )modelItem ).getLabel();
		else if( modelItem instanceof TestCase )
			return ( ( TestCase )modelItem ).getLabel();

		return modelItem.getName();
	}

	public JPopupMenu getPopup()
	{
		return ActionSupport.buildPopup( getActions() );
	}

	public ActionList getActions()
	{
		return ActionListBuilder.buildActions( modelItem );
	}

	public SoapUITreeNode getParentTreeNode()
	{
		return treeModel.getTreeNode( parentItem );
	}

	public void propertyChange( PropertyChangeEvent evt )
	{
		String propertyName = evt.getPropertyName();
		if( propertyName.equals( ModelItem.NAME_PROPERTY ) || propertyName.equals( ModelItem.LABEL_PROPERTY ) )
		{
			// use this since length has probably changed
			getTreeModel().notifyNodeChanged( this );
		}
		else if( propertyName.equals( ModelItem.ICON_PROPERTY ) )
		{
			// hack to improve rendering performance
			JTree mainTree = SoapUI.getNavigator().getMainTree();
			TreePath nodePath = getTreeModel().getPath( this );
			Rectangle rowBounds = mainTree.getPathBounds( nodePath );
			if( rowBounds != null )
			{
				mainTree.repaint( rowBounds );
			}
		}
	}

	public void release()
	{
		modelItem.removePropertyChangeListener( this );

		if( internalSettingsListener != null )
			SoapUI.getSettings().removeSettingsListener( internalSettingsListener );

		getTreeModel().unmapModelItem( modelItem );
	}

	public <T2 extends SoapUITreeNode> void initOrdering( List<T2> items, String setting )
	{
		this.orderItems = items;
		this.orderSetting = setting;

		internalSettingsListener = new InternalSettingsListener( this, setting );
		SoapUI.getSettings().addSettingsListener( internalSettingsListener );
		sortModelItems( items, setting );
	}

	private final class InternalSettingsListener implements SettingsListener
	{
		private final AbstractModelItemTreeNode<?> node;
		private final String setting;

		public InternalSettingsListener( AbstractModelItemTreeNode<?> node, String setting )
		{
			this.node = node;
			this.setting = setting;
		}

		public void settingChanged( String name, String newValue, String oldValue )
		{
			if( name.equals( setting ) )
			{
				if( oldValue == null )
					oldValue = "false";

				if( newValue == null )
					newValue = "false";

				if( !oldValue.equals( newValue ) )
				{
					TreePath path = getTreeModel().getPath( AbstractModelItemTreeNode.this );
					node.reorder( SoapUI.getNavigator().isVisible( path ) && SoapUI.getNavigator().isExpanded( path ) );
				}
			}
		}

		@Override
		public void settingsReloaded()
		{
			// TODO Auto-generated method stub
		}
	}

	public void reorder( boolean notify )
	{
		if( orderItems != null )
		{
			sortModelItems( orderItems, orderSetting );

			if( notify )
			{
				getTreeModel().notifyStructureChanged( new TreeModelEvent( this, getTreeModel().getPath( this ) ) );
			}
		}
	}

	public <T2 extends SoapUITreeNode> void sortModelItems( List<T2> modelItems, final String setting )
	{
		Collections.sort( modelItems, new Comparator<T2>()
		{
			public int compare( T2 o1, T2 o2 )
			{
				String name1 = o1.getModelItem().getName();
				String name2 = o2.getModelItem().getName();

				if( name1 == null && name2 == null )
				{
					return 0;
				}
				else if( name1 == null )
				{
					return -1;
				}
				else if( name2 == null )
				{
					return 1;
				}
				else if( setting != null && SoapUI.getSettings().getBoolean( setting ) )
				{
					return name1.compareToIgnoreCase( name2 );
				}
				else
				{
					return name1.compareTo( name2 );
				}
			}
		} );
	}

	public Enumeration<?> children()
	{
		Vector<TreeNode> children = new Vector<TreeNode>();
		for( int c = 0; c < getChildCount(); c++ )
			children.add( getChildAt( c ) );

		return children.elements();
	}

	public boolean getAllowsChildren()
	{
		return !isLeaf();
	}

	public TreeNode getChildAt( int childIndex )
	{
		return getChildNode( childIndex );
	}

	public int getIndex( TreeNode node )
	{
		return getIndexOfChild( node );
	}

	public TreeNode getParent()
	{
		return getParentTreeNode();
	}

	public class ReorderPropertyChangeListener implements PropertyChangeListener
	{
		public void propertyChange( PropertyChangeEvent arg0 )
		{
			reorder( true );
			SoapUI.getNavigator().selectModelItem( ( ModelItem )arg0.getSource() );
		}
	}
}
