-- Light Unix I/O for Lua
-- Copyright 2016 Daniel Silverstone <dsilvers+luxio@digital-scurf.org>
--
-- Distributed under the same terms as Lua itself (MIT).
--
-- Wrappers which make certain functions easier to use.

--- High-level wrappers for certain POSIX functionality.
--- @module luxio.wrapper

local luxio = require "luxio"

local l_opendir = luxio.opendir
local l_readdir = luxio.readdir
local l_closedir = luxio.closedir
local l_lstat = luxio.lstat

local S_ISFIFO = luxio.S_ISFIFO
local S_ISCHR = luxio.S_ISCHR
local S_ISDIR = luxio.S_ISDIR
local S_ISBLK = luxio.S_ISBLK
local S_ISREG = luxio.S_ISREG
local S_ISLNK = luxio.S_ISLNK or function () return false end
local S_ISSOCK = luxio.S_ISSOCK or function () return false end

local DT_UNKNOWN = luxio.DT_UNKNOWN

local dir_to_dirp = setmetatable({}, {__mode="k"})

local function opendir(dirp)
   local dir, errno = l_opendir(dirp)
   if not dir then
      return dir, errno
   end
   dir_to_dirp[dir] = dirp
   if luxio.DT_UNKNOWN == nil then
	luxio.DT_UNKNOWN = 0
	luxio.DT_FIFO = 1
	luxio.DT_CHR = 2
	luxio.DT_DIR = 3
	luxio.DT_BLK = 4
	luxio.DT_REG = 5
	luxio.DT_LNK = 6
	luxio.DT_SOCK = 7
   end
   return dir, errno
end

local function readdir(dir)
   local v, result = l_readdir(dir)
   if type(result) == "table" then
      -- if d_type available, DT_UNKNOWN is defined, otherwise it's nil.
      if result.d_type == DT_UNKNOWN and dir_to_dirp[dir] then
	 result.d_type = luxio.DT_UNKNOWN
	 local path = dir_to_dirp[dir] .. "/" .. result.d_name
	 local r, stab = l_lstat(path)
	 if r == 0 then
	    -- Stat was successful, try and calculate proper d_type
	    local mode = stab.mode
	    if S_ISREG(mode) then
	       result.d_type = luxio.DT_REG
	    elseif S_ISDIR(mode) then
	       result.d_type = luxio.DT_DIR
	    elseif S_ISLNK(mode) then
	       result.d_type = luxio.DT_LNK
	    elseif S_ISCHR(mode) then
	       result.d_type = luxio.DT_CHR
	    elseif S_ISBLK(mode) then
	       result.d_type = luxio.DT_BLK
	    elseif S_ISFIFO(mode) then
	       result.d_type = luxio.DT_FIFO
	    elseif S_ISSOCK(mode) then
	       result.d_type = luxio.DT_SOCK
	    end
	 end
      end
   end
   return v, result
end

local function closedir(dir)
   dir_to_dirp[dir] = nil
   l_closedir(dir)
end

local function install_dir_wrapper()
   luxio.opendir = opendir
   luxio.readdir = readdir
   luxio.closedir = closedir
end

return {
   opendir = opendir,
   readdir = readdir,
   closedir = closedir,
   install_dir_wrapper = install_dir_wrapper,
}
