/*
 *  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.impl.wsdl.panels.project;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.dnd.Autoscroll;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragSource;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;

import com.eviware.soapui.impl.wsdl.WsdlProject;
import com.eviware.soapui.impl.wsdl.WsdlTestSuite;
import com.eviware.soapui.impl.wsdl.actions.project.AddNewTestSuiteAction;
import com.eviware.soapui.impl.wsdl.panels.support.ProgressBarTestSuiteAdapter;
import com.eviware.soapui.model.ModelItem;
import com.eviware.soapui.model.support.ProjectListenerAdapter;
import com.eviware.soapui.model.testsuite.TestSuite;
import com.eviware.soapui.support.UISupport;
import com.eviware.soapui.support.action.swing.ActionList;
import com.eviware.soapui.support.action.swing.ActionListBuilder;
import com.eviware.soapui.support.action.swing.ActionSupport;
import com.eviware.soapui.support.action.swing.SwingActionDelegate;
import com.eviware.soapui.support.dnd.DropType;
import com.eviware.soapui.support.dnd.SoapUIDragAndDropHandler;
import com.eviware.soapui.support.dnd.SoapUIDragAndDropable;
import com.eviware.soapui.support.swing.AutoscrollSupport;

/**
 * A panel showing a scrollable list of TestSuites in a Project.
 * 
 * @author Ole.Matzura
 */

public class JProjectTestSuiteList extends JPanel
{
	private Map<TestSuite, TestSuiteListPanel> panels = new HashMap<TestSuite, TestSuiteListPanel>();
	private final WsdlProject project;
	private final InternalTestSuiteListener testSuiteListener = new InternalTestSuiteListener();
	private TestSuiteListPanel selectedTestSuite;

	public JProjectTestSuiteList( WsdlProject testSuite )
	{
		this.project = testSuite;
		setLayout( new BoxLayout( this, BoxLayout.Y_AXIS ) );

		for( int c = 0; c < testSuite.getTestSuiteCount(); c++ )
		{
			TestSuiteListPanel testSuiteListPanel = createTestSuiteListPanel( testSuite.getTestSuiteAt( c ) );
			panels.put( testSuite.getTestSuiteAt( c ), testSuiteListPanel );
			add( testSuiteListPanel );
		}

		add( Box.createVerticalGlue() );
		setBackground( Color.WHITE );

		testSuite.addProjectListener( testSuiteListener );

		ActionList actions = ActionListBuilder.buildActions( testSuite );
		actions.removeAction( 0 );
		actions.removeAction( 0 );
		setComponentPopupMenu( ActionSupport.buildPopup( actions ) );

		DragSource dragSource = DragSource.getDefaultDragSource();

		SoapUIDragAndDropHandler dragAndDropHandler = new SoapUIDragAndDropHandler( new TestSuiteListDragAndDropable(
				this ), DropType.AFTER );

		dragSource.createDefaultDragGestureRecognizer( this, DnDConstants.ACTION_COPY_OR_MOVE, dragAndDropHandler );
	}

	public void reset()
	{
		for( TestSuiteListPanel testSuitePanel : panels.values() )
		{
			testSuitePanel.reset();
		}
	}

	@Override
	public void addNotify()
	{
		super.addNotify();
		project.addProjectListener( testSuiteListener );
	}

	@Override
	public void removeNotify()
	{
		super.removeNotify();
		project.removeProjectListener( testSuiteListener );
	}

	private final class InternalTestSuiteListener extends ProjectListenerAdapter
	{
		public void testSuiteAdded( TestSuite testSuite )
		{
			TestSuiteListPanel testSuiteListPanel = createTestSuiteListPanel( testSuite );
			panels.put( testSuite, testSuiteListPanel );
			add( testSuiteListPanel, testSuite.getProject().getIndexOfTestSuite( testSuite ) );
			revalidate();
			repaint();
		}

		public void testSuiteRemoved( TestSuite testSuite )
		{
			TestSuiteListPanel testSuiteListPanel = panels.get( testSuite );
			if( testSuiteListPanel != null )
			{
				remove( testSuiteListPanel );
				panels.remove( testSuite );
				revalidate();
				repaint();
			}
		}

		public void testSuiteMoved( TestSuite testSuite, int index, int offset )
		{
			TestSuiteListPanel testSuiteListPanel = panels.get( testSuite );
			if( testSuiteListPanel != null )
			{
				boolean hadFocus = testSuiteListPanel.hasFocus();

				remove( testSuiteListPanel );
				add( testSuiteListPanel, index + offset );

				revalidate();
				repaint();

				if( hadFocus )
					testSuiteListPanel.requestFocus();
			}
		}
	}

