package siprp.medicina.processo.ui;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Map;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JTextArea;

public class LeafInputField<ObjClass extends Object> extends JTextArea
{
	private static final long serialVersionUID = 1L;

	private static final DateFormat sdf = DateFormat.getDateInstance( DateFormat.SHORT, new Locale( "pt", "PT" ) );

	private static final int defaultColorDec = 20;

	private int colorDec = defaultColorDec;

	private Color hot = null;
	private Color cold = null;
	private Color background = null;
	private Color startColor = null;
	private Color endColor = null;

	private Object selectedOption = null;

	private boolean isError = false;

	private boolean isEditable = false;

	private ObjClass object = null;

	private GradientPaint outerContour = null;
	private GradientPaint outerReversedContour = null;
	private GradientPaint innerContour = null;
	private GradientPaint innerReversedContour = null;
	private GradientPaint gradient = null;

	public LeafInputField()
	{
		super();
		setOpaque( false );
		super.setEditable( false );
		setBorder( BorderFactory.createEmptyBorder( 3, 10, 3, 10 ) );
		setBackground( Color.WHITE );
		hot = getBackground();
		cold = new Color( hot.getRed() > colorDec ? hot.getRed() - colorDec : 0, hot.getGreen() > colorDec ? hot.getGreen() - colorDec : 0, hot.getBlue() > colorDec ? hot.getBlue() - colorDec : 0, hot.getAlpha() );
		background = cold;

		this.addFocusListener( new FocusListener()
		{
			@Override
			public void focusLost( FocusEvent e )
			{
				repaint();
			}

			@Override
			public void focusGained( FocusEvent e )
			{
				repaint();
			}
		} );

		this.addMouseListener( new MouseListener()
		{

			@Override
			public void mouseReleased( MouseEvent e )
			{
			}

			@Override
			public void mousePressed( MouseEvent e )
			{
			}

			@Override
			public void mouseExited( MouseEvent e )
			{
				background = isEditable ? cold : background;
				repaint();
			}

			@Override
			public void mouseEntered( MouseEvent e )
			{
				background = isEditable ? hot : background;
				repaint();
			}

			@Override
			public void mouseClicked( MouseEvent e )
			{
				ObjClass obj = object;
				if( obj != null && isEditable )
				{
					if( obj instanceof Date )
					{
						obj = (ObjClass) getDateFromUser();
					}
					else if( obj instanceof Map )
					{
						ObjClass out = (ObjClass) getOptionFromUser();
						selectedOption = out == null ? selectedOption : out;
						obj = object;
					}
					else if( obj instanceof String )
					{
						obj = (ObjClass) getStringFromUser();
					}
					if( obj != null )
					{
						setObject( obj );
					}
				}
			}

		} );

	}

	@Override
	public void setText( String text )
	{
		try
		{
			setObject( (ObjClass) text );
		} catch( ClassCastException ex )
		{
			ex.printStackTrace();
		}
	}

	private String getStringFromUser()
	{
		LeafTextDialog textDialog = new LeafTextDialog( getParentFrame(), this, (String) object );
		return textDialog.getText();
	}

	private Object getOptionFromUser()
	{
		LeafOptionDialog optionDialog = new LeafOptionDialog( getParentFrame(), this, (Map) object );
		return optionDialog.getOption();
	}

	private Date getDateFromUser()
	{
		LeafCalendarDialog calendarDialog = new LeafCalendarDialog( getParentFrame(), this );
		return calendarDialog.getDate();
	}

	private JFrame getParentFrame()
	{
		if( getRootPane() != null && getRootPane().getParent() instanceof JFrame )
		{
			return (JFrame) getRootPane().getParent();
		}
		return null;
	}

	/**
	 * Sets the component to an error state appearance
	 * 
	 * @param error
	 */
	public void setError( boolean error )
	{
		isError = error;
	}

	public boolean isError()
	{
		return isError;
	}

	protected void paintComponent( Graphics g )
	{
		Graphics2D g2d = (Graphics2D) g;
		int width = getWidth();
		int height = getHeight();
		setColors( width, height );

		g2d.setPaint( outerReversedContour );
		g2d.fillRect( 15, 0, width / 4, height );
		g2d.setPaint( outerContour );
		g2d.fillRect( width / 4, 0, width, height );
		g2d.setPaint( innerReversedContour );
		g2d.fillRect( 15, 0, width / 4, height - 1 );
		g2d.setPaint( innerContour );
		g2d.fillRect( width / 4, 0, width, height - 1 );
		g2d.setPaint( gradient );
		g2d.fillRoundRect( 0, 2, width, height - 5, 15, 15 );
		super.paintComponent( g );
	}

	private void setColors( int width, int height )
	{
		startColor = getGradientStartColor();
		endColor = getGradientEndColor( startColor );
		outerContour = new GradientPaint( width / 4, 0, Color.GRAY, width, height, this.getParent().getBackground() );
		outerReversedContour = new GradientPaint( 15, 0, this.getParent().getBackground(), width / 4, height, Color.GRAY );
		innerContour = new GradientPaint( width / 4, 0, Color.LIGHT_GRAY, width, height, this.getParent().getBackground() );
		innerReversedContour = new GradientPaint( 15, 0, this.getParent().getBackground(), width / 4, height, Color.LIGHT_GRAY );
		gradient = new GradientPaint( 0, 0, startColor, width, height, endColor );
	}

	private Color getGradientStartColor()
	{
		Color result = background;
		/*
		 * int red = result.getRed(), green = result.getGreen(), blue =
		 * result.getBlue(), alpha = result.getAlpha(); if( isError() ) { result =
		 * new Color( red, green > colorDec ? (green - colorDec) : 0, (blue >
		 * colorDec) ? (blue - colorDec) : 0, alpha ); } else if( isEditable() ) {
		 * result = new Color( red > colorDec ? (red - colorDec) : 0, green,
		 * blue > colorDec ? (blue - colorDec) : 0, alpha ); }
		 */
		return result;
	}

	private Color getGradientEndColor( Color startColor )
	{
		return getParent() != null ? getParent().getBackground() : startColor;
	}

	@Override
	public void setEditable( boolean editable )
	{
		isEditable = editable;
	}

	@Override
	public boolean isEditable()
	{
		return isEditable;
	}

	public void setSelectedObject( Object key )
	{
		selectedOption = key;
		Object value = null;
		if( key != null && object instanceof Map )
		{
			value = ((Map) object).get( selectedOption );
		}
		super.setText( value == null ? " " : value.toString() );
	}

	public Object getSelectedObject()
	{
		return selectedOption;
	}

	public void setObject( ObjClass object )
	{
		this.object = object;
		if( object != null )
		{
			if( object instanceof Date )
			{
				super.setText( ((Date) object).getTime() <= 0 ? " " : sdf.format( object ) );
			}
			else if( object instanceof Map )
			{
				setSelectedObject( selectedOption );
			}
			else
			{
				super.setText( object.toString() );
			}
		}
		else
		{
			super.setText( " " );
		}
	}

	public ObjClass getObject()
	{
		return this.object;
	}
}