package leaf.ui;

import info.clearthought.layout.TableLayout;
import info.clearthought.layout.TableLayoutConstraints;

import java.awt.Dimension;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.ScrollPaneConstants;

import leaf.data.OrderedMap;


public class LeafOptionDialog<KeyClass extends Object> extends JDialog
{
	private Dimension OPTION_SIZE = new Dimension( 200, 20 );
	private Dimension BUTTON_SIZE = new Dimension( 200, 20 );

	private int MAX_VISIBLE_OPTIONS = 20;

	private static final long serialVersionUID = 1L;

	private String CANCEL_LABEL = "Cancelar";

	private String okLabel = null;

	private final JLabel labelMessage = new JLabel();

	private final JPanel optionsPanel = new JPanel();

	private final JScrollPane optionsScrollPane = new JScrollPane();

	private boolean cancelActive = true;

	private LeafInputField<String> submitButton = new LeafInputField<String>();

	private Map<KeyClass, ? extends Object> map = null;

	private OrderedMap<KeyClass> orderedMap = null;

	private List<KeyClass> selected = new ArrayList<KeyClass>();

	private Map<KeyClass, Boolean> mapEnabledForKey = null;

	private Map<KeyClass, Boolean> mapChosenForKey = null;

	private String message = null;

	private boolean ordered = false;

	public LeafOptionDialog( LeafWindow owner, Map<KeyClass, ? extends Object> map, String message)
	{
		super( owner );
		this.mapEnabledForKey = new HashMap<KeyClass, Boolean>();
		this.mapChosenForKey = new HashMap<KeyClass, Boolean>();
		this.message = message;
		cancelActive = false;
		for( KeyClass key : map.keySet() )
		{
			mapEnabledForKey.put( key, true );
		}
		startup( map, null );
	}

	public LeafOptionDialog( LeafWindow owner, OrderedMap<KeyClass> orderedMap, Map<KeyClass, Boolean> chosen, Map<KeyClass, Boolean> enabled, String message, String okButton)
	{
		super( owner );
		ordered = true;
		this.message = message;
		okLabel = okButton;
		cancelActive = okButton != null;
		this.mapChosenForKey = chosen == null ? new HashMap<KeyClass, Boolean>() : chosen;
		this.mapEnabledForKey = enabled == null ? new HashMap<KeyClass, Boolean>() : chosen;
		if( chosen != null )
		{
			for( KeyClass key : chosen.keySet() )
			{
				Boolean isChosen = chosen.get( key );
				if( isChosen != null && isChosen )
				{
					selected.add( key );
				}
			}
		}
		startup( null, orderedMap );
	}

	private void startup( Map<KeyClass, ? extends Object> map, OrderedMap<KeyClass> orderedMap )
	{
		if( map == null )
		{
			this.map = new HashMap<KeyClass, Object>();
		}
		else
		{
			this.map = map;
		}
		if( orderedMap == null )
		{
			this.orderedMap = new OrderedMap<KeyClass>();
		}
		else
		{
			this.orderedMap = orderedMap;
		}
		setupComponents( map == null ? orderedMap.iterator() : map.keySet().iterator(), map == null ? orderedMap.rows() : map.keySet().size(), map == null ? true : false );
		setUndecorated( true );
		setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		getRootPane().setWindowDecorationStyle(JRootPane.NONE);
		setSize( getLayout().minimumLayoutSize( getRootPane() ) );
		setLocationRelativeTo( getParent() );
		setModal( true );
		setVisible( true );
	}

	private void setupComponents( Iterator<KeyClass> iterator, Integer size, boolean ordered )
	{
		double[] cols = new double[] {
			TableLayout.PREFERRED
		};
		double[] rows = new double[(message == null ? 0 : 2) + 1 + (cancelActive ? 2 : 0)];
		for( int i = 0; i < rows.length; ++i )
		{
			rows[i] = TableLayout.PREFERRED;
		}
		TableLayout layout = new TableLayout( cols, rows );
		layout.setVGap( 3 );
		setContentPane( new LeafGradientPanel() );
		getContentPane().setLayout( layout );

		int shift = 0;
		if( message != null )
		{
			labelMessage.setText( message );
			getContentPane().add( labelMessage, new TableLayoutConstraints( 0, shift++ ) );
			getContentPane().add( new JSeparator(), new TableLayoutConstraints( 0, shift++ ) );
		}
		setupOptionsComponents( iterator, size );
		getContentPane().add( optionsScrollPane, new TableLayoutConstraints( 0, shift++ ) );
		if( cancelActive )
		{
			getContentPane().add( new JSeparator(), new TableLayoutConstraints( 0, shift++ ) );
			submitButton.setObject( ordered ? okLabel : CANCEL_LABEL );
			submitButton.setClickable( true );
			submitButton.setPreferredSize( BUTTON_SIZE );
			getContentPane().add( submitButton, new TableLayoutConstraints( 0, shift++ ) );
			addListenerToComponent( submitButton );
		}
		((JComponent) getContentPane()).setBorder( BorderFactory.createRaisedBevelBorder() );
	}

