local stdnse = require "stdnse"
local os = require "os"
local math = require "math"
local string = require "string"
_ENV = stdnse.module("datetime", stdnse.seeall)
local difftime = os.difftime
local time = os.time
local date = os.date
local floor = math.floor
local fmod = math.fmod
local format = string.format
local match = string.match
function record_skew(host, timestamp, received)
  local skew_tab = host.registry.datetime_skew
  skew_tab = skew_tab or {}
  local srtt = host.times and host.times.srtt or 0
  local adjusted = difftime(floor(timestamp), floor(received)) - srtt / 2.0
  skew_tab[#skew_tab + 1] = adjusted
  stdnse.debug2("record_skew: %s", adjusted)
  host.registry.datetime_skew = skew_tab
end
local function utc_offset(t)
  local localtime = date("*t", t)
  local gmtime = date("!*t", t)
  return difftime(time(localtime), time(gmtime))
end
function date_to_timestamp(date, offset)
  offset = offset or 0
  return time(date) + utc_offset(time(date)) - offset
end
local function format_tz(offset)
  local sign, hh, mm
  if not offset then
    return ""
  end
  if offset < 0 then
    sign = "-"
    offset = -offset
  else
    sign = "+"
  end
  offset = floor(offset / 60)
  hh = floor(offset / 60)
  mm = floor(fmod(offset, 60))
  return format("%s%02d:%02d", sign, hh, mm)
end
function format_timestamp(t, offset)
  if type(t) == "table" then
    return format(
      "%d-%02d-%02dT%02d:%02d:%02d",
      t.year, t.month, t.day, t.hour, t.min, t.sec
      )
  else
    local tz_string = format_tz(offset)
    offset = offset or 0
    local status, result = pcall(date, "!%Y-%m-%dT%H:%M:%S", floor(t + offset))
    if not status then
      local tmp = floor(t + offset)
      if tmp > 0xffffffff then
        local seconds_in_year = 31556926
        local extra_years = (tmp  - 0xffffffff) // seconds_in_year + 1
        tmp = tmp - extra_years * seconds_in_year
        status, result = pcall(date, "!*t", tmp)
        if status then
          result = format("%d-%02d-%02d", result.year + extra_years, result.month, result.day)
        end
      end
    end
    if not status then
      return ("Invalid timestamp: %s"):format(t)
    end
    return result .. tz_string
  end
end
function format_time(interval, unit)
  local sign = ""
  if interval < 0 then
    sign = "-"
    interval = math.abs(interval)
  end
  unit = unit or 1
  local precision = floor(math.log(unit, 10))
  local sec = (interval % (60 * unit)) / unit
  interval = interval // (60 * unit)
  local min = interval % 60
  interval = interval // 60
  local hr = interval % 24
  interval = interval // 24
  local s = format("%.0fd%02.0fh%02.0fm%02.".. precision .."fs",
    interval, hr, min, sec)
  return sign .. (match(s, "([1-9].*)") or format("%0.".. precision .."fs", 0))
end
function format_difftime(t2, t1)
  local d, s, sign, yeardiff
  d = difftime(time(t2), time(t1))
  if d > 0 then
    sign = "+"
  elseif d < 0 then
    sign = "-"
    t2, t1 = t1, t2
    d = -d
  else
    sign = ""
  end
  if t2.year > t1.year then
    local tmpyear = t1.year
    t1.year = t2.year
    d = difftime(time(t2), time(t1))
    if d < 0 then
      t1.year = t2.year - 1
      d = difftime(time(t2), time(t1))
    end
    yeardiff = t1.year - tmpyear
    t1.year = tmpyear
  else
    yeardiff = 0
  end
  local s = format_time(d)
  if yeardiff == 0 then return sign .. s end
  s = format("%dy", yeardiff) .. s
  return sign .. s
end
return _ENV
