--interface:
--  output.short_status
--  output.descriptive_status
--  output.currently_executing

local ansicolors = require "ansicolors"
local s = require 'say'

local output = function()
  local pending_description = function(status, options)
    return "\n\n"..ansicolors("%{yellow}"..s('output.pending')).." → "..
    ansicolors("%{cyan}"..status.info.short_src).." @ "..
    ansicolors("%{cyan}"..status.info.linedefined)..
    "\n"..ansicolors("%{bright}"..status.description)
  end

  local error_description = function(status, options)
    return "\n\n"..ansicolors("%{red}"..s('output.failure')).." → "..
    ansicolors("%{cyan}"..status.info.short_src).." @ "..
    ansicolors("%{cyan}"..status.info.linedefined)..
    "\n"..ansicolors("%{bright}"..status.description)..
    "\n"..status.err
  end

  local success_string = function()
    return ansicolors('%{green}●')
  end

  local failure_string = function()
    return ansicolors('%{red}●')
  end

  local pending_string = function()
    return ansicolors('%{yellow}●')
  end

  local running_string = function()
    return ansicolors('%{blue}○')
  end

  local status_string = function(short_status, descriptive_status, successes, failures, pendings, ms, options)
    local success_str = s('output.success_plural')
    local failure_str = s('output.failure_plural')
    local pending_str = s('output.pending_plural')

    if successes == 0 then
      success_str = s('output.success_zero')
    elseif successes == 1 then
      success_str = s('output.success_single')
    end

    if failures == 0 then
      failure_str = s('output.failure_zero')
    elseif failures == 1 then
      failure_str = s('output.failure_single')
    end

    if pendings == 0 then
      pending_str = s('output.pending_zero')
    elseif pendings == 1 then
      pending_str = s('output.pending_single')
    end

    if not options.defer_print then
      io.write("\08 ")
      short_status = ""
    end

    local formatted_time = ("%.6f"):format(ms):gsub("([0-9])0+$", "%1")

    return short_status.."\n"..
    ansicolors('%{green}'..successes).." "..success_str.." / "..
    ansicolors('%{red}'..failures).." "..failure_str.." / "..
    ansicolors('%{yellow}'..pendings).." "..pending_str.." : "..
    ansicolors('%{bright}'..formatted_time).." "..s('output.seconds').."."..descriptive_status
  end

  local format_statuses = function (statuses, options)
    local short_status = ""
    local descriptive_status = ""
    local successes = 0
    local failures = 0
    local pendings = 0

    for i,status in ipairs(statuses) do
      if status.type == "description" then
        local inner_short_status, inner_descriptive_status, inner_successes, inner_failures, inner_pendings = format_statuses(status, options)
        short_status = short_status..inner_short_status
        descriptive_status = descriptive_status..inner_descriptive_status
        successes = inner_successes + successes
        failures = inner_failures + failures
        pendings = inner_pendings + pendings
      elseif status.type == "success" then
        short_status = short_status..success_string(options)
        successes = successes + 1
      elseif status.type == "failure" then
        short_status = short_status..failure_string(options)
        descriptive_status = descriptive_status..error_description(status, options)

        if options.verbose then
          descriptive_status = descriptive_status.."\n"..status.trace
        end

        failures = failures + 1
      elseif status.type == "pending" then
        short_status = short_status..pending_string(options)
        pendings = pendings + 1

        if not options.suppress_pending then
          descriptive_status = descriptive_status..pending_description(status, options)
        end
      end
    end

    return short_status, descriptive_status, successes, failures, pendings
  end

  local strings = {
    failure = failure_string,
    success = success_string,
    pending = pending_string,
  }

  local on_first

  return {
    options = {},
    name = "utf_whatever",

    header = function(desc, test_count)
      on_first = true
    end,


    formatted_status = function(statuses, options, ms)
      local short_status, descriptive_status, successes, failures, pendings = format_statuses(statuses, options)
      return status_string(short_status, descriptive_status, successes, failures, pendings, ms, options)
    end,

    currently_executing = function(test_status, options)
      if on_first then
        on_first = false
      else
        io.write("\08")
      end

      io.write(strings[test_status.type](options)..running_string(options))
      io.flush()
    end
  }
end

return output
