-----------------------------------------------------------------------------
-- Provides some utility functions for use by versium and its clients.
--
-- (c) 2007, 2008  Yuri Takhteyev (yuri@freewisdom.org)
-- (c) 2007 Hisham Muhammad (quick_LCS())
-- License: MIT/X, see http://sputnik.freewisdom.org/en/License
-----------------------------------------------------------------------------

module(..., package.seeall)
local errors = require"versium.errors"

-----------------------------------------------------------------------------
-- Escapes a node id to make it safe for use as a file name.
-- 
-- @param id             a node id.
-- @return               an escaped node id.
-----------------------------------------------------------------------------
function fs_escape_id(id)
   assert(id and id:len() > 0)
   return fs_escape_id_but_keep_slash(id):gsub("/", "%%2F")
end

-----------------------------------------------------------------------------
-- Escapes a node id to make it safe for use as a file name, but keeps
-- forward slash in it.
-- 
-- @param id             a node id.
-- @return               an escaped node id.
-----------------------------------------------------------------------------
function fs_escape_id_but_keep_slash(id, options)
   return id:gsub("%%", "%%25"):gsub(":", "%%3A")
end

-----------------------------------------------------------------------------
-- Un-escapes a node id. (See escape_id().)
-- 
-- @param id             an escaped node id.
-- @return               the original node id.
-----------------------------------------------------------------------------
function fs_unescape_id(id)
   assert(id and id:len() > 0)
   return id:gsub("%%2F", "/"):gsub("%%3A", ":"):gsub("%%25", "%%")
end

-----------------------------------------------------------------------------
-- Writes data to a file (to be replaced with atomic write).
-- 
-- @param path           the file path.
-- @param data           data to be written to the file.
-- @return               nothing
-----------------------------------------------------------------------------
function write_file(path, data, node)
   assert(path)
   assert(data)
   local f, err = io.open(path, "w")
   assert(f, errors.could_not_save(node, err))
   f:write(data)
   f:close()
end

-----------------------------------------------------------------------------
-- An auxiliary function for reading the content of a file.  (Throws an a 
-- Versium-specific error if something goes wrong.)
-- 
-- @param path           the file path.
-- @return               the data read from the file as a string.
-----------------------------------------------------------------------------
function read_file(path, node)
   assert(path)
   local f, err = io.open(path)
   assert(f, errors.could_not_read(node, err))
   local data = f:read("*all")
   f:close()
   return data
end

-----------------------------------------------------------------------------
-- Reads data from a file without complaining if the file doesn't exist.
-- 
-- @param path           the file path.
-- @return               the data read from the file or just "" if the file 
--                       doesn't exist.
-----------------------------------------------------------------------------
function read_file_if_exists(path)
   assert(path)
   local status, f = pcall(io.open, path)
   if status and f then
      local data = f:read("*all")
      f:close()
      return data
   else
      return ""
   end
end

-----------------------------------------------------------------------------
-- Converts a versium time stamp into the requested format.  Users some code
-- from http://lua-users.org/wiki/TimeZone)
--
-- @param timestamp      Versium timestamp (string) 
-- @param format         Lua time format (string) 
-- @param tzoffset       time zone offset as "+hh:mm" or "-hh:mm"
--                        (ISO 8601) or "local" [string, optional, defaults 
--                        to "local"]
-- @param tzname         name/description of the time zone [string, optional,
--                        defaults to tzoffset, valid XHTML is ok]
-- @return               formatted time (string)
-----------------------------------------------------------------------------

function format_time(timestamp, format, tzoffset, tzname)
   if tzoffset == "local" then  -- calculate local time zone (for the server)
      local now = os.time()
      local timezone = os.difftime(now, os.time(os.date("!*t", now)))
      local h, m = math.modf(timezone / 3600)
      tzoffset = string.format("%+.4d", 100 * h + 60 * m)
   end
   tzoffset = tzoffset or "GMT"
   format = format:gsub("%%z", tzname or tzoffset)
   if tzoffset == "GMT" then 
      tzoffset = "+0000"
   end
   tzoffset = tzoffset:gsub(":", "")

   local sign = 1
   if tzoffset:sub(1,1) == "-" then
      sign = -1
      tzoffset = tzoffset:sub(2)
   elseif tzoffset:sub(1,1) == "+" then
      tzoffset = tzoffset:sub(2)       
   end
   tzoffset = sign * (tonumber(tzoffset:sub(1,2))*60 + tonumber(tzoffset:sub(3,4)))*60
   return os.date(format, os.time{ year=timestamp:sub(1,4), month=timestamp:sub(6,7),
                                   day=timestamp:sub(9,10), hour=timestamp:sub(12,13),
                                   min=timestamp:sub(15,16), sec=timestamp:sub(18)} + tzoffset)
end
