/*
 *  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.support.editor.inspectors.attachments;

import java.awt.Component;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
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.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.List;

import javax.swing.AbstractListModel;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JTable;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import com.eviware.soapui.SoapUI;
import com.eviware.soapui.impl.support.actions.ShowOnlineHelpAction;
import com.eviware.soapui.impl.wsdl.AttachmentContainer;
import com.eviware.soapui.impl.wsdl.MutableAttachmentContainer;
import com.eviware.soapui.impl.wsdl.support.HelpUrls;
import com.eviware.soapui.impl.wsdl.support.PathUtils;
import com.eviware.soapui.impl.wsdl.support.WsdlAttachment;
import com.eviware.soapui.model.iface.Attachment;
import com.eviware.soapui.model.iface.MessagePart.AttachmentPart;
import com.eviware.soapui.support.StringUtils;
import com.eviware.soapui.support.Tools;
import com.eviware.soapui.support.UISupport;
import com.eviware.soapui.support.components.JXToolBar;

/**
 * Utility Panel for displaying a table of attachments
 * 
 * @author emibre
 */

public class AttachmentsPanel extends javax.swing.JPanel
{
	private DropTarget dropTarget;
	private FileTransferHandler fileTransferHandler;
	private AttachmentsTableModel tableModel;
	private JFileChooser fc;
	private final AttachmentContainer container;
	private JButton exportBtn;
	private JButton reloadBtn;

	/** Creates new form FileTableList */
	public AttachmentsPanel( AttachmentContainer container )
	{
		this.container = container;
		initComponents();
		initFileTransfer();
	}

	public void release()
	{
		tableModel.release();
		if( attachmentPartCellEditor != null )
			attachmentPartCellEditor.release();
	}

	private void initFileTransfer()
	{
		if( container instanceof MutableAttachmentContainer )
		{
			fileTransferHandler = new FileTransferHandler( tableModel );
			fileTable.setDragEnabled( true );
			fileTable.setTransferHandler( fileTransferHandler );

			dropTarget = new DropTarget();
			dropTarget.setActive( true );
			try
			{
				dropTarget.addDropTargetListener( new DropTargetListener()
				{
					public void dragEnter( DropTargetDragEvent dtde )
					{
					}

					public void dragExit( DropTargetEvent dte )
					{
					}

					public void dragOver( DropTargetDragEvent dtde )
					{
					}

					@SuppressWarnings( "unchecked" )
					public void drop( DropTargetDropEvent dtde )
					{
						try
						{
							dtde.acceptDrop( DnDConstants.ACTION_COPY_OR_MOVE );
							Transferable trans = dtde.getTransferable();
							List<File> files = ( List<File> )trans.getTransferData( DataFlavor.javaFileListFlavor );
							for( File f : files )
							{
								System.out.println( "Dropping file: " + f.getName() );

								Boolean retval = UISupport.confirmOrCancel( "Cache attachment in request?", "Att Attachment" );
								if( retval == null )
									return;

								tableModel.addFile( f, retval );
							}

						}
						catch( Exception e )
						{
							SoapUI.logError( e );
						}
					}

					public void dropActionChanged( DropTargetDragEvent dtde )
					{
					}
				} );
			}
			catch( Exception e )
			{
				SoapUI.logError( e );
			}

			jScrollPane1.getViewport().setDropTarget( dropTarget );
		}
	}

