/*
	name   : colkit.js
	date   : 16-Mar-00
	author : A. Gunther
	www    : http://home.earthlink.net/~redbird77
	email  : redbird77@earthlink.net


	This file is VERY much still under construction.

	x, y, z   - three seperate values
	(x, y, z) - a three element array
	#rrggbb   - hexadecimal triplet string
	0xrrggbb  - hexadecimal literal
	rrggbb    - hex string (w/o #)

	(h)ue, (l)ightnes, (s)aturation /(h)ue, (s)aturation, (b)rightness

	This .js file contains the following functions
	----------------------------------------------

	rgb_to_hex
		r, g, b or (r, g, b) --> #rrggbb

	rgb_to_hls
		r, g, b --> (h, l, s)

	hls_to_hex
		h, l, s	 or (h, l, s) --> #rrggbb

	hsb_to_hex
		h, s, b	 or (h, s, b) --> #rrggbb

	hex_to_rgb
		rrggbb or 0xrrggbb --> (r, g, b)
	
*/

function dec_to_hex(n)
{
/*
	Purpose:
		To return a 2-digit string containing the hexadecimal representation
		of a number.  Since this function is used in color calculations, any
		number < 0 will be set to 0 and any number > 255 will be set to 255.

	Input  : any number
	Output : hexadecimal representation of n (as a string)
*/
	
	if (n < 0) n = 0
	else if (n > 255) n = 255

	n = Math.floor(n)

	// The toString(16) function returns the hex value of a number, thus
	// '4'.toString(16) returns 4 not '04'.  You need to add the leading
	// zero to any single digit hex number (0 - A).
	return (n < 16 ? '0' : '') + n.toString(16)
}

function dec_to_hex2(n)
{
	
	return n.toString(16)
}

function hex_to_dec(hex)
{
	var typ = typeof hex
	
	if (typ == 'string') hex = parseInt(hex, 16)
	//else if (typ != 'number') alert('bad input')

	if (hex < 0)   return 0
	if (hex > 255) return 255
	
	return hex 
}

function hex_to_rgb(hex)
{
/*
INPUT
	hex literal (0xff6633) or string ('ff6633' - with no '#')

OUTPUT
	3 element array of r, b, g values

CALL
	var c = hex_to_rgb(0xff6633)	// now c[0] = 255, c[1] = 102, c[2] = 51
*/

	var ret
	var typ = typeof hex

	if (typ == 'string') hex = parseInt(hex, 16)
	
	/* 
		more complete:
		
		if (typ == 'string' || hex.constructor == String)
			hex = parseInt(hex, 16)
		else if (typ != 'number')
		{
			alert('Input NaN. Changing hex to 0x000000 (black).')
			hex = 0
		}
	*/

	if (hex > 0xffffff)
	{
		//alert('Input too large. Changing hex to 0xffffff (white).')
		hex = 0xffffff
	}
	else if (hex < 0)
	{
		//alert('Input too small. Changing hex to 0x000000 (black).')
		hex = 0
	}

	ret = new Array(hex >> 16, (hex >> 8) & 0xff, hex & 0xff)

	return ret
}

/*
To create the hls_to_hex and hsb_to_hex functions, I hacked together info from
various sources.  The basic algorithms found in all the sources came from the
book "The Fundamentals of Computer Graphics" by Foley and VanDam.  Many sources
though contradicted others and/or had typos in them.  The list below represents
what I have come to understand about color.  Take it with a grain of salt, and
check out the Color FAQ (http://www.inforamp.net/~poynton/Poynton-colour.html)
to be thoroughly confused :-).

	hls --> (h)ue, (l)ightness, (s)aturation
	-------------------------------------------
	o when l = 0, color is white
	o to get a color like ff0000 use l = .5

	hsb --> (h)ue, (s)aturation, (b)rightness
	-------------------------------------------
	o brightness sometimes referred to as (v)alue
	o to get a color like ff0000 use b = 1
*/

