--[[
functions for using HSV colors in StepMania.

Code for dealing with HSV values was adapted from
http://www.cs.rit.edu/~ncs/color/t_convert.html
______________________________________________________________________________
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.
--]]

--[[ HSV functions ]]
-- 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;
	if c[4] then a = c[4];
	else a = 1;
	end;

	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 = a
		};
	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;
	elseif h > 360 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;
	if c[4] then a = c[4];
	else a = 1;
	end;

	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;

--[[ ]]
-- 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;

--[[ color modification functions ]]
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;

-- useful for glows
function Alpha(color,percent)
	local c = ColorToHSV(color);
	-- error checking
	if percent < 0 then
		percent = 0.0;
	elseif percent > 1 then
		percent = 1.0;
	end;
	c.Alpha = percent;
	return HSVToColor(c);
end;