package j3deditor.bin.components;

import j3deditor.bin.util.Utilities;

import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Robot;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import java.awt.event.MouseMotionListener;

import java.util.ArrayList;

import javax.swing.JPanel;


/**
 * Button that reacts on mouse clicks or drags.
 *
 * @author Risto Seene
 * 
 * @see SimpleButtonEvent
 * @see SimpleButtonListener
 */
public class SimpleButton extends JPanel implements MouseListener, MouseMotionListener{


	/** specifies that this button is clickable button. */
	public static final int CLICK = 0;

	/** specifies that this button is horizontally dragable button. */
	public static final int HORIZONTAL_DRAG = 1;

	/** specifies that this button is vertically dragable button. */
	public static final int VERTICAL_DRAG = 2;

	/** specifies that this button is dragable button on both axes. */
	public static final int MULTIPLE_DRAG = 3;
	
	/** specifies that an image will be applied to the "default" state of this button. */
	public static final int NORMAL_IMAGE = 10;
	
	/** specifies that an image will be applied to the "mouse over" state of this button. */
	public static final int MOUSE_OVER_IMAGE = 11;
	
	/** specifies that an image will be applied to the "mouse down" state of this button. */
	public static final int MOUSE_DOWN_IMAGE = 12;
	
	/** specifies that an image will be applied to the "disabled" state of this button. */
	public static final int DISABLED_IMAGE = 13;


	/** list of listeners */
	private ArrayList<SimpleButtonListener> listeners;

	/** the type of this button */
	private int type;

	/** specifies if the cursor is currently on this button */
	private boolean isOver = false;

	/** position of cursor */
	private Point position;


	/** specifies if this button is currently enabled or disabled */
	private boolean isEnabled = true;


	/** currently shown image on this button */
	private Image current;

	/** image shown when the button is idle */
	private Image defaultImg = null;

	/** image shown when the button is disabled */
	private Image offImg = null;

	/** image shown when the cursor is on this button */
	private Image mOver = null;

	/** image shown when the mouse is pressed on this button */
	private Image mDown = null;

	/** information about this button */
	private String buttonInfo = null;


	/**
	 * Creates an instance of <code>SimpleButton</code>
	 * of the given type.
	 *
	 * @param type
	 *	the type of this button: CLICK, HORIZONTAL_DRAG, VERTICAL_DRAG, MULTIPLE_DRAG
	 */
	public SimpleButton(int type){
		this.type = type;
		this.listeners = new ArrayList<SimpleButtonListener>();

		setOpaque(false);
		addMouseListener(this);

		if(type != 0){
			addMouseMotionListener(this);
		}

		setCursor();
	}


	/**
	 * Applies the given image to the specified state of this button.
	 * Loads the images from this applications JAR file.
	 *
	 * @param fileName
	 *	name of the image file
	 * @param type
	 * 	type of the image: NORMAL_IMAGE, MOUSE_OVER_IMAGE, MOUSE_DOWN_IMAGE, DISABLED_IMAGE
	 */
	public void setImage(String fileName, int type){
		if(type == 10){
			defaultImg = Utilities.image.getImage("buttons/" + fileName);
			current = defaultImg;
		} else if(type == 11){
			mOver = Utilities.image.getImage("buttons/" + fileName);
		} else if(type == 12){
			mDown = Utilities.image.getImage("buttons/" + fileName);
		} else if(type == 13){
			offImg = Utilities.image.getImage("buttons/" + fileName);
		}
	}
	
	
	/**
	 * Applies the given image to the specified state of this button.
	 *
	 * @param image
	 *        image that will be applied to this button
	 * @param type
	 *        type of the image: NORMAL_IMAGE, MOUSE_OVER_IMAGE, MOUSE_DOWN_IMAGE, DISABLED_IMAGE
	 */
	public void setImage(Image image, int type){
	          if(type == 10){
	                    defaultImg = image;
	                    current = defaultImg;
	          } else if(type == 11){
	                    mOver = image;
	          } else if(type == 12){
	                    mDown = image;
	          } else if(type == 13){
	                    offImg = image;
	          }
	}


	/**
	 * Enables/disables this button.
	 *
	 * @param isEnabled
	 *	if <code>true</code> enables this button,
	 *	otherwise disables
	 */
	public void setEnabled(boolean isEnabled){
		this.isEnabled = isEnabled;

		if(offImg != null){
			current = offImg;
		} else {
			current = defaultImg;
		}

		repaint();
	}


	/**
	 * Adds the given listener to this button.
	 * 
	 * @param listener
	 * 	listener that will be added to this button
	 */
	public void addSimpleButtonListener(SimpleButtonListener listener){
		listeners.add(listener);
	}