	public final class TestSuiteListPanel extends JPanel implements Autoscroll
	{
		private final WsdlTestSuite testSuite;
		private JProgressBar progressBar;
		private JLabel label;
		private ProgressBarTestSuiteAdapter progressBarAdapter;
		private TestSuitePropertyChangeListener testSuitePropertyChangeListener;
		private AutoscrollSupport autoscrollSupport;

		public TestSuiteListPanel( WsdlTestSuite testSuite )
		{
			super( new BorderLayout() );

			setFocusable( true );

			this.testSuite = testSuite;
			autoscrollSupport = new AutoscrollSupport( this );

			progressBar = new JProgressBar( 0, 100 )
			{
				protected void processMouseEvent( MouseEvent e )
				{
					if( e.getID() == MouseEvent.MOUSE_PRESSED || e.getID() == MouseEvent.MOUSE_RELEASED )
					{
						TestSuiteListPanel.this.processMouseEvent( translateMouseEvent( e ) );
					}
				}

				protected void processMouseMotionEvent( MouseEvent e )
				{
					TestSuiteListPanel.this.processMouseMotionEvent( translateMouseEvent( e ) );
				}

				/**
				 * Translates the given mouse event to the enclosing map panel's
				 * coordinate space.
				 */
				private MouseEvent translateMouseEvent( MouseEvent e )
				{
					return new MouseEvent( TestSuiteListPanel.this, e.getID(), e.getWhen(), e.getModifiers(), e.getX()
							+ getX(), e.getY() + getY(), e.getClickCount(), e.isPopupTrigger(), e.getButton() );
				}
			};

			JPanel progressPanel = UISupport.createProgressBarPanel( progressBar, 5, false );

			progressBar.setMinimumSize( new Dimension( 0, 10 ) );
			progressBar.setBackground( Color.WHITE );
			progressBar.setInheritsPopupMenu( true );

			label = new JLabel( testSuite.getLabel() );
			label.setBorder( BorderFactory.createEmptyBorder( 5, 5, 5, 5 ) );
			label.setInheritsPopupMenu( true );
			label.setEnabled( !testSuite.isDisabled() );

			add( progressPanel, BorderLayout.CENTER );
			add( label, BorderLayout.NORTH );

			testSuitePropertyChangeListener = new TestSuitePropertyChangeListener();

			initPopup( testSuite );

			addMouseListener( new MouseAdapter()
			{

				@Override
				public void mousePressed( MouseEvent e )
				{
					requestFocus();
				}

				public void mouseClicked( MouseEvent e )
				{
					if( e.getClickCount() < 2 )
					{
						if( selectedTestSuite != null )
							selectedTestSuite.setSelected( false );

						setSelected( true );
						selectedTestSuite = TestSuiteListPanel.this;
						return;
					}

					UISupport.selectAndShow( TestSuiteListPanel.this.testSuite );
				}
			} );

			addKeyListener( new TestSuiteListPanelKeyHandler() );

			// init border
			setSelected( false );
		}

		public void reset()
		{
			progressBar.setValue( 0 );
			progressBar.setString( "" );
		}

		private void initPopup( WsdlTestSuite testSuite )
		{
			ActionList actions = ActionListBuilder.buildActions( testSuite );
			actions.insertAction(
					SwingActionDelegate.createDelegate( AddNewTestSuiteAction.SOAPUI_ACTION_ID, project, null, null ), 0 );
			actions.insertAction( ActionSupport.SEPARATOR_ACTION, 1 );

			setComponentPopupMenu( ActionSupport.buildPopup( actions ) );
		}

		public void addNotify()
		{
			super.addNotify();
			testSuite.addPropertyChangeListener( testSuitePropertyChangeListener );
			progressBarAdapter = new ProgressBarTestSuiteAdapter( progressBar, testSuite );
		}

		public void removeNotify()
		{
			super.removeNotify();
			if( progressBarAdapter != null )
			{
				testSuite.removePropertyChangeListener( testSuitePropertyChangeListener );
				progressBarAdapter.release();

				progressBarAdapter = null;
			}
		}

		public Dimension getMaximumSize()
		{
			Dimension size = super.getMaximumSize();
			size.height = 50;
			return size;
		}

		public void setSelected( boolean selected )
		{
			if( selected )
			{
				setBorder( BorderFactory.createLineBorder( Color.GRAY ) );
			}
			else
			{
				setBorder( BorderFactory.createLineBorder( Color.WHITE ) );
			}
		}

		public boolean isSelected()
		{
			return selectedTestSuite != null && selectedTestSuite.getTestSuite() == testSuite;
		}

