/* -*- 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.Graphics2D;
import java.awt.geom.AffineTransform;
import java.math.BigDecimal;
import java.util.Comparator;
import java.util.Locale;


public class Handle {
	public String type;
	public String name;
	public String strValue;
	public String strNewValue;
	public int varIndex;
	public int startChar;
	public int endChar;
	public int newStartChar;
	public int newEndChar;
	public int line;
	int tabIndex;
	int decimalPlaces; // number of digits after the decimal point
	float incValue;

	java.lang.Number value, newValue;
	String strDiff;

	// connect with color control box
	ColorControlBox colorBox;

	// interface
	int x, y, width, height;
	int xCenter, xCurrent, xLast;
	HProgressBar progBar = null;
	String textFormat;

	// the client that sends the changes
	TweakClient tweakClient;


	public Handle(String t, String n, int vi, String v, int ti, int l, int sc,
								int ec, int dp) {
		type = t;
		name = n;
		varIndex = vi;
		strValue = v;
		tabIndex = ti;
		line = l;
		startChar = sc;
		endChar = ec;
		decimalPlaces = dp;

		incValue = (float) (1 / Math.pow(10, decimalPlaces));

		if ("int".equals(type)) {
			value = newValue = Integer.parseInt(strValue);
			strNewValue = strValue;
			textFormat = "%d";

		} else if ("hex".equals(type)) {
			Long val = Long.parseLong(strValue.substring(2, strValue.length()), 16);
			value = newValue = val.intValue();
			strNewValue = strValue;
			textFormat = "0x%x";

		} else if ("webcolor".equals(type)) {
			Long val = Long.parseLong(strValue.substring(1, strValue.length()), 16);
			val = val | 0xff000000;
			value = newValue = val.intValue();
			strNewValue = strValue;
			textFormat = "#%06x";

		} else if ("float".equals(type)) {
			value = newValue = Float.parseFloat(strValue);
			strNewValue = strValue;
			textFormat = "%.0" + decimalPlaces + "f";
		}

		newStartChar = startChar;
		newEndChar = endChar;
	}


	public void initInterface(int x, int y, int width, int height) {
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;

		// create drag ball
		progBar = new HProgressBar(height, width);
	}


	public void setCenterX(int mx) {
		xLast = xCurrent = xCenter = mx;
	}


	public void setCurrentX(int mx) {
		xLast = xCurrent;
		xCurrent = mx;

		progBar.setPos(xCurrent - xCenter);

		updateValue();
	}


	public void resetProgress() {
		progBar.setPos(0);
	}


	public void updateValue() {
		float change = getChange();

		if ("int".equals(type)) {
			if (newValue.intValue() + (int) change > Integer.MAX_VALUE ||
					newValue.intValue() + (int) change < Integer.MIN_VALUE) {
				change = 0;
				return;
			}
			setValue(newValue.intValue() + (int) change);
		} else if ("hex".equals(type)) {
			setValue(newValue.intValue() + (int) change);
		} else if ("webcolor".equals(type)) {
			setValue(newValue.intValue() + (int) change);
		} else if ("float".equals(type)) {
			setValue(newValue.floatValue() + change);
		}

		updateColorBox();
	}


	public void setValue(Number value) {
		if ("int".equals(type)) {
			newValue = value.intValue();
			strNewValue = String.format(Locale.US, textFormat, newValue.intValue());

		} else if ("hex".equals(type)) {
			newValue = value.intValue();
			strNewValue = String.format(Locale.US, textFormat, newValue.intValue());

		} else if ("webcolor".equals(type)) {
			newValue = value.intValue();
			// keep only RGB
			int val = (newValue.intValue() & 0xffffff);
			strNewValue = String.format(Locale.US, textFormat, val);

		} else if ("float".equals(type)) {
			BigDecimal bd = new BigDecimal(value.floatValue());
			bd = bd.setScale(decimalPlaces, BigDecimal.ROUND_HALF_UP);
			newValue = bd.floatValue();
			strNewValue = String.format(Locale.US, textFormat, newValue.floatValue());
		}

		// send new data to the server in the sketch
		sendNewValue();
	}


	public void updateColorBox() {
		if (colorBox != null) {
			colorBox.colorChanged();
		}
	}


	private float getChange() {
		int pixels = xCurrent - xLast;
		return pixels * incValue;
	}


	public void setPos(int nx, int ny) {
		x = nx;
		y = ny;
	}


	public void setWidth(int w) {
		width = w;
		progBar.setWidth(w);
	}


	public void draw(Graphics2D g2d, boolean hasFocus) {
		AffineTransform prevTrans = g2d.getTransform();
		g2d.translate(x, y);

		// draw underline on the number
		g2d.setColor(ColorScheme.getInstance().progressFillColor);
		g2d.drawLine(0, 0, width, 0);

		if (hasFocus) {
			if (progBar != null) {
				g2d.translate(width / 2, 2);
				progBar.draw(g2d);
			}
		}

		g2d.setTransform(prevTrans);
	}


	public boolean pick(int mx, int my) {
		return pickText(mx, my);
	}


	public boolean pickText(int mx, int my) {
		return (mx > x - 2 && mx < x + width + 2 && my > y - height && my < y);
	}


	public boolean valueChanged() {
		if ("int".equals(type)) {
			return (value.intValue() != newValue.intValue());
		} else if ("hex".equals(type)) {
			return (value.intValue() != newValue.intValue());
		} else if ("webcolor".equals(type)) {
			return (value.intValue() != newValue.intValue());
		} else {
			return (value.floatValue() != newValue.floatValue());
		}
	}


	public void setColorBox(ColorControlBox box) {
		colorBox = box;
	}


	public void setTweakClient(TweakClient client) {
		tweakClient = client;
	}


	public void sendNewValue() {
		int index = varIndex;
		try {
			if ("int".equals(type)) {
				tweakClient.sendInt(index, newValue.intValue());
			} else if ("hex".equals(type)) {
				tweakClient.sendInt(index, newValue.intValue());
			} else if ("webcolor".equals(type)) {
				tweakClient.sendInt(index, newValue.intValue());
			} else if ("float".equals(type)) {
				tweakClient.sendFloat(index, newValue.floatValue());
			}
		} catch (Exception e) {
			System.out.println("error sending new value!");
		}
	}


	public String toString() {
		return type + " " + name + " = " + strValue + " (tab: " + tabIndex
			+ ", line: " + line + ", start: " + startChar + ", end: "
			+ endChar + ")";
	}
}


/*
 * Used for sorting the handles by order of occurrence inside each tab
 */
class HandleComparator implements Comparator<Handle> {
	public int compare(Handle handle1, Handle handle2) {
		int tab = handle1.tabIndex - handle2.tabIndex;
		if (tab != 0) {
			return tab;
		}
		return handle1.startChar - handle2.startChar;
	}
}
