
import java.awt.Graphics;
import java.awt.Color;
import java.util.Vector;

public class ColorPaletteWidget extends CustomWidget {

	// The color palette looks like the following.
	// The window coordinates, in pixels, of the upper-left
	// corner of the handle are given by (x0,y0)
	//
	//    +---------------------------------------+
	//    |           this is the handle          |
	//    +---------+---------+---------+---------+
	//    |  this   | this is |         |  the    |
	//    |  is a   | another |   ...   |  last   |
	//    | swatch  | swatch  |         |  swatch |
	//    +---------+---------+---------+---------+


	static final int widthOfEachSwatch = 26; // in pixels
	static final int heightOfEachSwatch = 42; // in pixels
	static final int heightOfHandle = 14; // in pixels
	int x0 = 20, y0 = 20; // coordinates of upper left corner of handle
	int old_mouse_x, old_mouse_y, mouse_x, mouse_y;

	Vector< Color > colors = new Vector< Color >();
	int indexOfCurrentlySelectedColor = 0;

	int indexOfCurrentlyHilitedColor = -1; // -1 means none
	boolean isHandleHilited = false;

	// If this is true, the mouse is being dragged
	// in a drag that originated over the color palette.
	boolean isMouseBeingDragged = false;

	public ColorPaletteWidget() {
		isVisible = true;

		// rainbow colors
		colors.addElement( new Color( 255,   0,   0 ) );
		colors.addElement( new Color( 255, 127,   0 ) );
		colors.addElement( new Color( 255, 255,   0 ) );
		colors.addElement( new Color( 127, 255,   0 ) );
		colors.addElement( new Color(   0, 255,   0 ) );
		colors.addElement( new Color(   0, 255, 127 ) );
		colors.addElement( new Color(   0, 255, 255 ) );
		colors.addElement( new Color(   0, 127, 255 ) );
		colors.addElement( new Color(   0,   0, 255 ) );
		colors.addElement( new Color( 127,   0, 255 ) );
		colors.addElement( new Color( 255,   0, 255 ) );
		colors.addElement( new Color( 255,   0, 127 ) );

		// shades of gray
		colors.addElement( new Color(   0,   0,   0 ) );
		colors.addElement( new Color(  63,  63,  63 ) );
		colors.addElement( new Color( 127, 127, 127 ) );
		colors.addElement( new Color( 191, 191, 191 ) );
		colors.addElement( new Color( 255, 255, 255 ) );
	}

	Color getCurrentlySelectedColor() {
		final float DEFAULT_ALPHA = 0.3f; // transparency: 1 for opaque, 0 for invisible, 0.5f for 50% transparency
		Color c = colors.elementAt( indexOfCurrentlySelectedColor );
		return new Color(c.getRed(),c.getGreen(),c.getBlue(),(int)(DEFAULT_ALPHA*255));
	}

	private int widthOfWidget() {
		return colors.size() * widthOfEachSwatch;
	}
	private int heightOfWidget() {
		return heightOfHandle + heightOfEachSwatch;
	}

	public boolean isMouseOverWidget() {
		return
			x0 <= mouse_x
			&& mouse_x < x0 + widthOfWidget()
			&& y0 <= mouse_y
			&& mouse_y < y0 + heightOfWidget();
	}

	// Updates hiliting, and returns true if a redraw is necessary
	private boolean hasHilitingChanged() {
		int new_indexOfCurrentlyHilitedColor = -1;
		boolean new_isHandleHilited = false;
		if ( isMouseOverWidget() ) {
			if ( mouse_y-y0 < heightOfHandle ) {
				// mouse is over handle
				new_isHandleHilited = true;
			}
			else {
				// mouse is over a swatch
				new_indexOfCurrentlyHilitedColor
					= (mouse_x-x0)/widthOfEachSwatch;
			}
		}

		boolean hasHilitingChanged = false;
		if (
			new_indexOfCurrentlyHilitedColor != indexOfCurrentlyHilitedColor
			|| new_isHandleHilited != isHandleHilited
		) {
			hasHilitingChanged = true;
		}
		indexOfCurrentlyHilitedColor = new_indexOfCurrentlyHilitedColor;
		isHandleHilited = new_isHandleHilited;
		return hasHilitingChanged;
	}

