/*
 *  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.x.impl.swing;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

import com.eviware.soapui.SoapUI;
import com.eviware.soapui.actions.UpdateableAction;
import com.eviware.soapui.support.DescriptionPanel;
import com.eviware.soapui.support.UISupport;
import com.eviware.soapui.support.action.swing.DefaultActionList;
import com.eviware.soapui.support.components.JButtonBar;
import com.eviware.soapui.support.swing.ModalFrameUtil;
import com.eviware.soapui.support.types.StringToStringMap;
import com.eviware.x.form.WizardPage;
import com.eviware.x.form.XForm;
import com.eviware.x.form.XFormDialog;
import com.eviware.x.form.XFormField;

public class JWizardDialog extends SwingXFormDialog
{
	private String name;
	private ArrayList<String> pageNames = new ArrayList<String>();

	private JFrame dialog;
	private DescriptionPanel descriptionPanel;
	private List<SwingXFormImpl> forms = new ArrayList<SwingXFormImpl>();
	private JPanel pages;
	private CardLayout cardLayout;

	private HashMap<String, WizardPage> controllers = new HashMap<String, WizardPage>();
	private int currentPage = 0;

	private DefaultActionList actions;
	private JButtonBar buttons;

	public JWizardDialog( String name, XForm[] forms, Action helpAction, String description, ImageIcon icon )
	{
		this.name = name;

		// Use JFrame instead of JDialog to get maximize button.
		dialog = new JFrame( name );

		initActions( helpAction );

		cardLayout = new CardLayout();
		pages = new JPanel( cardLayout );
		for( XForm form : forms )
		{
			SwingXFormImpl swingFormImpl = ( SwingXFormImpl )form;
			this.forms.add( swingFormImpl );

			JPanel panel = swingFormImpl.getPanel();
			panel.setBorder( BorderFactory.createEmptyBorder( 0, 0, 5, 0 ) );

			addPage( form.getName(), panel );
		}

		buttons = UISupport.initFrameActions( actions, dialog );

		if( description != null || icon != null )
		{
			descriptionPanel = UISupport.buildDescription( name, description, icon );
			dialog.getContentPane().add( descriptionPanel, BorderLayout.NORTH );
		}

		dialog.getContentPane().add( pages, BorderLayout.CENTER );

		buttons.setBorder( BorderFactory.createEmptyBorder( 3, 5, 3, 5 ) );
		dialog.getContentPane().add( buttons, BorderLayout.SOUTH );
		dialog.pack();
	}

	public XForm[] getForms()
	{
		List<XForm> result = new ArrayList<XForm>();
		for( XForm form : forms )
			result.add( form );
		return result.toArray( new XForm[result.size()] );
	}

	public void dispose()
	{
		dialog.dispose();
	}

	private void initActions( Action helpAction )
	{
		actions = new DefaultActionList();
		actions.addAction( new BackAction() );
		actions.addAction( new NextAction() );
		actions.addAction( new CancelAction() );
		actions.addAction( new FinishAction() );
		if( helpAction != null )
			actions.addAction( helpAction );
	}

	public void addAction( Action action )
	{
		DefaultActionList actions = new DefaultActionList();
		actions.addAction( action );
		buttons.addActions( actions );
	}

	private void addPage( String name, JComponent component )
	{
		pages.add( component, name );
		if( !pageNames.contains( name ) )
			pageNames.add( name );
		actions.update();
	}

	public void addPageController( WizardPage controller )
	{
		controllers.put( controller.getName(), controller );
	}

	public void addPageAndController( JComponent component, WizardPage controller )
	{
		addPage( controller.getName(), component );
		addPageController( controller );
	}

	public void setValues( StringToStringMap values )
	{
		for( XForm form : forms )
			form.setValues( values );
	}

	public void setOptions( String field, Object[] options )
	{
		for( XForm form : forms )
			form.setOptions( field, options );
	}

	public XFormField getFormField( String name )
	{
		for( XForm form : forms )
		{
			XFormField formField = form.getFormField( name );
			if( formField != null )
				return formField;
		}

		return null;
	}

	public StringToStringMap getValues()
	{
		StringToStringMap result = new StringToStringMap();

		for( XForm form : forms )
			result.putAll( form.getValues() );

		return result;
	}

	public void setVisible( boolean visible )
	{
		if( visible )
		{
			if( showPage( 0 ) )
			{
				Frame mainFrame = UISupport.getMainFrame();
				UISupport.centerDialog( dialog, mainFrame );
				// dialog.setVisible( visible );
				ModalFrameUtil.showAsModal( dialog, mainFrame );
			}
		}
		else
		{
			dialog.setVisible( visible );
		}
	}

	public boolean validate()
	{
		return true;
	}

	public void setFormFieldProperty( String name, Object value )
	{
		for( XForm form : forms )
			form.setFormFieldProperty( name, value );
	}

	public String getValue( String field )
	{
		for( XForm form : forms )
		{
			if( form.getComponent( field ) != null )
				return form.getComponent( field ).getValue();
		}

		return null;
	}

	public void setValue( String field, String value )
	{
		for( XForm form : forms )
		{
			if( form.getComponent( field ) != null )
				form.getComponent( field ).setValue( value );
		}
	}

	public int getValueIndex( String name )
	{
		for( SwingXFormImpl form : forms )
		{
			if( form.getComponent( name ) != null )
			{
				Object[] options = form.getOptions( name );
				if( options == null )
					return -1;

				return Arrays.asList( options ).indexOf( form.getComponentValue( name ) );
			}
		}

		return -1;
	}

	public boolean show()
	{
		setReturnValue( XFormDialog.CANCEL_OPTION );
		show( new StringToStringMap() );
		return getReturnValue() == XFormDialog.OK_OPTION;
	}

	public void setWidth( int i )
	{
		dialog.setPreferredSize( new Dimension( i, ( int )dialog.getPreferredSize().getHeight() ) );
	}

	public void setSize( int w, int h )
	{
		dialog.setSize( w, h );
	}

	private boolean showPage( int pageNo )
	{
		currentPage = pageNo;
		String pageName = pageNames.get( currentPage );
		WizardPage page = controllers.get( pageName );

		descriptionPanel.setTitle( page.getName() );
		descriptionPanel.setDescription( page.getDescription() );
		cardLayout.show( pages, pageName );

		if( initPage( pageName, page ) )
		{
			actions.update();
			return true;
		}
		else
		{
			setVisible( false );
			return false;
		}
	}

	private boolean initPage( String pageName, WizardPage page )
	{
		try
		{
			dialog.setCursor( Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ) );
			return page.init();
		}
		catch( Exception e )
		{
			dialog.setCursor( Cursor.getDefaultCursor() );
			SoapUI.logError( e );
			UISupport.showInfoMessage( pageName + " could not be initialized", this.name );
			return false;
		}
		finally
		{
			dialog.setCursor( Cursor.getDefaultCursor() );
		}
	}

	private boolean runCurrentPage()
	{
		String pageName = pageNames.get( currentPage );
		WizardPage controller = controllers.get( pageName );
		try
		{
			dialog.setCursor( Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ) );
			return controller.run();
		}
		catch( Exception e )
		{
			dialog.setCursor( Cursor.getDefaultCursor() );
			SoapUI.logError( e );
			UISupport.showInfoMessage( pageName + " failed", this.name );
			return false;
		}
		finally
		{
			dialog.setCursor( Cursor.getDefaultCursor() );
		}
	}

	private class BackAction extends AbstractAction implements UpdateableAction
	{
		public BackAction()
		{
			super( "< Back" );
		}

		public void update()
		{
			boolean enable = false;
			if( currentPage > 0 )
			{
				String pageName = pageNames.get( currentPage - 1 );
				WizardPage prevPage = controllers.get( pageName );
				enable = prevPage.canGoBack();
			}
			setEnabled( enable );
		}

		public void actionPerformed( ActionEvent e )
		{
			showPage( currentPage - 1 );
		}
	}

	private class NextAction extends AbstractAction implements UpdateableAction
	{
		public NextAction()
		{
			super( "Next >" );
		}

		public void update()
		{
			setEnabled( currentPage + 1 < pageNames.size() );
		}

		public void actionPerformed( ActionEvent evt )
		{
			if( runCurrentPage() )
				showPage( currentPage + 1 );
			else
				setVisible( false );
		}
	}

	private final class CancelAction extends AbstractAction implements UpdateableAction
	{
		public CancelAction()
		{
			super( "Cancel" );
		}

		public void update()
		{
		}

		public void actionPerformed( ActionEvent e )
		{
			setReturnValue( XFormDialog.CANCEL_OPTION );
			setVisible( false );
		}
	}

	private final class FinishAction extends AbstractAction implements UpdateableAction
	{
		public FinishAction()
		{
			super( "Finish" );
		}

		public void update()
		{
			setEnabled( currentPage == pageNames.size() - 1 );
		}

		public void actionPerformed( ActionEvent e )
		{
			runCurrentPage();
			setReturnValue( XFormDialog.OK_OPTION );
			setVisible( false );
		}
	}

	public void release()
	{
		dialog.dispose();
	}
}
