var TColorPicker = Class.create(TemplatedElement, {
	currentColor: '',
	defaultColor: '000000',
	inputName: 'color',
	currH: 0, currS: 0, currV: 0,

	initialize: function (input, options) {
		Object.extend (this, options || {});
		this._buildFromTemplate('template_color_picker');
		this.colorInput = $(input);
		this.colorInput.type = 'hidden';
		this.colorInput.parentNode.insertBefore(this.element, this.colorInput);

		TColorPicker.Color.buildHArray();
		this.setColorHex(this.defaultColor);
		this.visible = false;
		this._updateHDrag = this.updateHDrag.bindAsEventListener(this);
		this._endHDrag = this.endHDrag.bindAsEventListener(this);
		this._updateSVDrag = this.updateSVDrag.bindAsEventListener(this);
		this._endSVDrag = this.endSVDrag.bindAsEventListener(this);
		this._onCustomColorChanged = this.onCustomColorChanged.bind(this);
	},

	showhideBox: function (event) {
		this.visible = !this.visible;
		this.dropdownElement.style.display = (this.visible?'block':'none');
	},
	setColor: function (color) {
		this.setColorHex (color);
	},
	setColorHex: function (color) {
		if (!this.isValidColor(color)) return;
		this.currentColor = color;

		var o = TColorPicker.Color.HEX_HSV(color);
		this.currH = o.H; this.currS = o.S; this.currV = o.V;

		var sh = (165 - TColorPicker.Color.normalize(165,360,this.currH));

		this.hDraggable.style.top = sh - 2 + "px";
		this.svDraggable.style.top = 162 - TColorPicker.Color.normalize(162,100,this.currV) + "px";
		this.svDraggable.style.left = TColorPicker.Color.normalize(162,100,this.currS) + "px";

		this.updateHue(sh);
	},
	isValidColor: function(color) {
		return (/^[0-9A-F]{6}$/i.test(color));
	},
	startHDrag: function (event) {
		if (this._hdrag){ this.endHDrag(event); return }
		this._hdsyd = Event.pointerY(event) - this.hDraggable.offsetTop;
		Event.observe (document, 'mousemove', this._updateHDrag);
		Event.observe (document, 'mouseup', this._endHDrag);
		Event.stop(event);
		this._hdrag = true;
	},
	updateHDrag: function (event) {
		if (!this._hdrag) return;
		var nt = Event.pointerY(event) - this._hdsyd;
		if (nt < 0) nt = 0;
		if (nt > 165) nt = 165;
		this.hDraggable.style.top = nt - 2 + "px";
		Event.stop(event);
		this.updateHue(nt);
	},
	endHDrag: function (event) {
		if (!this._hdrag) return;
		this._hdrag = false;

		Event.stopObserving (document, 'mousemove', this._updateHDrag);
		Event.stopObserving (document, 'mouseup', this._endHDrag);
		Event.stop(event);
	},
	onClickH: function (event) {
		if (!this._hdrag) {
			var p = Position.cumulativeOffset(this.hValues);
			this.hDraggable.style.top = Event.pointerY(event) - p[1] - 2 + "px";
			this.startHDrag(event);
		}
		this.updateHDrag(event);
	},

	startSVDrag: function (event) {
		if (this._svdrag){ this.endSVDrag(event); return }
		this._svdsyd = Event.pointerY(event) - this.svDraggable.offsetTop;
		this._svdsxd = Event.pointerX(event) - this.svDraggable.offsetLeft;
		Event.observe (document, 'mousemove', this._updateSVDrag);
		Event.observe (document, 'mouseup', this._endSVDrag);
		Event.stop(event);
		this._svdrag = true;
	},
	updateSVDrag: function (event) {
		if (!this._svdrag) return;
		var nt = Event.pointerY(event) - this._svdsyd;
		var nl = Event.pointerX(event) - this._svdsxd;
		if (nt < 0) nt = 0;  if (nt > 165) nt = 165;
		if (nl < 0) nl = 0;  if (nl > 165) nl = 165;

		this.svDraggable.style.top = nt - 3 + "px";
		this.svDraggable.style.left = nl - 3 + "px";
		Event.stop(event);
		this.currS = TColorPicker.Color.normalize(100, 162, nl);
		this.currV = TColorPicker.Color.normalize(100, 162, 165 - nt);
		this.updateColor();
	},
	endSVDrag: function (event) {
		if (!this._svdrag) return;
		this._svdrag = false;

		Event.stopObserving (document, 'mousemove', this._updateSVDrag);
		Event.stopObserving (document, 'mouseup', this._endSVDrag);
		Event.stop(event);
	},
	onClickSV: function (event) {
		if (!this._svdrag) {
			var p = Position.cumulativeOffset(this.svBox);
			this.svDraggable.style.left = Event.pointerX(event) - p[0] - 3 + "px";
			this.svDraggable.style.top = Event.pointerY(event) - p[1] - 3 + "px";
			this.startSVDrag(event);
		}
		this.updateSVDrag(event);
	},

	updateHue: function (value) {
		if (arguments.length == 0) value = this.hDraggable.offsetTop + 2;
		this.currH = TColorPicker.Color.normalize(360, 163, 163-value);
		this.svBox.style.backgroundColor = '#' + TColorPicker.Color.hArray[value];
		this.updateColor();
	},

	updateColor: function () {
		var c = TColorPicker.Color.HSV_HEX({S:this.currS, H: this.currH, V: this.currV});
		this.currentColor = c;
		this.previewElement.style.backgroundColor = this.selectedColor.style.backgroundColor = '#' + c;
		this.selectedColor.style.color = '#' + TColorPicker.Color.HSV_HEX(TColorPicker.Color.invert ({S:this.currS, H: this.currH, V: this.currV}));
		this.selectedColorText.innerHTML = '#' + c;
		this.colorInput.value = c;
		if (this.onColorChanged) this.onColorChanged();
	},
	onCustomColorChange: function () {
		if (this._ccct) clearTimeout(this._ccct);
		this._ccct = setTimeout(this._onCustomColorChanged, 200);
	},
	onCustomColorChanged: function () {
		var nc = this.customColorInput.value;
		if (this.isValidColor(nc)) {
			this.setColorHex(nc);
			Element.removeClassName(this.customColorInput, 'error');
		} else {
			this.setColorHex(this.defaultColor);
			Element.addClassName(this.customColorInput, 'error');
		}
	}
});
TColorPicker.Color = {
	cords: function(W) {
		var W2 = W / 2, rad = (hsv.H / 360) * (Math.PI * 2), hyp = (hsv.S + (100 - hsv.V)) / 100 * (W2 / 2);

		$S('mCur').left=Math.round(Math.abs(Math.round(Math.sin(rad)*hyp)+W2+3))+'px';
		$S('mCur').top=Math.round(Math.abs(Math.round(Math.cos(rad)*hyp)-W2-21))+'px';

	},
	HEX_RGB: function(c) {
		var r = c.charAt(0) + c.charAt(1);
		var g = c.charAt(2) + c.charAt(3);
		var b = c.charAt(4) + c.charAt(5);
		eval ('r = 0x' + r + ';');
		eval ('g = 0x' + g + ';');
		eval ('b = 0x' + b + ';');
		return {R:r,G:g,B:b}
	},
	RGB_HEX: function(o) {
		return (this.HEX(o.R) + this.HEX(o.G) + this.HEX(o.B));
	},
	HSV_RGB: function(o) {
    	var R, G, A, B, C, S=o.S/100, V=o.V/100, H=o.H/360;

    	if (S > 0) {
    		if (H >= 1) H=0;
        	H=6*H; F=H-Math.floor(H);
        	A=Math.round(255*V*(1-S));
        	B=Math.round(255*V*(1-(S*F)));
        	C=Math.round(255*V*(1-(S*(1-F))));
        	V=Math.round(255*V);
        	switch(Math.floor(H)) {
	            case 0: R=V; G=C; B=A; break;
	            case 1: R=B; G=V; B=A; break;
	            case 2: R=A; G=V; B=C; break;
	            case 3: R=A; G=B; B=V; break;
	            case 4: R=C; G=A; B=V; break;
	            case 5: R=V; G=A; B=B; break;
        	}
        	return ({'R':R?R:0, 'G':G?G:0, 'B':B?B:0, 'A':1});
    	} else
    		return({'R':(V=Math.round(V*255)), 'G':V, 'B':V, 'A':1});
	},
	RGB_HSV: function(o) {
		var r = o.R / 255, g = o.G / 255, b = o.B / 255; // Scale to unity.
		var HSV = {};

		var minVal = Math.min(r, g, b);
		var maxVal = Math.max(r, g, b);
		var delta = maxVal - minVal;
		HSV.V = maxVal;

		if (delta == 0) {
			HSV.H = 0;
			HSV.S = 0;
		} else {
			HSV.S = delta / maxVal;
			var del_R = (((maxVal - r) / 6) + (delta / 2)) / delta;
			var del_G = (((maxVal - g) / 6) + (delta / 2)) / delta;
			var del_B = (((maxVal - b) / 6) + (delta / 2)) / delta;

			if (r == maxVal) {HSV.H = del_B - del_G;}
			else if (g == maxVal) {HSV.H = (1 / 3) + del_R - del_B;}
			else if (b == maxVal) {HSV.H = (2 / 3) + del_G - del_R;}

			if (HSV.H < 0) {HSV.H += 1;}
			if (HSV.H > 1) {HSV.H -= 1;}
		}
		HSV.H *= 360;
		HSV.S *= 100;
		HSV.V *= 100;
		return HSV;
	},
	HSV_HEX: function(o) {
		return (this.RGB_HEX (this.HSV_RGB (o)));
	},
	HEX_HSV: function (c) {
		return (this.RGB_HSV(this.HEX_RGB(c)));
	},
	HEX: function(o) {
		o = Math.round(Math.min(Math.max(0,o),255));
    	return ("0123456789ABCDEF".charAt((o-o%16)/16)+"0123456789ABCDEF".charAt(o%16));
	},
	invert: function (o) { // that's by the book
		var no = o;
		no.H += 180;
		if (no.H > 360) no.H -= 360;
		no.V = 100 - no.V;
		//no.S = 100 - no.S;
		return no;
	},
	gsInvert: function (o) { // that's concidering grayscales like #7F7F7F
		var no = o;
		no.H += 180;
		if (no.H > 360) no.H -= 360;
		if (no.S > 13) { // treshold is 13% - those seems pretty grayish
			no.V = 100 - no.V;
		} else { // grayscale color
			no.V = (no.V < 50?100:0);
		}
		return no;
	},
	invertHEX: function (hexVal) {
		var hsvVal = this.HEX_HSV(hexVal);
		var res = TColorPicker.Color.invert(hsvVal);
		return this.HSV_HEX(res);
	},
	// same like above but gray scale colors are inverted to white/black
	gsInvertHEX: function (hexVal) {
		var hsvVal = this.HEX_HSV(hexVal);
		var res = TColorPicker.Color.gsInvert(hsvVal);
		return this.HSV_HEX(res);
	},
	buildHArray: function () {
		if (this.hArray) return;
		this.hArray = [];
		for (var i = 165; i >=0; i--)
			this.hArray[165-i] = this.HSV_HEX({H:Math.round((360/165)*i), S:100, V:100});
	},
	normalize: function(a,b,c) {
		return (Math.min (a, Math.max (0, Math.ceil ((parseInt (c) / b) * a))));
	}
}
