--:Minify:--
local cloptions = {
    a    = false,
    h    = false,
    l    = false,
    help = false,
}
local inpArgs = { ... }
local args    = {}
local name    = syscall.getTask(syscall.getpid()).name

for _, v in pairs(inpArgs) do
    if v:sub(1, 2) == "--" then
        local opt = v:sub(3)
        if cloptions[opt] == nil then
            print(name .. ": unrecognized option '" .. v .. "'.")
            print("try '" .. name .. " --help' for more information.")
            return
        end
        cloptions[opt] = true
    elseif v:sub(1, 1) == "-" then
        for i = 2, #v do
            local opt = v:sub(i, i)
            if cloptions[opt] == nil then
                print(name .. ": invalid option '-" .. opt .. "'.")
                print("try '" .. name .. " --help' for more information.")
                return
            end
            cloptions[opt] = true
        end
    else
        table.insert(args, v)
    end
end

if cloptions.help then
    print("Usage: " .. name .. " [OPTION]... [DIR]")
    print("List all entries in the specified DIRectory, or cwd if not specified.")
    print("")
    print("Options:")
    print("  -a          do not ignore entries starting with .")
    print("  -h          with -l, print sizes in human readable format")
    print("  -l          use a long listing format")
    print("  --help      display this help and exit")
    return
end

local fs  = require("fs")
local dir = args[1] or ""
if dir:sub(1, 1) ~= "/" then
    dir = syscall.getcwd() .. "/" .. dir
end
if dir:sub(-1) ~= "/" then dir = dir .. "/" end

if not fs.isDir(dir) then
    print(name .. ": cannot access '" .. (args[1] or dir) .. "': no such directory")
    return
end

local function permStr(perms, etype)
    local function b(n) return math.floor(perms / (2^n)) % 2 == 1 end
    local t
    if etype == 0x01 then t = "l"
    elseif etype == nil then t = "-"
    else t = "-" end

    local ur = b(5) and "r" or "-"
    local uw = b(4) and "w" or "-"
    local ux = b(9) and (b(6) and "s" or "x") or (b(6) and "S" or "-")
    local gr = b(3) and "r" or "-"
    local gw = b(2) and "w" or "-"
    local gx = b(8) and "x" or "-"
    local wr = b(1) and "r" or "-"
    local ww = b(0) and "w" or "-"
    local wx = b(7) and "x" or "-"

    return t .. ur .. uw .. ux .. gr .. gw .. gx .. wr .. ww .. wx
end

local sizePrefixes = { "K", "M", "G", "T" }
local function humanSize(size)
    local scale = 0
    while size >= 1024 and scale < #sizePrefixes do
        size  = size / 1024
        scale = scale + 1
    end
    if scale == 0 then return tostring(size).."B" end
    if size < 10 then
        return string.format("%.1f%s", size, sizePrefixes[scale])
    end
    return math.floor(size) .. sizePrefixes[scale]
end

local screenSizeStr = syscall.devctl(1, "size")
local sizeX = tonumber(screenSizeStr:match("^(%d+)")) or 80

local list = fs.list(dir)
if not cloptions.a then
    for i = #list, 1, -1 do
        if list[i]:sub(1, 1) == "." then table.remove(list, i) end
    end
end
table.sort(list)

if #list == 0 then return end

if cloptions.l then
    for _, v in ipairs(list) do
        local fullPath = dir .. v
        local stat = syscall.lstat and syscall.lstat(fullPath) or syscall.stat(fullPath)
        local isDir  = fs.isDir(fullPath)
        local isSym  = stat and stat.etype == 0x01
        local isSock = stat and stat.etype == 0x02

        local typeChar
        if isSym then typeChar = "l"
        elseif isDir then typeChar = "d"
        elseif isSock then typeChar = "s"
        else              typeChar = "-" end

        local pstr
        if stat and stat.perms then
            pstr = permStr(stat.perms, stat.etype)
        else
            pstr = typeChar .. "---------"
        end

        local size = (stat and stat.size) or 0
        local sizeStr = cloptions.h and humanSize(size) or tostring(size)

        local mtime = (stat and stat.modified) and math.floor(stat.modified / 1000) or 0

        local owner = (stat and tostring(stat.owner)) or "0"
        local group = (stat and tostring(stat.group)) or "0"

        printInline(pstr .. " " .. owner .. " " .. group .. " ")
        printInline(string.format("%6s", sizeStr) .. " ")
        printInline(tostring(mtime) .. " ")

        if isSym then
            syscall.devctl(1, "sfgc", 0x00FFFF)
            printInline(v)
            syscall.devctl(1, "sfgc", 0xFFFFFF)
            local ok, target = pcall(syscall.readlink, fullPath)
            if ok then
                printInline(" -> ")
                local targetExists = pcall(syscall.stat, fullPath)
                syscall.devctl(1, "sfgc", targetExists and 0x00FFFF or 0xFF0000)
                printInline(target)
                syscall.devctl(1, "sfgc", 0xFFFFFF)
            end
        elseif isDir then
            syscall.devctl(1, "sfgc", 0x6D00FF)
            printInline(v)
            syscall.devctl(1, "sfgc", 0xFFFFFF)
        elseif isSock then
            syscall.devctl(1, "sfgc", 0xFF00FF)
            printInline(v)
            syscall.devctl(1, "sfgc", 0xFFFFFF)
        else
            local isExec = stat and stat.perms and (math.floor(stat.perms / (2^9)) % 2 == 1)
            syscall.devctl(1, "sfgc", isExec and 0x00FF00 or 0xFFFFFF)
            printInline(v)
            syscall.devctl(1, "sfgc", 0xFFFFFF)
        end
        print("")
    end
    return
end

local colWidth = 0
for _, v in ipairs(list) do
    if #v + 2 > colWidth then colWidth = #v + 2 end
end
local numCols = math.max(1, math.floor(sizeX / colWidth))

for i, v in ipairs(list) do
    local fullPath = dir .. v
    local isDir    = fs.isDir(fullPath)
    local stat     = syscall.lstat and syscall.lstat(fullPath) or syscall.stat(fullPath)
    local isSym    = stat and stat.etype == 0x01

    if isSym then
        syscall.devctl(1, "sfgc", 0x00FFFF)
    elseif isDir then
        syscall.devctl(1, "sfgc", 0x6D00FF)
    elseif isSock then
        syscall.devctl(1, "sfgc", 0xFF00FF)
    else
        local isExec = stat and stat.perms and (math.floor(stat.perms / (2^9)) % 2 == 1)
        syscall.devctl(1, "sfgc", isExec and 0x00FF00 or 0xFFFFFF)
    end

    printInline(v)
    syscall.devctl(1, "sfgc", 0xFFFFFF)
    printInline((" "):rep(colWidth - #v))

    if i % numCols == 0 then print("") end
end

if #list % numCols ~= 0 then print("") end