		private final class TestSuitePropertyChangeListener implements PropertyChangeListener
		{
			public void propertyChange( PropertyChangeEvent evt )
			{
				if( evt.getPropertyName().equals( TestSuite.LABEL_PROPERTY ) )
				{
					label.setEnabled( !testSuite.isDisabled() );
					label.setText( testSuite.getLabel() );
				}
				else if( evt.getPropertyName().equals( TestSuite.DISABLED_PROPERTY ) )
				{
					initPopup( testSuite );
				}
			}
		}

		protected WsdlTestSuite getTestSuite()
		{
			return testSuite;
		}

		public ModelItem getModelItem()
		{
			return testSuite;
		}

		public void autoscroll( Point pt )
		{
			int ix = getIndexOf( this );
			if( pt.getY() < 12 && ix > 0 )
			{
				Rectangle bounds = JProjectTestSuiteList.this.getComponent( ix - 1 ).getBounds();
				JProjectTestSuiteList.this.scrollRectToVisible( bounds );
			}
			else if( pt.getY() > getHeight() - 12 && ix < project.getTestSuiteCount() - 1 )
			{
				Rectangle bounds = JProjectTestSuiteList.this.getComponent( ix + 1 ).getBounds();
				JProjectTestSuiteList.this.scrollRectToVisible( bounds );
			}
		}

		public Insets getAutoscrollInsets()
		{
			return autoscrollSupport.getAutoscrollInsets();
		}

		private final class TestSuiteListPanelKeyHandler extends KeyAdapter
		{
			public void keyPressed( KeyEvent e )
			{
				if( e.getKeyChar() == KeyEvent.VK_ENTER )
				{
					UISupport.selectAndShow( testSuite );
					e.consume();
				}
				else
				{
					ActionList actions = ActionListBuilder.buildActions( testSuite );
					if( actions != null )
						actions.dispatchKeyEvent( e );
				}
			}
		}
	}

	protected int getIndexOf( TestSuiteListPanel panel )
	{
		return Arrays.asList( getComponents() ).indexOf( panel );
	}

	protected TestSuiteListPanel createTestSuiteListPanel( TestSuite testSuite )
	{
		TestSuiteListPanel testSuiteListPanel = new TestSuiteListPanel( ( WsdlTestSuite )testSuite );

		DragSource dragSource = DragSource.getDefaultDragSource();

		SoapUIDragAndDropHandler dragAndDropHandler = new SoapUIDragAndDropHandler(
				new TestSuiteListPanelDragAndDropable( testSuiteListPanel ), DropType.BEFORE_AND_AFTER );

		dragSource.createDefaultDragGestureRecognizer( testSuiteListPanel, DnDConstants.ACTION_COPY_OR_MOVE,
				dragAndDropHandler );

		return testSuiteListPanel;
	}

	private class TestSuiteListDragAndDropable implements SoapUIDragAndDropable<ModelItem>
	{
		private final JProjectTestSuiteList list;

		public TestSuiteListDragAndDropable( JProjectTestSuiteList list )
		{
			this.list = list;
		}

		public JComponent getComponent()
		{
			return list;
		}

		public Rectangle getModelItemBounds( ModelItem modelItem )
		{
			return list.getBounds();
		}

		public ModelItem getModelItemForLocation( int x, int y )
		{
			int testSuiteCount = project.getTestSuiteCount();
			return testSuiteCount == 0 ? project : project.getTestSuiteAt( testSuiteCount - 1 );
		}

		public Component getRenderer( ModelItem modelItem )
		{
			return null;
		}

		public void selectModelItem( ModelItem modelItem )
		{
		}

		public void setDragInfo( String dropInfo )
		{
			list.setToolTipText( dropInfo );
		}

		public void toggleExpansion( ModelItem modelItem )
		{
		}
	}

	private static class TestSuiteListPanelDragAndDropable implements SoapUIDragAndDropable<ModelItem>
	{
		private final TestSuiteListPanel testSuitePanel;

		public TestSuiteListPanelDragAndDropable( TestSuiteListPanel testSuitePanel )
		{
			this.testSuitePanel = testSuitePanel;
		}

		public JComponent getComponent()
		{
			return testSuitePanel;
		}

		public void setDragInfo( String dropInfo )
		{
			testSuitePanel.setToolTipText( dropInfo.length() == 0 ? null : dropInfo );
		}

		public Rectangle getModelItemBounds( ModelItem path )
		{
			return new Rectangle( testSuitePanel.getSize() );
		}

		public ModelItem getModelItemForLocation( int x, int y )
		{
			return testSuitePanel.getModelItem();
		}

		public Component getRenderer( ModelItem path )
		{
			return null;
		}

		public void selectModelItem( ModelItem path )
		{
			testSuitePanel.setSelected( !testSuitePanel.isSelected() );
		}

		public void toggleExpansion( ModelItem last )
		{
		}
	}
}