	private void setupOptionsComponents( Iterator<KeyClass> iterator, Integer size )
	{
		int maxWidth = OPTION_SIZE.width;
		double[] cols = new double[] {
			TableLayout.PREFERRED
		};
		double[] rows = new double[size];
		for( int i = 0; i < rows.length; ++i )
		{
			rows[i] = TableLayout.PREFERRED;
		}
		TableLayout layout = new TableLayout( cols, rows );
		layout.setVGap( 3 );
		optionsPanel.setLayout( layout );
		KeyClass current = null;
		for( int i = 0; i < size && iterator.hasNext(); ++i )
		{
			current = iterator.next();
			LeafInputField<Object> component = new LeafInputField<Object>();
			Object value;
			if( ordered )
			{
				List<Object> values = orderedMap.getValues( current );
				value = (values == null || values.size() == 0) ? null : values.get( 0 );
			}
			else
			{
				value = map.get( current );
			}
			component.setObject( value );

			Boolean isChosen = mapChosenForKey.get( current );
			component.setSelected( isChosen != null && isChosen );

			Boolean isEnabled = mapEnabledForKey.get( current );
			component.setClickable( isEnabled == null || isEnabled );

			if(component.size != null && component.size.width > maxWidth )
			{
				maxWidth = component.size.width;
			}
			optionsPanel.add( component, new TableLayoutConstraints( 0, i ) );
			addListenerToComponent( component );
		}
		
		optionsScrollPane.setViewportView( optionsPanel );
		optionsScrollPane.setHorizontalScrollBarPolicy( ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER );
		JScrollBar verticalScrollBar = optionsScrollPane.getVerticalScrollBar();
		verticalScrollBar.setUnitIncrement( (int) OPTION_SIZE.getHeight() + 3  );
		optionsScrollPane.setVerticalScrollBar( verticalScrollBar );
		optionsScrollPane.setVerticalScrollBarPolicy( size > MAX_VISIBLE_OPTIONS ? ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS : ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER );
		optionsScrollPane.setPreferredSize( new Dimension( maxWidth + 30 , size > MAX_VISIBLE_OPTIONS ? (MAX_VISIBLE_OPTIONS * ((int) (OPTION_SIZE.getHeight() + 3 )) ) : (size * ((int) (OPTION_SIZE.getHeight() + 3 )) ) ));
	}

	private KeyClass getKeyForValue( Object value )
	{
		if( value != null && map.containsValue( value ) )
		{
			for( KeyClass key : map.keySet() )
			{
				if( map.get( key ).equals( value ) )
				{
					return key;
				}
			}
		}
		else if( ordered )
		{
			return orderedMap.getKeyForValue( value );
		}
		return null;
	}

	private void addListenerToComponent( JComponent component )
	{
		component.addPropertyChangeListener( new PropertyChangeListener()
		{
			@Override
			public void propertyChange( PropertyChangeEvent e )
			{
				if( e.getSource() instanceof LeafInputField )
				{
					LeafInputField<Object> source = (LeafInputField<Object>) e.getSource();
					if( LeafInputField.PROPERTY_CHANGED_CLICK.equals( e.getPropertyName() ) )
					{
						if( !source.equals( submitButton ) )
							{
							Object value = source.getObject();
							if( value != null )
							{
								KeyClass key = getKeyForValue( value );
								if( selected.contains( key ) )
								{
									selected.remove( key );
									source.setSelected( false );
								}
								else
								{
									selected.add( key );
									source.setSelected( true );
								}
							}
						}
						if( okLabel == null || source.equals( submitButton ) )
						{
							close();
						}
					}
				}
			}

		} );
	}

	public KeyClass getOption()
	{
		return selected.isEmpty() ? null : selected.get( 0 );
	}

	public List<KeyClass> getSelected()
	{
		return selected;
	}

	public void close()
	{
		setVisible( false );
		dispose();
	}

}
