/*
 *  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.loadtest;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumnModel;

import org.jdesktop.swingx.JXTable;

import com.eviware.soapui.impl.support.actions.ShowOnlineHelpAction;
import com.eviware.soapui.impl.wsdl.loadtest.LoadTestAssertion;
import com.eviware.soapui.impl.wsdl.loadtest.LoadTestListener;
import com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTest;
import com.eviware.soapui.impl.wsdl.loadtest.assertions.LoadTestAssertionRegistry;
import com.eviware.soapui.impl.wsdl.support.Configurable;
import com.eviware.soapui.impl.wsdl.support.HelpUrls;
import com.eviware.soapui.support.UISupport;
import com.eviware.soapui.support.components.JXToolBar;

/**
 * Table showing configured assertions for a WsdlLoadTest
 * 
 * @todo add popup menu
 * 
 * @author Ole.Matzura
 */

public class JLoadTestAssertionsTable extends JPanel
{
	private JXTable table;
	private final WsdlLoadTest loadTest;
	private ConfigureAssertionAction configureAssertionAction;
	private RemoveAssertionAction removeAssertionAction;
	private AddLoadTestAssertionAction addLoadTestAssertionAction;
	private LoadTestAssertionsTableModel tableModel;
	private JPopupMenu assertionPopup;
	private InternalLoadTestListener internalLoadTestListener = new InternalLoadTestListener();

	public JLoadTestAssertionsTable( WsdlLoadTest wsdlLoadTest )
	{
		super( new BorderLayout() );
		this.loadTest = wsdlLoadTest;

		loadTest.addLoadTestListener( internalLoadTestListener );

		tableModel = new LoadTestAssertionsTableModel();
		table = new JXTable( tableModel );
		table.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );

		TableColumnModel columnModel = table.getColumnModel();
		columnModel.getColumn( 0 ).setMaxWidth( 16 );
		columnModel.getColumn( 0 ).setCellRenderer( new IconTableCellRenderer() );
		columnModel.getColumn( 1 ).setPreferredWidth( 100 );
		columnModel.getColumn( 2 ).setPreferredWidth( 100 );
		columnModel.getColumn( 3 ).setPreferredWidth( 200 );

		JScrollPane scrollPane = new JScrollPane( table );
		add( scrollPane, BorderLayout.CENTER );

		table.addMouseListener( new MouseAdapter()
		{

			public void mouseClicked( MouseEvent e )
			{
				if( e.getClickCount() < 2 )
					return;

				int ix = table.getSelectedRow();
				if( ix == -1 )
					return;
				ix = table.convertRowIndexToModel( ix );

				Object obj = loadTest.getAssertionAt( ix );
				if( obj instanceof Configurable )
				{
					( ( Configurable )obj ).configure();
				}
				else
					Toolkit.getDefaultToolkit().beep();
			}
		} );

		add( buildToolbar(), BorderLayout.NORTH );

		table.getSelectionModel().addListSelectionListener( new ListSelectionListener()
		{

			public void valueChanged( ListSelectionEvent e )
			{
				int ix = table.getSelectedRow();

				configureAssertionAction.setEnabled( ix >= 0 );
				removeAssertionAction.setEnabled( ix >= 0 );

				if( ix == -1 )
					return;

				ix = table.convertRowIndexToModel( ix );
				configureAssertionAction.setEnabled( loadTest.getAssertionAt( ix ) instanceof Configurable );
			}
		} );

		assertionPopup = new JPopupMenu();
		assertionPopup.add( configureAssertionAction );
		assertionPopup.addSeparator();
		assertionPopup.add( addLoadTestAssertionAction );
		assertionPopup.add( removeAssertionAction );

		setComponentPopupMenu( assertionPopup );

