--[[

Copyright 2012 connectFree k.k. and the lev authors. All Rights Reserved.
Copyright 2012 The Luvit Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS-IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

--]]

local table = require('table')

local utils = {}

local colors = {
  black   = "0;30",
  red     = "0;31",
  green   = "0;32",
  yellow  = "0;33",
  blue    = "0;34",
  magenta = "0;35",
  cyan    = "0;36",
  white   = "0;37",
  B        = "1;",
  Bblack   = "1;30",
  Bred     = "1;31",
  Bgreen   = "1;32",
  Byellow  = "1;33",
  Bblue    = "1;34",
  Bmagenta = "1;35",
  Bcyan    = "1;36",
  Bwhite   = "1;37"
}

if utils.useColors == nil then
  utils.useColors = true
end

function color(color_name)
  return utils.useColors and ("\27[" .. (colors[color_name] or "0") .. "m") or ""
end

function colorize(color_name, string, reset_name)
  return color(color_name) .. tostring(string) .. color(reset_name)
end

function utils.dump(o, depth)
  local t = type(o)
  if t == 'string' then
    return colorize("Bgreen", '"', "green") 
      .. o:gsub("\\", colorize("Bgreen", "\\\\", "green"))
      :gsub("%z", colorize("Bgreen", "\\0", "green"))
      :gsub("\n", colorize("Bgreen", "\\n", "green"))
      :gsub("\r", colorize("Bgreen", "\\r", "green"))
      :gsub("\t", colorize("Bgreen", "\\t", "green")) 
      .. colorize("Bgreen", '"')
  end
  if t == 'nil' then
    return colorize("Bblack", "nil")
  end
  if t == 'boolean' then
    return colorize("yellow", tostring(o))
  end
  if t == 'number' then
    return colorize("blue", tostring(o))
  end
  if t == 'userdata' then
    return colorize("magenta", tostring(o))
  end
  if t == 'thread' then
    return colorize("Bred", tostring(o))
  end
  if t == 'function' then
    return colorize("cyan", tostring(o))
  end
  if t == 'cdata' then
    return colorize("Bmagenta", tostring(o))
  end
  if t == 'table' then
    if type(depth) == 'nil' then
      depth = 0
    end
    if depth > 1 then
      return colorize("yellow", tostring(o))
    end
    local indent = ("  "):rep(depth)

    -- Check to see if this is an array
    local is_array = true
    local i = 1
    for k,v in pairs(o) do
      if not (k == i) then
        is_array = false
      end
      i = i + 1
    end

    local first = true
    local lines = {}
    i = 1
    local estimated = 0
    for k,v in (is_array and ipairs or pairs)(o) do
      local s
      if is_array then
        s = ""
      else
        if type(k) == "string" and k:find("^[%a_][%a%d_]*$") then
          s = k .. ' = '
        else
          s = '[' .. utils.dump(k, 100) .. '] = '
        end
      end
      s = s .. utils.dump(v, depth + 1)
      lines[i] = s
      estimated = estimated + #s
      i = i + 1
    end
    if estimated > 200 then
      return "{\n  " .. indent .. table.concat(lines, ",\n  " .. indent) .. "\n" .. indent .. "}"
    else
      return "{ " .. table.concat(lines, ", ") .. " }"
    end
  end
  -- This doesn't happen right?
  return tostring(o)
end

-- Replace print
function utils.print(...)
  local n = select('#', ...)
  local arguments = { ... }

  for i = 1, n do
    arguments[i] = tostring(arguments[i])
  end

  process.stdout:write(table.concat(arguments, "\t") .. "\n")
end

-- A nice global data dumper
function utils.prettyPrint(...)
  local n = select('#', ...)
  local arguments = { ... }

  for i = 1, n do
    arguments[i] = utils.dump(arguments[i])
  end

  process.stdout:write(table.concat(arguments, "\t") .. "\n")
end

-- Like p, but prints to stderr using blocking I/O for better debugging
function utils.debug(...)
  local n = select('#', ...)
  local arguments = { ... }

  for i = 1, n do
    arguments[i] = utils.dump(arguments[i])
  end

  process.stderr:write(table.concat(arguments, "\t") .. "\n")
end

function utils.bind(fn, self, ...)
  local bindArgsLength = select("#", ...)

  -- Simple binding, just inserts self (or one arg or any kind)
  if bindArgsLength == 0 then
    return function (...)
      return fn(self, ...)
    end
  end

  -- More complex binding inserts arbitrary number of args into call.
  local bindArgs = {...}
  return function (...)
    local argsLength = select("#", ...)
    local args = {...}
    local arguments = {}
    for i = 1, bindArgsLength do
      arguments[i] = bindArgs[i]
    end
    for i = 1, argsLength do
      arguments[i + bindArgsLength] = args[i]
    end
    return fn(self, unpack(arguments, 1, bindArgsLength + argsLength))
  end
end

return utils