	/**
	 * Removes the specified listener.
	 * 
	 * @param listener
	 * 	listener that will be removed from this button
	 */
	public void removeSimpleButtonListener(SimpleButtonListener listener){
		listeners.remove(listeners.indexOf(listener));
	}


	/**
	 * Sets the cursor type according to the type of the button.
	 */
	private void setCursor(){
		Cursor cursor;

		if(type == 1){
			cursor = new Cursor(Cursor.W_RESIZE_CURSOR);
		} else if(type == 2){
			cursor = new Cursor(Cursor.N_RESIZE_CURSOR);
		} else if(type == 3){
			cursor = new Cursor(Cursor.MOVE_CURSOR);
		} else {
			cursor = getCursor();
		}

		setCursor(cursor);
	}


	/**
	 * Paints the background image of this button
	 * according to current state.
	 * 
	 * @param graphics
	 * 	<code>Graphics</code> object sent by method caller
	 */
	@Override
	protected void paintComponent(Graphics graphics){
		Graphics2D g = (Graphics2D)graphics;
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		super.paintComponent(g);

		g.drawImage(current, 0, 0, this);
	}


	/**
	 * Changes the state of this button and memorizes the current position of cursor.
	 * 
	 * @param e
	 * 	<code>MouseEvent</code> object that describes occured mouse event
	 */
	public void mousePressed(MouseEvent e){
		position = new Point(e.getX(), e.getY());

		if(isEnabled && mDown != null){
			current = mDown;
			repaint();
		}
	}


	/**
	 * Changes the state of this button.
	 * 
	 * @param e
	 * 	<code>MouseEvent</code> object that describes occured mouse event
	 */
	public void mouseReleased(MouseEvent e){
		if(isEnabled && mOver != null && isOver){
			current = mOver;
			repaint();
		} else if(isEnabled){
			current = defaultImg;
			repaint();
		}

		if(type != 0){
			for(int i = 0; i < listeners.size(); i++){
				listeners.get(i).buttonDragEnded(new SimpleButtonEvent(this));
			}
		}
	}


	/**
	 * Notifies listeners if this is clickable button.
	 * 
	 * @param e
	 * 	<code>MouseEvent</code> object that describes occured mouse event
	 */
	public void mouseClicked(MouseEvent e){
		if(isEnabled && type == 0){
			for(int i = 0; i < listeners.size(); i++){
				listeners.get(i).buttonClicked(new SimpleButtonEvent(this));
			}
		}
	}


	/**
	 * Changes the state of this button.
	 * 
	 * @param e
	 * 	<code>MouseEvent</code> object that describes occured mouse event
	 */
	public void mouseEntered(MouseEvent e){
		isOver = true;
		if(isEnabled){
			if(mOver != null){
				current = mOver;
				repaint();
			}

			for(int i = 0; i < listeners.size(); i++){
				listeners.get(i).buttonEntered(new SimpleButtonEvent(this));
			}
		}
	}

	/**
	 * Changes the state of this button.
	 * 
	 * @param e
	 * 	<code>MouseEvent</code> object that describes occured mouse event
	 */
	public void mouseExited(MouseEvent e){
		isOver = false;
		if(isEnabled){
			current = defaultImg;
			repaint();

			for(int i = 0; i < listeners.size(); i++){
				listeners.get(i).buttonExited(new SimpleButtonEvent(this));
			}
		}
	}


	/**
	 * Doesn't do anything.
	 * 
	 * @param e
	 * 	<code>MouseEvent</code> object that describes occured mouse event
	 */
	public void mouseMoved(MouseEvent e){
	}


	/**
	 * Calculates drag distance and notifies listeners
	 * if this is draggable button.
	 * 
	 * @param e
	 * 	<code>MouseEvent</code> object that describes occured mouse event
	 */
	public void mouseDragged(MouseEvent e){
		if(isEnabled && type != 0){
			float x = (float)(position.getX() - e.getX());
			float y = (float)(position.getY() - e.getY());

			try{
				Robot r = new Robot();
				Point loc = getLocationOnScreen();
				loc.translate((int)position.getX(), (int)position.getY());
				r.mouseMove((int)loc.getX(), (int)loc.getY());
			} catch(Throwable t){
			}

			for(int i = 0; i < listeners.size(); i++){
				listeners.get(i).buttonDragged(new SimpleButtonEvent(this, x, y));
			}
		}
	}


	/**
	 * Sets the information text that describes this button.
	 *
	 * @param info
	 *	input text
	 */
	public void setButtonInfo(String info){
		buttonInfo = info;
	}


	/**
	 * Returns the information text that describes this button.
	 *
	 * @return Returns the information text that describes this button
	 */
	public String getButtonInfo(){
		return buttonInfo;
	}

}