function hls_to_hex()
{
/*
INPUT
	3 values representing the hue, lightness, and saturation of a color OR
	a 3 element array containing those same values

OUTPUT
	the h, l, s values represented as a hexadecimal triplet string

CALL
	alert(hls_to_hex(.5, .5, 1)) OR
	alert(hls_to_hex(hls_vals)) //given that hls_vals is an 3 element array
*/
	
	var m1, m2, r, g, b, h, l, s

	var hls = args_to_array(hls_to_hex.arguments)
	
	h = hls[0]; l = hls[1]; s = hls[2]

	h = h - Math.floor(h)

	// The following alterations of l and s, are my own ways of keeping,
	// the lightness and saturation within the 0 to 1 range.
	//
	// L and s vary from a starting point to 1 and then back to 0 and up to
	// 1 again -  a kind of ladder.  These calcuations elminate the need
	// for a keep track of l and s by incrementing and decrementing.
	l = Math.abs((Math.floor(l) & 1) - l + Math.floor(l))
	s = Math.abs((Math.floor(s) & 1) - s + Math.floor(s))

	if (s == 0) r = g = b = l
	else
	{
		if (l <= 0.5) m2 = l * (1 + s)
		else m2 = l + s - l * s

		m1 = 2 * l - m2

		r = V(m1, m2, h + 1 / 3)
		g = V(m1, m2, h)
		b = V(m1, m2, h - 1 / 3)
	}
	
	return rgb_to_hex(r * 255, g * 255, b * 255)
}

// a helper function to hls_to_rgb
function V(m1, m2, h)
{
	if (h > 1) h -= 1
	if (h < 0) h += 1
	
	if (6 * h < 1) return (m1 + (m2 - m1) * h * 6)
	if (2 * h < 1) return m2
	if (3 * h < 2) return (m1 + (m2 - m1) * ((2 / 3) - h) * 6)

	return m1
}

/////////////////////////////////////////////////////////////////////////////
//
//	hsb_to_hex
//
/////////////////////////////////////////////////////////////////////////////

function hsb_to_hex()
{
/*
	Input  : three values representing the hue, saturation and brightness(value) of a color
	Output : hexadecimal triplet color (i.e. #cc3399)
*/
	var p, q, t, f, c, i, hsb

	var hsb = args_to_array(hsb_to_hex.arguments)

	h = hsb[0]; s = hsb[1]; b = hsb[2]
	
	// The three following calculations make it possible to feed progressively higher values into hsb_to_hex
	// without needing to worry about the values staying in the 0 - 1 range.
	
	// h varies from a starting pt. to a max of 1, then starts over again at 0, a kind of circle. 
	// s and b vary from a starting pt. to a max of 1, then head back toward 0, a kind of ladder.
	
	h = h - Math.floor(h)
	s = Math.abs((Math.floor(s) & 1) - s + Math.floor(s))
	b = 255 * Math.abs((Math.floor(b) & 1) - b + Math.floor(b))

	if (s == 0) c = rgb_to_hex(b, b, b)
	else
	{
		h *= 6
		i = Math.floor(h)
		f = h - i
		h = Math.floor(h)
		p = Math.floor(b * (1 - s))
		q = Math.floor(b * (1 - (s * f)))
		t = Math.floor(b * (1 - (s * (1 - f))))

		if (i == 0)      c = rgb_to_hex(b, t, p)
	 	else if (i == 1) c = rgb_to_hex(q, b, p)	    	
		else if (i == 2) c = rgb_to_hex(p, b, t)
		else if (i == 3) c = rgb_to_hex(p, q, b)
		else if (i == 4) c = rgb_to_hex(t, p, b)
		else			 c = rgb_to_hex(b, p, q)
	}

	return c
}

function get_random_color()
{
	var i, hex

	for (i = 0, hex = ''; i < 3; i++) hex += dec_to_hex(Math.random() * 256)

	return hex
}

function invert(col)
{
	var a

	a = hex_to_rgb(col)

	return rgb_to_hex(~a[0] & 0xff, ~a[1] & 0xff, ~a[2] & 0xff)
}

