--- Geyser color functions.
-- @author guy
-- @module Geyser.Color

Geyser.Color = {}

--- Converts color to 3 hex values as a string, no alpha, css style
-- @return The color formatted as a hex string, as accepted by html/css
function Geyser.Color.hex (r, g, b)
  return string.format("#%02x%02x%02x", Geyser.Color.parse(r, g, b))
end

--- Converts color to 4 hex values as a string, with alpha, css style
-- @return The color formatted as a hex string, as accepted by html/css
function Geyser.Color.hexa (r, g, b, a)
  return string.format("#%02x%02x%02x%02x", Geyser.Color.parse(r, g, b, a))
end

--- Converts color to 3 hex values as a string, no alpha, hecho style
-- @return The color formatted as a hex string, as accepted by hecho
function Geyser.Color.hhex (r, g, b)
  return string.format("|c%02x%02x%02x", Geyser.Color.parse(r, g, b))
end

--- Converts color to 4 hex values as a string, with alpha, hecho style
-- @return The color formatted as a hex string, as accepted by hecho
function Geyser.Color.hhexa (r, g, b, a)
  return string.format("|c%02x%02x%02x%02x", Geyser.Color.parse(r, g, b, a))
end

--- Converts color to 3 decimal values as a string, no alpha, decho style
-- @return The color formatted as a decho() style string
function Geyser.Color.hdec (r, g, b)
  return string.format("<%d,%d,%d>", Geyser.Color.parse(r, g, b))
end

--- Converts color to 4 decimal values as a string, with alpha, decho style
-- @return The color formatted as a decho() style string
function Geyser.Color.hdeca (r, g, b, a)
  return string.format("<%d,%d,%d,%d>", Geyser.Color.parse(r, g, b, a))
end

--- Returns 4 color components from (nearly any) acceptable format.  Colors can be
-- specified in two ways.  First: as a single word in english ("purple") or
-- hex ("#AA00FF", "|cAA00FF", or "0xAA00FF") or decimal ("<190,0,255>"). If
-- the hex or decimal representations contain a fourth element then alpha is
-- set too - otherwise alpha can't be set this way.  Second: by passing in
-- distinct components as unsigned integers (e.g. 23 or 0xA7).  When using the
-- second way, at least three values must be passed.  If only three are
-- passed, then alpha is 255.  Third: by passing in a table that has explicit
-- values for some, all or none of the keys r,g,b, and a.
-- @param red Either a valid string representation or the red component.
-- @param green The green component.
-- @param blue The blue component.
-- @param alpha The alpha component.
function Geyser.Color.parse(red, green, blue, alpha)
  local r, g, b, a = 0, 0, 0, 255

  -- have to have something to set, else can't do anything!
  if not red then
    return nil, "No color supplied"
  end

  -- function to return next number
  local next_num = nil
  local base = 10
  -- assigns all the colors, used after we figure out how the color is
  -- represented as a string
  local assign_colors = function()
    r = tonumber(next_num(), base)
    g = tonumber(next_num(), base)
    b = tonumber(next_num(), base)
    local has_a = next_num()
    if has_a then
      a = tonumber(has_a, base)
    end
  end

  -- Check if we were passed a string or table that needs to be parsed, i.e.,
  -- there is only a valid red value, and other params are nil.
  if not green or not blue then
    if type(red) == "table" then
      -- Here just copy over the appropriate values with sensible defaults
      r = red.r or 127
      g = red.g or 127
      b = red.b or 127
      a = red.a or 255
      return r, g, b, a
    elseif type(red) == "string" then
      -- first case is a hex string, where first char is '#'
      if string.find(red, "^#") then
        local pure_hex = string.sub(red, 2) -- strip format char
        next_num = string.gmatch(pure_hex, "%w%w")
        base = 16

        -- second case is a hex string, where first chars are '|c' or '0x'
      elseif string.find(red, "^[|0][cx]") then
        local pure_hex = string.sub(red, 3) -- strip format chars
        next_num = string.gmatch(pure_hex, "%w%w")
        base = 16

        -- third case is a decimal string, of the format "<dd,dd,dd>"
      elseif string.find(red, "^<") then
        next_num = string.gmatch(red, "%d+")

        -- fourth case is a named string
      elseif Geyser.Color.find_color_name(red) then
        local col = Geyser.Color.find_color_name(red)
        local i = 0
        local n = #color_table[col]
        next_num = function()
          -- create a simple iterator
          i = i + 1
          if i <= n then
            return color_table[col][i]
          else return nil
          end
        end

      else
        -- finally, no matches, do nothing
        return
      end
    end

  else
    -- Otherwise we weren't passed a complete string, but instead discrete
    -- components as either decimal or hex
    -- Yes, this is a little silly to do this way, but it fits with the
    -- rest of the parsing going on...
    local i = 0
    next_num = function()
      i = i + 1
      if i == 1 then
        return red
      elseif i == 2 then
        return green
      elseif i == 3 then
        return blue
      elseif i == 4 then
        return alpha
      else return nil
      end
    end
  end
  assign_colors()
  return r, g, b, a
end

--- Searches for a close match to 'color' in the color_table
--  Returns the color found, or false if it can't find one
-- @param color the color name you're trying to find
function Geyser.Color.find_color_name(color)
  if type(color) ~= "string" then
    return false
  end
  color = color:lower()
  color = color:gsub("_", "")
  for color_name,_ in pairs(color_table) do
    if color_name:lower() == color then
      return color_name
    end
  end
  return false
end

--- Applies colors to a window drawing from defaults and overridden values.
-- @param cons The window to apply colors to
function Geyser.Color.applyColors(cons)
  cons:setFgColor(cons.fgColor)
  cons:setBgColor(cons.bgColor)
  cons:setColor(cons.color)
end