		scrollPane.setInheritsPopupMenu( true );
		table.setComponentPopupMenu( assertionPopup );
	}

	public void release()
	{
		tableModel.release();
		loadTest.removeLoadTestListener( internalLoadTestListener );
	}

	private JComponent buildToolbar()
	{
		configureAssertionAction = new ConfigureAssertionAction();
		removeAssertionAction = new RemoveAssertionAction();
		addLoadTestAssertionAction = new AddLoadTestAssertionAction();

		JXToolBar toolbar = UISupport.createSmallToolbar();

		JButton button = UISupport.createToolbarButton( addLoadTestAssertionAction );
		toolbar.addFixed( button );
		button = UISupport.createToolbarButton( removeAssertionAction );
		toolbar.addFixed( button );
		button = UISupport.createToolbarButton( configureAssertionAction );
		toolbar.addFixed( button );
		toolbar.addGlue();
		toolbar.add( new ShowOnlineHelpAction( HelpUrls.LOADTEST_ASSERTIONS_URL ) );

		return toolbar;
	}

	private class LoadTestAssertionsTableModel extends AbstractTableModel implements PropertyChangeListener
	{
		public LoadTestAssertionsTableModel()
		{
			for( int c = 0; c < loadTest.getAssertionCount(); c++ )
			{
				loadTest.getAssertionAt( c ).addPropertyChangeListener( LoadTestAssertion.CONFIGURATION_PROPERTY, this );
			}
		}

		public void release()
		{
			for( int c = 0; c < loadTest.getAssertionCount(); c++ )
			{
				loadTest.getAssertionAt( c ).removePropertyChangeListener( LoadTestAssertion.CONFIGURATION_PROPERTY, this );
			}
		}

		public int getRowCount()
		{
			return loadTest.getAssertionCount();
		}

		public int getColumnCount()
		{
			return 4;
		}

		public Class<?> getColumnClass( int columnIndex )
		{
			switch( columnIndex )
			{
			case 0 :
				return ImageIcon.class;
			default :
				return String.class;
			}
		}

		public String getColumnName( int column )
		{
			switch( column )
			{
			case 0 :
				return " ";
			case 1 :
				return "Name";
			case 2 :
				return "Step";
			case 3 :
				return "Details";
			}

			return null;
		}

		public Object getValueAt( int rowIndex, int columnIndex )
		{
			LoadTestAssertion assertion = loadTest.getAssertionAt( rowIndex );

			switch( columnIndex )
			{
			case 0 :
				return assertion.getIcon();
			case 1 :
				return assertion.getName();
			case 2 :
				return assertion.getTargetStep();
			case 3 :
				return assertion.getDescription();
			}

			return null;
		}

		public void assertionRemoved( LoadTestAssertion assertion )
		{
			assertion.removePropertyChangeListener( LoadTestAssertion.CONFIGURATION_PROPERTY, this );
			fireTableDataChanged();
		}

		public void assertionAdded( LoadTestAssertion assertion )
		{
			assertion.addPropertyChangeListener( LoadTestAssertion.CONFIGURATION_PROPERTY, this );
			fireTableRowsInserted( getRowCount() - 1, getRowCount() - 1 );
		}

		public void propertyChange( PropertyChangeEvent evt )
		{
			fireTableDataChanged();
		}
	}

	private static final class IconTableCellRenderer extends DefaultTableCellRenderer
	{
		public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus,
				int row, int column )
		{
			if( value != null )
				setIcon( ( Icon )value );

			if( isSelected )
			{
				setBackground( table.getSelectionBackground() );
				setForeground( table.getSelectionForeground() );
			}
			else
			{
				setBackground( table.getBackground() );
				setForeground( table.getForeground() );
			}

			return this;
		}
	}

	public class AddLoadTestAssertionAction extends AbstractAction
	{
		public AddLoadTestAssertionAction()
		{
			super( "Add Assertion" );
			putValue( Action.SHORT_DESCRIPTION, "Adds an assertion to this LoadTest" );
			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/addAssertion.gif" ) );
		}

		public void actionPerformed( ActionEvent e )
		{
			String[] types = LoadTestAssertionRegistry.getAvailableAssertions();
			String type = ( String )UISupport.prompt( "Select assertion type to add", "Add Assertion", types );
			if( type != null )
			{
				loadTest.addAssertion( type, LoadTestAssertion.ANY_TEST_STEP, true );
			}
		}
	}

	public class ConfigureAssertionAction extends AbstractAction
	{
		ConfigureAssertionAction()
		{
			super( "Configure" );
			putValue( Action.SHORT_DESCRIPTION, "Configures the selection assertion" );
			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/options.gif" ) );
			setEnabled( false );
		}

		public void actionPerformed( ActionEvent e )
		{
			int ix = table.getSelectedRow();
			if( ix == -1 )
				return;
			ix = table.convertRowIndexToModel( ix );

			Object obj = loadTest.getAssertionAt( ix );
			if( obj instanceof Configurable )
			{
				( ( Configurable )obj ).configure();
				tableModel.fireTableRowsUpdated( ix, ix );
			}
			else
				Toolkit.getDefaultToolkit().beep();
		}
	}

	public class RemoveAssertionAction extends AbstractAction
	{
		public RemoveAssertionAction()
		{
			super( "Remove Assertion" );
			putValue( Action.SHORT_DESCRIPTION, "Removes the selected assertion from this LoadTest" );
			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/remove_assertion.gif" ) );
			setEnabled( false );
		}

		public void actionPerformed( ActionEvent e )
		{
			int ix = table.getSelectedRow();
			if( ix == -1 )
				return;
			ix = table.convertRowIndexToModel( ix );

			LoadTestAssertion assertion = loadTest.getAssertionAt( ix );
			if( UISupport.confirm( "Remove assertion [" + assertion.getName() + "]", "Remove Assertion" ) )
			{
				loadTest.removeAssertion( assertion );
			}
		}
	}

	public class InternalLoadTestListener implements LoadTestListener
	{
		public void assertionAdded( LoadTestAssertion assertion )
		{
			tableModel.assertionAdded( assertion );
			table.getSelectionModel().setSelectionInterval( tableModel.getRowCount() - 1, tableModel.getRowCount() - 1 );
		}

		public void assertionRemoved( LoadTestAssertion assertion )
		{
			tableModel.assertionRemoved( assertion );
		}
	}
}