function isValidColor(col)
{
	// this function could be a central error checking function
	// each color would be passed through 
	// if typeof col is string then
	// parseInt(col, 16)
	// then
	// check if col < 0xffffff and > 0x000000

	//for (var i = 0; i < col.length; i++)
	//
}

function rgb_to_hex()
{
/*
INPUT
	3 values representing the red, green, and blue componets of a color OR
	a 3 element array containing those same values

OUTPUT
	the r, g, b values represented as a hexadecimal triplet color

CALL
	var col = rgb_to_hex(255, 102, 51)	// col now is the string - #ff6633
*/
	var c = args_to_array(rgb_to_hex.arguments)

	var h = (c[0] << 16 | c[1] << 8 | c[2]).toString(16)

	while (h.length < 6) h = '0' + h

	return '#' + h 
}

function hex_literal_to_hex_triplet(hex_literal)
{
	//return rgb_to_hex(hex_to_rgb(hex_literal))

	var h = parseInt(hex_literal).toString(16)

	while (h.length < 6) h = '0' + h

	return '#' + h 
}

function rgb_to_hls(r, g, b)
{
	r /= 255
	g /= 255
	b /= 255

	var min = Math.min(r, Math.min(g, b))
	var max = Math.max(r, Math.max(g, b))

	var ret, h, s, delta = max - min
	
	var l = (max + min) / 2
	
	if (!delta) s = h = 0	// really undefined
	else
	{   
        s = delta / ((l <= 0.5) ? max + min : 2 - max - min)

		if      (r == max) h = (g - b) / delta
        else if (g == max) h = 2 + (b - r) / delta
        else if (b == max) h = 4 + (r - g) / delta

        h /= 6

		if (h < 0) h += 1
	}

	ret = new Array(h, l, s)

	return ret
}

function rgb_to_hls_old(r, g, b)
{
	r /= 255
	g /= 255
	b /= 255

	var min = Math.min(r, Math.min(g, b))
	var max = Math.max(r, Math.max(g, b))

	var ret, h, s
	
	var l = (max + min) / 2
	
	if (max == min)
	{
		s = 0
        h = 0	// really undefined
	}
	else
	{
		if (l <= 0.5) s = (max - min) / (max + min)
        else          s = (max - min) / (2 - max - min)
        
        var delta = max - min

		if (r == max)      h = (g - b) / delta
        else if (g == max) h = 2 + (b - r) / delta
        else if (b == max) h = 4 + (r - g) / delta

        h /= 6

		if (h < 0) h += 1
	}

	ret = new Array(h, l, s)

	return ret
}

function hls_to_hex2()
{
/*
INPUT
	3 values representing the hue, lightness, and saturation of a color OR
	a 3 element array containing those same values

OUTPUT
	the h, l, s values represented as a hexadecimal triplet color

CALL
	alert(hls_to_hex(.5, .5, 1)) OR
	alert(hls_to_hex(hls_vals)) //given that hls_vals is an 3 element array
*/
	
	var m1, m2, r, g, b, h, l, s

	var hls = args_to_array(hls_to_hex2.arguments)
	
	h = hls[0]; l = hls[1]; s = hls[2]

	h = h - Math.floor(h)
	//l = Math.abs((Math.floor(l) & 1) - l + Math.floor(l))
	if (l > 1) l = 1
	//s = Math.abs((Math.floor(s) & 1) - s + Math.floor(s))
	//s = s - Math.floor(s)
	if (s > 1) s = 1

	if (s == 0) r = g = b = l
	else
	{
		if (l <= 0.5) m2 = l * (1 + s)
		else m2 = l + s - l * s

		m1 = 2 * l - m2

		r = V(m1, m2, h + 1 / 3)
		g = V(m1, m2, h)
		b = V(m1, m2, h - 1 / 3)
	}
	
	return rgb_to_hex(r * 255, g * 255, b * 255)
}

// helper function
function args_to_array(argv)
{
	var argc = argv.length
	var ret  = new Array()

	for (var i = 0; i < 3; i++) ret[i] = argc > 1 ? argv[i] : argv[0][i]

	return ret
}