$(".colour_selector").ready(function() {
	$(".colour_selector").each(function(i) {
		//controls
		var colour_chooser = $(this).find(".colour_chooser");
		var brightness_changer = $(this).find(".brightness_changer");
		var colour_preview = $(this).find(".colour_preview");
		var hue_image = colour_chooser.find(".hue_image");
		var colour_cursor = colour_chooser.find(".colour_cursor");
		var brightness_cursor = brightness_changer.find(".brightness_cursor");
		
		var span_red_value = $(this).find(".red_value");
		var span_green_value = $(this).find(".green_value");
		var span_blue_value = $(this).find(".blue_value");
		var hue_value = $(this).find(".hue_value");
		var saturation_value = $(this).find(".saturation_value");
		var lightness_value = $(this).find(".lightness_value");
		
		//variables
		var lightness = 1.0, hue = 0.0, saturation = 1.0;
		var red=256, green=256, blue=256;
		var mouseDown = false;
		
		//when ready
		updateColourDisplay();
		
		//methods
		brightness_changer.select(function() { $(this).blur(); });
		brightness_changer.bind("mousedown",function(event) { mouseDown=true; changeBrightness(event, $(this)); });
		brightness_changer.bind("mousemove",function(event) { if (mouseDown) changeBrightness(event, $(this)); });
		brightness_changer.bind("mouseup",function() { mouseDown=false; });

		function changeBrightness(mouseEvent, obj) {
			yPos = (mouseEvent.pageY-obj.offset().top);
			if (yPos<0) yPos=0;
			if (yPos>obj.height()) yPos = obj.height();
			
			brightness_cursor.css("top", yPos);
			
			lightness = 1.0 - (yPos/(obj.height()));
			
			setColourFromHSV();
			updateColourDisplay();
		}

		colour_chooser.bind("mousedown" ,function(event) { mouseDown=true; changeColour(event, $(this)); });
		colour_chooser.bind("mousemove" ,function(event) { if (mouseDown) changeColour(event, $(this)); });
		colour_chooser.bind("mouseup" ,function(event) { mouseDown=false; });

		function changeColour(mouseEvent, obj) {
			xPos = (mouseEvent.pageX-obj.offset().left);
			yPos = (mouseEvent.pageY-obj.offset().top);
			
			if (xPos<0) xPos=0;
			if (xPos>obj.height()) xPos = obj.Width();
			
			if (yPos<0) yPos=0;
			if (yPos>obj.height()) yPos = obj.height();
			
			colour_cursor.css("top", yPos);
			colour_cursor.css("left", xPos);

			hue = xPos/obj.width();
			saturation = 1.0 - (yPos/obj.height());
			
			var newColour = hslToRGB(hue, saturation, 0.5);
			brightness_changer.css("background-color","rgb(" + newColour[0] + "," + newColour[1] + "," + newColour[2] + ")");
			
			setColourFromHSV();
			updateColourDisplay();
		}
		
		$(".common_colours li").bind("click", function(event) {
			colour_preview.css("background-color", $(this).css("background-color"));
			return false;
		});

		function setColourFromHSV() {
			var newColour = hslToRGB(hue, saturation, lightness);
			
			red = newColour[0];
			green = newColour[1];
			blue = newColour[2];
		}

		function updateColourDisplay() {
			colour_preview.css("background-color", "rgb(" + red + "," + green + "," + blue + ")");
			
			span_red_value.text(red);
			span_green_value.text(green);
			span_blue_value.text(blue);
			
			hue_value.text(Math.floor(hue*100) + "%");
			saturation_value.text(Math.floor(saturation*100) + "%");
			lightness_value.text(Math.floor(lightness*100) + "%");
		}
	});
});

/**
* Maths used from Wikipedia
*/
function hslToRGB(hue, saturation, lightness) {
	var q = 0.0, p=0.0;
	var newColour = new Array(3);
	
	if (lightness<0.5) {
		q = lightness * (1+saturation);
	} else {
		q = lightness + saturation - (lightness*saturation);
	}
	
	p = 2*lightness - q;
	
	newColour[0] = Math.floor(hslColourToRGBColour(hue+0.33,p,q)*256); //red
	newColour[1] = Math.floor(hslColourToRGBColour(hue,p,q)*256); //green
	newColour[2] = Math.floor(hslColourToRGBColour(hue-0.33,p,q)*256); //blue
	
	return newColour;
}

function hslColourToRGBColour(hslColour, p, q) {
	if (hslColour<0.0) hslColour+= 1.0;
	if (hslColour>1.0) hslColour-= 1.0;
	
	if (hslColour<0.167) {
		return p+((q-p) * 6 * hslColour);
	} else if (hslColour<0.5) {
		return q;
	} else if (hslColour<0.67) {
		return p+((q-p)*6*(0.67-hslColour));
	} else {
		return p;
	}
}