/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */

/*
	Part of the Processing project - http://processing.org
	Copyright (c) 2012-15 The Processing Foundation

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License version 2
	as published by the Free Software Foundation.

	This program 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 General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software Foundation, Inc.
	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package processing.mode.java.tweak;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;

import javax.swing.Box;
import javax.swing.JComponent;
import javax.swing.JFrame;


public class ColorSelector {
	int hue, saturation, brightness;

	public JFrame frame;
	public ColorControlBox colorBox;
	ColorSelectorBox selectorBox;
	ColorSelectorSlider selectorSlider;
	SelectorTopBar topBar;


	public ColorSelector(ColorControlBox colorBox) {
		this.colorBox = colorBox;
		createFrame();
	}


	public void createFrame() {
		frame = new JFrame();
		frame.setBackground(Color.BLACK);

		Box box = Box.createHorizontalBox();
		box.setBackground(Color.BLACK);

		selectorSlider = new ColorSelectorSlider();

		if (!colorBox.isBW) {
			selectorBox = new ColorSelectorBox();
			box.add(selectorBox);
		}

		box.add(Box.createHorizontalGlue());
		box.add(selectorSlider, BorderLayout.CENTER);
		box.add(Box.createHorizontalGlue());

		frame.getContentPane().add(box, BorderLayout.CENTER);
		frame.pack();
		frame.setResizable(false);
		frame.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
	}


	public void show(int x, int y) {
		frame.setLocation(x, y);
		frame.setVisible(true);
		frame.repaint();
	}


	public void hide() {
		this.colorBox = null;
		frame.setVisible(false);
	}


	public void refreshColor() {
		if (!colorBox.ilegalColor) {
			setColor(colorBox.color);
		}
	}


	public void setColor(Color c) {
		if (selectorBox != null) {
			selectorBox.setToColor(c);
		}
		selectorSlider.setToColor(c);
		repaintSelector();
	}


	public void satBrightChanged() {
		repaintSelector();
	}


	public void hueChanged() {
		if (selectorBox != null) {
			selectorBox.renderBack();
		}
		repaintSelector();
	}


	public void repaintSelector() {
		if (selectorBox != null) {
			selectorBox.repaint();
		}
		selectorSlider.repaint();
	}


	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


	class ColorSelectorBox extends JComponent {
		int lastX, lastY;
		BufferedImage backImg;

		ColorSelectorBox() {
			if (!colorBox.ilegalColor) {
				setToColor(colorBox.color);
			}
			renderBack();

			addMouseListener(new MouseAdapter() {
				public void mousePressed(MouseEvent e) {
					updateMouse(e);
				}
			});
			addMouseMotionListener(new MouseAdapter() {
				public void mouseDragged(MouseEvent e) {
					updateMouse(e);
				}
			});
		}


		public void paintComponent(Graphics g) {
			g.drawImage(backImg, 0, 0, this);

			Graphics2D g2 = (Graphics2D) g;
			// otherwise the oval is hideous
			// TODO make a proper hidpi version of all this
			g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
													RenderingHints.VALUE_ANTIALIAS_ON);

			g.setColor(lastY < 128 ? Color.BLACK : Color.WHITE);
			AffineTransform tx = g2.getTransform();
			g2.translate(lastX, lastY);
			//g2.drawOval(0, 0, 5, 5);
			g2.drawOval(-3, -3, 6, 6);
			g2.drawLine(-8, 0, -6, 0);
			g2.drawLine(6, 0, 8, 0);
			g2.drawLine(0, -8, 0, -6);
			g2.drawLine(0, 6, 0, 8);
			g2.setTransform(tx);
		}


		public void renderBack() {
			int[] pixels = new int[256 * 256];
			int index = 0;
			for (int j = 0; j < 256; j++) {
				for (int i = 0; i < 256; i++) {
					pixels[index++] =	// color(hue, i, 255-j);
						Color.HSBtoRGB(hue / 255f, (i / 255f), (255-j)/255f);
				}
			}
			backImg = new BufferedImage(256, 256, BufferedImage.TYPE_INT_RGB);
			backImg.getRaster().setDataElements(0, 0, 256, 256, pixels);
		}


		public void setToColor(Color c) {
			// set selector color
			float hsb[] = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null);
			saturation = (int) (hsb[1] * 255);
			brightness = (int) (hsb[2] * 255);
			lastX = saturation;
			lastY = 255 - brightness;
		}


		void updateMouse(MouseEvent event) {
			int mouseX = event.getX();
			int mouseY = event.getY();

			if (mouseX >= 0 && mouseX < 256 &&
					mouseY >= 0 && mouseY < 256) {
				lastX = mouseX;
				lastY = mouseY;
				updateColor();
			}
		}


		void updateColor() {
			saturation = lastX;
			brightness = 255 - lastY;

			satBrightChanged();
			colorBox.selectorChanged(hue, saturation, brightness);
		}


		public Dimension getPreferredSize() {
			return new Dimension(256, 256);
		}


		public Dimension getMinimumSize() {
			return getPreferredSize();
		}


		public Dimension getMaximumSize() {
			return getPreferredSize();
		}
	}


	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


	class ColorSelectorSlider extends JComponent {
		final int WIDE = 30;
		BufferedImage backImg;
		int lastY;

		ColorSelectorSlider() {
//			size(30, 255);
//			noLoop();
//			colorMode(HSB, 255, 255, 255);
//			strokeWeight(1);
//			noFill();
//			loadPixels();
			if (!colorBox.ilegalColor) {
				setToColor(colorBox.color);
			}

			// draw the slider background
			renderBack();

			addMouseListener(new MouseAdapter() {
				public void mousePressed(MouseEvent e) {
					updateMouse(e);
				}
			});
			addMouseMotionListener(new MouseAdapter() {
				public void mouseDragged(MouseEvent e) {
					updateMouse(e);
				}
			});
		}

		public void paintComponent(Graphics g) {
			g.drawImage(backImg, 0, 0, this);

			Graphics2D g2 = (Graphics2D) g;
//			if (colorBox.isBW) {
//				stroke(lastY<128 ? 0 : 255);
//			}
//			else {
//				stroke(0);
//			}
			if (colorBox.isBW && lastY >= 128) {
				g2.setColor(Color.WHITE);
			} else {
				g2.setColor(Color.BLACK);
			}

			AffineTransform tx = g2.getTransform();
			g2.translate(0, lastY);
			// draw left bracket
//			beginShape();
//			vertex(5, -2);
//			vertex(1, -2);
//			vertex(1, 2);
//			vertex(5, 2);
//			endShape();
			g.drawRect(1, -2, 6, 4);

			// draw middle lines
			g.drawLine(13, 0, 17, 0);
			g.drawLine(15, -2, 15, 2);

			// draw right bracket
//			beginShape();
//			vertex(24, -2);
//			vertex(28, -2);
//			vertex(28, 2);
//			vertex(24, 2);
//			endShape();
			g.drawRect(24, -2, 4, 4);
			g2.setTransform(tx);

			/*
			if (colorBox.isBW) {
//				stroke(255);
//				rect(0, 0, 29, 254);
				g.setColor(Color.WHITE);
				g.drawRect(0, 0, WIDE, 255);
			} else {
//				stroke(0);
//				line(0, 0, 0, 255);
//				line(29, 0, 29, 255);
				g.setColor(Color.BLACK);
				g.drawLine(0, 0, 0, 255);
				g.drawLine(29, 0, 29, 255);
			}
			*/
		}


		void renderBack() {
			int[] pixels = new int[WIDE * 256];
			int index = 0;

			int argb = 0;
			for (int j = 0; j < 256; j++) {
				if (colorBox.isBW) {
					int gray = 255 - j;
					argb = 0xff000000 | (gray << 16) | (gray << 8) | gray;
				} else {
					// color(255-j, 255, 255);
					argb = Color.HSBtoRGB((255 - j) / 255f, 1, 1);
				}
				for (int i = 0; i < WIDE; i++) {
					pixels[index++] = argb;
				}
			}
			backImg = new BufferedImage(WIDE, 256, BufferedImage.TYPE_INT_RGB);
			backImg.getRaster().setDataElements(0, 0, WIDE, 256, pixels);
		}


		void setToColor(Color c) {
			// set slider position
			if (colorBox.isBW) {
				hue = c.getRed();
			} else {
				float hsb[] = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null);
				hue = (int)(hsb[0]*255);
			}
			lastY = 255 - hue;
		}


		void updateMouse(MouseEvent event) {
			int mouseY = event.getY();
			if (mouseY >= 0 && mouseY < 256) {
				lastY = mouseY;
				updateColor();
			}
		}


		public void updateColor() {
			hue = 255 - lastY;
			hueChanged();
			colorBox.selectorChanged(hue, saturation, brightness);
		}


		public Dimension getPreferredSize() {
			return new Dimension(30, 255);
		}


		public Dimension getMinimumSize() {
			return getPreferredSize();
		}


		public Dimension getMaximumSize() {
			return getPreferredSize();
		}
	}


	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


	public class SelectorTopBar extends JComponent {
		int barWidth;
		int barHeight = 16;

		public SelectorTopBar(int w) {
			barWidth = w;
		}

		@Override
		public void paintComponent(Graphics g) {
			g.setColor(Color.GRAY);
			Dimension size = getSize();
			g.fillRect(0, 0, size.width, size.height);
		}

		public Dimension getPreferredSize() {
			return new Dimension(barWidth, barHeight);
		}

		public Dimension getMinimumSize() {
			return getPreferredSize();
		}

		public Dimension getMaximumSize() {
			return getPreferredSize();
		}
	}
}