	private void initComponents()
	{
		jScrollPane1 = new javax.swing.JScrollPane();
		tableModel = new AttachmentsTableModel( container );
		fileTable = new JTable( tableModel );

		if( container instanceof MutableAttachmentContainer )
		{
			attachmentPartCellEditor = new AttachmentPartCellEditor();
			fileTable.getColumnModel().getColumn( 3 ).setCellEditor( attachmentPartCellEditor );
		}

		setLayout( new java.awt.BorderLayout() );
		jScrollPane1.setViewportView( fileTable );

		add( jScrollPane1, java.awt.BorderLayout.CENTER );

		jPanel1 = UISupport.createSmallToolbar();

		if( container instanceof MutableAttachmentContainer )
		{
			addFileBtn = UISupport.createToolbarButton( UISupport.createImageIcon( "/add_property.gif" ) );
			removeBtn = UISupport.createToolbarButton( UISupport.createImageIcon( "/remove_property.gif" ) );
			reloadBtn = UISupport.createToolbarButton( UISupport.createImageIcon( "/reload_properties.gif" ) );

			addFileBtn.setToolTipText( "Adds an attachment" );
			addFileBtn.addActionListener( new java.awt.event.ActionListener()
			{
				public void actionPerformed( java.awt.event.ActionEvent evt )
				{
					addFileBtnActionPerformed( evt );
				}
			} );

			jPanel1.addFixed( addFileBtn );

			removeBtn.setToolTipText( "Removes the selected attachment" );
			removeBtn.setEnabled( false );
			removeBtn.addActionListener( new java.awt.event.ActionListener()
			{
				public void actionPerformed( java.awt.event.ActionEvent evt )
				{
					removeBtnActionPerformed( evt );
				}
			} );

			jPanel1.addFixed( removeBtn );

			reloadBtn.setToolTipText( "Reloads the selected attachment" );
			reloadBtn.setEnabled( false );
			reloadBtn.addActionListener( new java.awt.event.ActionListener()
			{
				public void actionPerformed( java.awt.event.ActionEvent evt )
				{
					reloadBtnActionPerformed( evt );
				}
			} );

			jPanel1.addFixed( reloadBtn );
		}

		exportBtn = UISupport.createToolbarButton( UISupport.createImageIcon( "/export.gif" ) );
		exportBtn.setToolTipText( "Exports the selected attachment to a file" );
		exportBtn.setEnabled( false );
		exportBtn.addActionListener( new java.awt.event.ActionListener()
		{
			public void actionPerformed( java.awt.event.ActionEvent evt )
			{
				exportBtnActionPerformed( evt );
			}
		} );

		jPanel1.addFixed( exportBtn );
		jPanel1.addGlue();
		jPanel1.addFixed( UISupport.createToolbarButton( new ShowOnlineHelpAction( HelpUrls.ATTACHMENTS_HELP_URL ) ) );
		add( jPanel1, java.awt.BorderLayout.NORTH );

		fileTable.getSelectionModel().addListSelectionListener( new ListSelectionListener()
		{
			public void valueChanged( ListSelectionEvent e )
			{
				if( removeBtn != null )
				{
					removeBtn.setEnabled( fileTable.getSelectedRowCount() > 0 );
					reloadBtn.setEnabled( fileTable.getSelectedRowCount() > 0 );
				}

				exportBtn.setEnabled( fileTable.getSelectedRowCount() > 0 );
			}
		} );

		fileTable.addMouseListener( new MouseAdapter()
		{
			public void mouseClicked( MouseEvent e )
			{
				if( e.getClickCount() < 2 )
					return;

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

				Attachment attachment = container.getAttachmentAt( ix );

				if( attachment.isCached() )
				{
					String name = attachment.getName();
					try
					{
						name = StringUtils.createFileName( name, '-' );
						File tempFile = File.createTempFile( "attachment-" + name,
								"." + ContentTypeHandler.getExtensionForContentType( attachment.getContentType() ) );
						exportAttachment( tempFile, attachment, false );
					}
					catch( Exception e1 )
					{
						UISupport.showErrorMessage( e1 );
					}
				}
				else
				{
					Tools.openURL( attachment.getUrl() );
				}
			}
		} );
	}

	protected void exportBtnActionPerformed( ActionEvent evt )
	{
		File file = UISupport.getFileDialogs().saveAs( this, "Export Attachment.." );
		while( file != null && file.exists()
				&& !UISupport.confirm( "File " + file.getName() + " exists, overwrite?", "Export Attachment" ) )
		{
			file = UISupport.getFileDialogs().saveAs( this, "Export Attachment.." );
		}

		if( file != null )
		{
			Attachment attachment = tableModel.getAttachmentAt( fileTable.getSelectedRow() );
			try
			{
				exportAttachment( file, attachment, true );
			}
			catch( Exception e )
			{
				UISupport.showErrorMessage( e );
			}
		}
	}

	private void exportAttachment( File file, Attachment attachment, boolean showOpenQuery )
			throws FileNotFoundException, IOException, Exception, MalformedURLException
	{
		FileOutputStream out = new FileOutputStream( file );

		long total = Tools.writeAll( out, attachment.getInputStream() );
		out.close();
		if( !showOpenQuery
				|| UISupport.confirm( "Written [" + total + "] bytes to " + file.getName() + ", open in browser?",
						"Saved File" ) )
		{
			Tools.openURL( file.toURI().toURL().toString() );
		}
	}