	public int pressEvent( int x, int y ) {
		mouse_x = x;
		mouse_y = y;
		if ( isMouseOverWidget() ) {
			isMouseBeingDragged = true;
			if ( indexOfCurrentlyHilitedColor >= 0 ) {
				indexOfCurrentlySelectedColor = indexOfCurrentlyHilitedColor;
				return S_REDRAW;
			}
			return S_DONT_REDRAW;
		}
		return S_EVENT_NOT_CONSUMED;
	}

	public int releaseEvent( int x, int y ) {
		mouse_x = x;
		mouse_y = y;
		if ( isMouseBeingDragged ) {
			isMouseBeingDragged = false;
			return S_DONT_REDRAW;
		}
		return S_EVENT_NOT_CONSUMED;
	}

	public int moveEvent( int x, int y ) {
		mouse_x = x;
		mouse_y = y;
		if ( hasHilitingChanged() )
			return S_REDRAW;
		if ( isMouseOverWidget() )
			return S_DONT_REDRAW;
		return S_EVENT_NOT_CONSUMED;
	}

	public int dragEvent( int x, int y ) {
		old_mouse_x = mouse_x;
		old_mouse_y = mouse_y;
		mouse_x = x;
		mouse_y = y;
		if ( isMouseBeingDragged ) {
			if ( isHandleHilited ) {
				x0 += mouse_x - old_mouse_x;
				y0 += mouse_y - old_mouse_y;
				if ( x0 < 0 ) x0 = 0;
				if ( y0 < 0 ) y0 = 0;
				return S_REDRAW;
			}
		}
		return S_EVENT_NOT_CONSUMED;
	}

	private void drawTriangle( Graphics g, int index ) {
		// draw triangular cursor below currently selected color
		final int heightOfTriangle = 10;
		int [] xPoints = new int [ 3 ];
		int [] yPoints = new int [ 3 ];
		xPoints[0] = x0 + index*widthOfEachSwatch
			+ widthOfEachSwatch/2;
		yPoints[0] = y0 + heightOfHandle + heightOfEachSwatch
			+ heightOfTriangle/2;
		xPoints[1] = xPoints[0] - heightOfTriangle;
		yPoints[1] = yPoints[0] + heightOfTriangle;
		xPoints[2] = xPoints[0] + heightOfTriangle;
		yPoints[2] = yPoints[1];
		g.fillPolygon( xPoints, yPoints, 3 );
	}

	public void draw(
		Graphics g,
		int window_width_in_pixels,
		int window_height_in_pixels
	) {
		if ( ! isVisible )
			return;

		g.setColor( Color.black );
		g.drawRect(
			x0,
			y0,
			widthOfWidget() - 1,
			heightOfHandle - 1
		);
		if ( isHandleHilited ) {
			g.setColor( Color.lightGray );
			g.fillRect(
				x0 + 1,
				y0 + 1,
				widthOfWidget() - 2,
				heightOfHandle - 2
			);
		}

		// draw a triangle to indicate which swatch is currently selected
		g.setColor( Color.black );
		drawTriangle( g, indexOfCurrentlySelectedColor );

		if (
			indexOfCurrentlyHilitedColor >= 0
			&& indexOfCurrentlyHilitedColor != indexOfCurrentlySelectedColor
		) {
			// draw a triangle for the hilited swatch
			g.setColor( Color.gray );
			drawTriangle( g, indexOfCurrentlyHilitedColor );
		}

		for ( int i = 0; i < colors.size(); ++i ) {
			g.setColor( Color.black );
			g.drawRect(
				x0 + i*widthOfEachSwatch,
				y0 + heightOfHandle,
				widthOfEachSwatch - 1,
				heightOfEachSwatch - 1
			);
			g.setColor( colors.elementAt( i ) );
			g.fillRect(
				x0 + i*widthOfEachSwatch + 1,
				y0 + heightOfHandle + 1,
				widthOfEachSwatch - 2,
				heightOfEachSwatch - 2
			);
		}
	}

}


