--[[
functions for using HSV colors in StepMania.
Code adapted from http://www.cs.rit.edu/~ncs/color/t_convert.html

changelog:

v 1

= v1.1 =
* Hue(color, newHue) changed to wrap around for invalid values.
* Desaturate(color, percent) renamed to Saturation(color, percent).
______________________________________________________________________________
xxx: support HSL<->RGB (which is different) too?
http://en.wikipedia.org/wiki/HSV_color_space#Conversion_from_RGB_to_HSL_or_HSV

HSL treats absolute brightness of a color at 0.5 in L, whereas
HSV treats absolute brightness of a color at 1.0 in V.

Saturation is different too.
]]

-- HasAlpha(c)
function HasAlpha(c)
	if c[4] then
	 	return c[4]
	else
		return 1
	end
end

-- ColorToHSV(c)
-- Takes in a normal color("") and returns a table with the HSV values.
function ColorToHSV(c)
	local r = c[1]
	local g = c[2]
	local b = c[3]
	-- alpha requires error checking sometimes.
	local a = HasAlpha(c)

	local h = 0
	local s = 0
	local v = 0

	local min = math.min( r, g, b )
	local max = math.max( r, g, b )
	v = max

	local delta = max - min

	-- xxx: how do we deal with complete black?
	if min == 0 and max == 0 then
		-- we have complete darkness; make it cheap.
		return {
			Hue = 0,
			Sat = 0,
			Value = 0,
			Alpha = a
		}
	end

	if max ~= 0 then
		s = delta / max -- rofl deltamax :|
	else
		-- r = g = b = 0; s = 0, v is undefined
		s = 0
		h = -1
		return {
			Hue = h,
			Sat = s,
			Value = v,
			Alpha = 1
		}
	end

	if r == max then
		h = ( g - b ) / delta     -- yellow/magenta
	elseif g == max then
		h = 2 + ( b - r ) / delta -- cyan/yellow
	else
		h = 4 + ( r - g ) / delta -- magenta/cyan
	end

	h = h * 60 -- degrees

	if h < 0 then
		h = h + 360
	end

	return {
		Hue = h,
		Sat = s,
		Value = v,
		Alpha = a
	}
end

-- HSVToColor(hsv)
-- Converts a set of HSV values to a color. hsv is a table.
-- See also: HSV(h, s, v)
function HSVToColor(hsv)
	local i
	local f, q, p, t
	local r, g, b
	local h, s, v

	local a

	s = hsv.Sat
	v = hsv.Value

	if hsv.Alpha then
		a = hsv.Alpha
	else
		a = 0
	end

	if s == 0 then
		return { v, v, v, a }
	end

	h = hsv.Hue / 60

	i = math.floor(h)
	f = h - i
	p = v * (1-s)
	q = v * (1-s*f)
	t = v * (1-s*(1-f))

	if i == 0 then
		return { v, t, p, a }
	elseif i == 1 then
		return { q, v, p, a }
	elseif i == 2 then
		return { p, v, t, a }
	elseif i == 3 then
		return { p, q, v, a }
	elseif i == 4 then
		return { t, p, v, a }
	else
		return { v, p, q, a }
	end
end

-- ColorToHex(c)
-- Takes in a normal color("") and returns the hex representation.
-- Adapted from code in LuaBit (http://luaforge.net/projects/bit/),
-- which is MIT licensed and copyright (C) 2006~2007 hanzhao.
function ColorToHex(c)
	local r = c[1]
	local g = c[2]
	local b = c[3]
	local a = HasAlpha(c)

	local function hex(value)
		value = math.ceil(value)

		local hexVals = { 'A', 'B', 'C', 'D', 'E', 'F' }
		local out = ""
		local last = 0

		while(value ~= 0) do
			last = math.mod(value, 16)
			if(last < 10) then
				out = tostring(last) .. out
			else
				out = hexVals[(last-10)+1] .. out
			end
			value = math.floor(value/16)
		end

		if(out == "") then
			return "00"
		end
		
		return string.format( "%02X", tonumber(out,16) )
	end

	local rX = hex( scale(r, 0, 1, 0, 255) )
	local gX = hex( scale(g, 0, 1, 0, 255) )
	local bX = hex( scale(b, 0, 1, 0, 255) )
	local aX = hex( scale(a, 0, 1, 0, 255) )

	return rX .. gX .. bX .. aX
end

function HSVToHex(hsv)
	return ColorToHex( HSVToColor(hsv) )
end

--[[ you should mainly use these functions                                    ]]

-- this is the lazy one; use this when you mean fullly visible
function HSV(h, s, v)
	local t = {
		Hue = h,
		Sat = s,
		Value = v,
		Alpha = 1
	}
	return HSVToColor(t)
end

-- here's the proper one
function HSVA(h, s, v, a)
	local t = {
		Hue = h,
		Sat = s,
		Value = v,
		Alpha = a
	}
	return HSVToColor(t)
end

function Saturation(color,percent)
	local c = ColorToHSV(color)
	-- error checking
	if percent < 0 then
		percent = 0.0
	elseif percent > 1 then
		percent = 1.0
	end
	c.Sat = percent
	return HSVToColor(c)
end

function Brightness(color,percent)
	local c = ColorToHSV(color)
	-- error checking
	if percent < 0 then
		percent = 0.0
	elseif percent > 1 then
		percent = 1.0
	end
	c.Value = percent
	return HSVToColor(c)
end

function Hue(color,newHue)
	local c = ColorToHSV(color)
	-- handle wrapping
	if newHue < 0 then
		newHue = 360 + newHue
	elseif newHue > 360 then
		--newHue = math.mod(newHue, 360); -- ?? untested
		newHue = newHue - 360
	end
	c.Hue = newHue
	return HSVToColor(c)
end;