	protected void reloadBtnActionPerformed( ActionEvent evt )
	{
		int selectedRow = fileTable.getSelectedRow();
		if( selectedRow == -1 )
			return;

		WsdlAttachment attachment = ( WsdlAttachment )tableModel.getAttachmentAt( selectedRow );
		if( attachment == null )
			return;

		File file = UISupport.getFileDialogs().open( this, "Reload Attachment..", "*", "Any File", attachment.getUrl() );
		if( file != null )
		{
			Boolean retval = UISupport.confirmOrCancel( "Cache attachment in request?", "Reload Attachment" );
			if( retval == null )
				return;

			try
			{
				attachment.reload( file, retval );
				tableModel.fireTableRowsUpdated( selectedRow, selectedRow );
			}
			catch( IOException e )
			{
				UISupport.showErrorMessage( e );
			}
		}
	}

	private void addFileBtnActionPerformed( java.awt.event.ActionEvent evt )
	{// GEN-FIRST:event_addFileBtnActionPerformed
		if( fc == null )
			fc = new JFileChooser();

		String root = PathUtils.getExpandedResourceRoot( container.getModelItem() );
		if( StringUtils.hasContent( root ) )
			fc.setCurrentDirectory( new File( root ) );

		int returnVal = fc.showOpenDialog( this );

		if( returnVal == JFileChooser.APPROVE_OPTION )
		{
			File file = fc.getSelectedFile();
			Boolean retval = UISupport.confirmOrCancel( "Cache attachment in request?", "Add Attachment" );
			if( retval == null )
				return;
			try
			{
				tableModel.addFile( file, retval );
			}
			catch( IOException e )
			{
				UISupport.showErrorMessage( e );
			}
		}
		else
		{
			System.out.println( "Open command cancelled by user." );
		}
	}// GEN-LAST:event_addFileBtnActionPerformed

	private void removeBtnActionPerformed( java.awt.event.ActionEvent evt )
	{// GEN-FIRST:event_removeBtnActionPerformed
		if( UISupport.confirm( "Remove selected attachments?", "Remove Attachments" ) )
			tableModel.removeAttachment( fileTable.getSelectedRows() );
	}// GEN-LAST:event_removeBtnActionPerformed

	// Variables declaration - do not modify//GEN-BEGIN:variables
	private javax.swing.JButton addFileBtn;
	private JTable fileTable;
	private JXToolBar jPanel1;
	private javax.swing.JScrollPane jScrollPane1;
	private javax.swing.JButton removeBtn;
	private AttachmentPartCellEditor attachmentPartCellEditor;

	// End of variables declaration//GEN-END:variables

	private class AttachmentPartCellEditor extends DefaultCellEditor
	{
		public AttachmentPartCellEditor()
		{
			super( new JComboBox( new PartsComboBoxModel() ) );
		}

		public void release()
		{
			( ( PartsComboBoxModel )( ( JComboBox )editorComponent ).getModel() ).release();
		}

		public Component getTableCellEditorComponent( JTable table, Object value, boolean isSelected, int row, int column )
		{
			PartsComboBoxModel model = ( ( PartsComboBoxModel )( ( JComboBox )editorComponent ).getModel() );
			( ( JComboBox )editorComponent ).setModel( model.init( tableModel.getAttachmentAt( row ) ) );

			return super.getTableCellEditorComponent( table, value, isSelected, row, column );
		}
	}

	private final class PartsComboBoxModel extends AbstractListModel implements ComboBoxModel, PropertyChangeListener
	{
		private Attachment attachment;
		private AttachmentPart[] parts;

		public PartsComboBoxModel()
		{
			container.addAttachmentsChangeListener( this );
		}

		public void release()
		{
			container.removeAttachmentsChangeListener( this );
		}

		public PartsComboBoxModel init( Attachment attachment )
		{
			this.attachment = attachment;

			int previousPartsCount = parts == null ? 0 : parts.length;

			parts = container.getDefinedAttachmentParts();
			if( previousPartsCount < parts.length )
			{
				fireIntervalAdded( this, previousPartsCount, parts.length );
			}
			else if( previousPartsCount > parts.length )
			{
				fireIntervalRemoved( this, parts.length - 1, previousPartsCount );
			}

			fireContentsChanged( this, 0, parts.length - 1 );

			return this;
		}

		public Object getElementAt( int index )
		{
			return parts == null ? null : parts[index].getName();
		}

		public int getSize()
		{
			return parts == null ? 0 : parts.length;
		}

		public Object getSelectedItem()
		{
			return attachment == null ? null : attachment.getPart();
		}

		public void setSelectedItem( Object anItem )
		{
			if( attachment != null )
				attachment.setPart( ( String )anItem );
		}

		public void propertyChange( PropertyChangeEvent arg0 )
		{
			// delete our current one?
			if( arg0.getOldValue() == attachment && arg0.getNewValue() == null )
			{
				attachment = null;
				parts = null;
			}
		}
	}
}
