local p = {}

function rgb2hsv(r, g, b)

   local max = math.max(r, g, b)
   local min = math.min(r, g, b)
   local c = max - min
   
   -- calculate hue
   local h
   if c == 0 then
       h = 0
   elseif max == r then
       h = 60 * (0 + ((g - b)/c))
   elseif max == g then
       h = 60 * (2 + ((g - b)/c))
   elseif max == b then
       h = 60 * (4 + ((g - b)/c))
   end
   if h < 0 then h = h + 360 end
   
   -- calculate value
   local v = max
   
   -- calculate luminance
   -- local l = (max + min) / 2
   
   -- calculate saturation
   local s
   if c == 0 then
       s = 0
   else
       s = c/v
   end
   
   return h, s, v

end

function hsv2rgb(h, s, v)

   local hi = math.floor(h / 60)
   local f = ((h / 60) - hi)
   
   local p = v * (1 - s)
   local q = v * (1 - s * f)
   local t = v * (1 - s * (1 - f))
   
   if hi == 1 then
       return q, v, p
   elseif hi == 2 then
       return p, v, t
   elseif hi == 3 then
       return p, q, v
   elseif hi == 4 then
       return t, p, v
   elseif hi == 5 then
       return v, p, q
   else
       return v, t, p
   end

end

--[[ toHSV

Description: Converts a RGB color to a HSV color.

Usage: {{#invoke:HSV|toHSV|R|G|B}}

Output: hue [0°..360°], saturation [0..1], value [0..1] ]] function p.toHSV(frame)

   return rgb2hsv(frame.args[1] / 255, frame.args[2] / 255, frame.args[3] / 255)

end

--[[ toHSV

Description: Returns the hue of a RGB color.

Usage: {{#invoke:HSV|hue|R|G|B}}

Output: hue [0°..360°] ]] function p.hue(frame)

   local h, s, v = rgb2hsv(frame.args[1] / 255, frame.args[2] / 255, frame.args[3] / 255)
   return h

end

--[[ toHSV

Description: Returns the saturation of a RGB color.

Usage: {{#invoke:HSV|saturation|R|G|B}}

Output: saturation [0..1] ]] function p.saturation(frame)

   local h, s, v = rgb2hsv(frame.args[1] / 255, frame.args[2] / 255, frame.args[3] / 255)
   return s

end

--[[ toHSV

Description: Returns the saturation of a RGB color.

Usage: {{#invoke:HSV|saturation|R|G|B}}

Output: saturation [0..1] ]] function p.saturate(frame)

   local h, s, v = rgb2hsv(frame.args[1] / 255, frame.args[2] / 255, frame.args[3] / 255)
   return s

end

--[[ toHSV

Description: Returns the hue of a RGB color.

Usage: {{#invoke:HSV|value|R|G|B}}

Output: value [0..1] ]] function p.value(frame)

   local h, s, v = rgb2hsv(frame.args[1] / 255, frame.args[2] / 255, frame.args[3] / 255)
   return v

end

--[[ toHSV

Description: Saturates a RGB color by a given factor.

Usage: {{#invoke:HSV|saturate|R|G|B|factor}}

Output: saturated RGB ]] function p.saturate(frame)

   -- convert to HSV
   local h, s, v = rgb2hsv(frame.args[1] / 255, frame.args[2] / 255, frame.args[3] / 255)
   -- saturate
   s = s * frame.args[4]
   if s < 0 then
       s = 0
   elseif s > 1 then
       s = 1
   end
   -- convert back to RGB
   local r, g, b = hsv2rgb(h, s, v)
   return math.floor(r * 255 + 0.5), math.floor(g * 255 + 0.5), math.floor(b * 255 + 0.5)

end

--[[ toHSV

Description: Converts a HSV color to a RGB color.

Usage: {{#invoke:HSV|toRGB|H|S|V}} with H in [0°..360°], S in [0..1] and V in [0..1]

Output: R, G, B ]] function p.toRGB(frame)

   local r, g, b = hsv2rgb(frame.args[1], frame.args[2], frame.args[3])
   -- transform from real [0..1] to integer [0..255]
   return math.floor(r * 255 + 0.5), math.floor(g * 255 + 0.5), math.floor(b * 255 + 0.5)

end

return p