#!/bin/lua

local version = "1.6.0"

local server = os.getenv("REPO") or "http://opentty.fun"
local mirror = {
    --["appmenu"] = { remote = "sys/appmenu/main.lua", here = "/bin/init", description = "Application Menu" },
    ["armitage"] = { remote = "net/armitage", here = "/bin/armitage", depends = { "ifconfig" }, description = "OpenTTY Network Tools" },
    ["audio"] = { remote = "file/audio.lua", here = "/bin/audio", description = "Audio Codec Manager" },
    ["autogc"] = { remote = "sys/autogc", here = "/bin/autogc", description = "Auto Clean Memory" },
    ["chroot"] = { remote = "sys/chroot.lua", here = "/bin/chroot", description = "Change to a real File System" },
    ["cmatrix"] = { remote = "games/cmatrix", here = "/bin/cmatrix", description = "The Matrix Effect" },
    ["curl"] = { remote = "net/curl.lua", here = "/bin/curl", description = "Connect with a Server" },
    ["debug"] = { remote = "sys/debug", here = "/bin/debug", description = "Toggle debug mode" },
    ["dns"] = { remote = "net/dns/main.lua", here = "/bin/dns", description = "DNS Server for OpenTTY" },
    ["docker"] = { remote = "sys/docker/main.lua", here = "/bin/docker", description = "Conteiners on OpenTTY" },
    ["du"] = { remote = "file/du.lua", here = "/bin/du", description = "View files lenght" },
    ["expr"] = { remote = "file/expr.lua", here = "/bin/expr", description = "Evaluate expressions" },
    ["explorer"] = { remote = "file/explorer.lua", here = "/bin/explorer", description = "File Explorer" },
    ["event"] = { remote = "sys/smile/event.lua", here = "/bin/event", description = "Event handler" },
    ["exec"] = { remote = "sys/execute/exec.lua", here = "/bin/exec", description = "Legacy exec: run commands chained with &" },
    ["execute"] = { remote = "sys/execute/main.lua", here = "/bin/execute", description = "Legacy execute: run commands chained with ;" },
    ["free"] = { remote = "sys/free.lua", here = "/bin/free", description = "Memory Informations" },
    ["forge"] = { remote = "dev/forge/main.lua", here = "/lib/forge", description = "Additional API for OpenTTY" },
    ["find"] = { remote = "file/find.lua", here = "/bin/find", description = "Search a pattern value in a file" },
    ["grep"] = { remote = "file/grep.lua", here = "/bin/grep", description = "Check if a file contains a pattern" },
    ["hash"] = { remote = "file/hash.lua", here = "/bin/hash", description = "Prints file hash" },
    ["head"] = { remote = "file/head.lua", here = "/bin/head", description = "Prints first lines of a file" },
    ["tail"] = { remote = "file/tail.lua", here = "/bin/tail", description = "Prints last lines of a file" },
    ["hostname"] = { remote = "net/hostname.lua", here = "/bin/hostname", description = "Manage Host Name" },
    ["htop"] = { remote = "sys/htop", here = "/bin/htop", description = "System Monitor" },
    ["ifconfig"] = { remote = "net/ifconfig.lua", here = "/bin/ifconfig", description = "Network Informations" },
    ["irc"] = { remote = "net/irc.lua", here = "/bin/irc", description = "IRC Client for OpenTTY" },
    ["java"] = { remote = "dev/java/main.lua", here = "/bin/java", description = "Java ME" },
    ["jdb"] = { remote = "sys/benchmark/main.lua", here = "/bin/jdb", depends = { "forge", "log" }, description = "Debugging API" },
    ["launch"] = { remote = "sys/launch.lua", here = "/bin/launch", description = "Launch Legacy Application" },
    ["log"] = { remote = "sys/smile/logs.lua", here = "/bin/log", description = "MIDlet Logs" },
    ["nano"] = { remote = "file/nano.lua", here = "/bin/nano", description = "Text Editor for OpenTTY" },
    ["nginx"] = { remote = "net/nginx/main.lua", here = "/bin/nginx", description = "HTTP Server (Nginx-style)" },
    ["nc"] = { remote = "net/netcat.lua", here = "/bin/nc", description = "Connect with Remote Interfaces" },
    ["netstat"] = { remote = "net/netstat.lua", here = "/bin/netstat", description = "Network connection test" },
    ["nice"] = { remote = "sys/nice.lua", here = "/bin/nice", description = "Chance process priority" },
    ["open"] = { remote = "sys/open.lua", here = "/bin/open", description = "Open a file with the default application" },
    ["passwd"] = { remote = "sys/passwd.lua", here = "/bin/passwd", description = "Change password" },
    ["pastebin"] = { remote = "net/pastebin", here = "/bin/pastebin", description = "PasteBin Client for OpenTTY" },
    ["ping"] = { remote = "net/ping.lua", here = "/bin/ping", description = "Test connection delay" },
    ["play"] = { remote = "file/play.lua", here = "/bin/play", description = "Audio Player (play command)" },
    ["prg"] = { remote = "sys/push.lua", here = "/bin/prg", description = "PushRegister Manager" },
    ["sed"] = { remote = "file/sed.lua", here = "/bin/sed", description = "String Editor" },
    ["sdk"] = { remote = "dev/sdkme", here = "/bin/sdk", depends = { "forge" }, description = "OpenTTY Application SDK" },
    ["stop"] = { remote = "sys/stop.lua", here = "/bin/stop", description = "Kill application by name" },
    ["sync"] = { remote = "sys/sync", here = "/bin/sync", description = "OpenTTY Updater Checker" },
    ["svchost"] = { remote = "sys/svchost.lua", here = "/bin/svchost", description = "Quick Launch Services" },
    ["sudo"] = { remote = "sys/sudo.lua", here = "/bin/sudo", description = "" },
    ["smile"] = { remote = "sys/smile/main.lua", here = "/bin/smile", depends = { "event", "logs" }, description = "Service Manager" },
    ["uname"] = { remote = "sys/uname", here = "/bin/uname", description = "System Informations" },
    ["tree"] = { remote = "file/tree.lua", here = "/bin/tree", description = "Directory Tree Viewer" },
    ["useradd"] = { remote = "sys/users/useradd.lua", here = "/bin/useradd", description = "Add users" },
    ["userdel"] = { remote = "sys/users/userdel.lua", here = "/bin/userdel", description = "Remove users" },
    ["viewer"] = { remote = "file/img.lua", here = "/bin/imgview", description = "View Images" },
    ["webproxy"] = { remote = "net/proxy.lua", here = "/bin/shprxy", description = "Access OpenTTY on WebProxy" },
    ["wget"] = { remote = "net/wget", here = "/bin/wget", description = "Download files from Network" },
    ["xterm"] = { remote = "sys/x11/xterm.lua", here = "/bin/xterm", description = "MIDlet Terminal" },
    ["x11"] = { remote = "sys/x11/main.lua", here = "/bin/x11", description = "X Server Client" },
    ["watch"] = { remote = "sys/watch.lua", here = "", description = "Watch a program" },

    --[""] = { remote = "", here = "", depends = {}, description = "" }
}

local function fetch(remote)
    local ok, content, status = pcall(socket.http.get, server .. "/apps/" .. remote)

    if ok and status == 200 then
        return content
    else
        return nil
    end
end

local function fetch_file(remote, here)
    local ok, input, status = pcall(socket.http.rget, server .. "/apps/" .. remote)

    if ok and status == 200 then
        return io.copy(input, here)
    else
        print(ok, input, status)
        return nil
    end
end

local function install(pkg, verbose)
    if verbose then
        print(":: Installing " .. pkg .. "...")
    end
    
    local info = mirror[pkg]

    if info.depends then
        print(":: Building dependencies")
        for _, dep in ipairs(info.depends) do
            install(dep, verbose)
        end
    end

    local status = fetch_file(info.remote, info.here)

    if status ~= nil then
        if verbose then
            if status == 0 then
                print(":: Installed " .. info.here)
            else
                print(":: Installation error for " .. pkg)
            end
        end

        return status == 0
    else
        if verbose then
            print(":: Connection error")
        end

        return nil
    end
end
local function download(pkg, output)
    local info = mirror[pkg]

    if info then
        local status = fetch_file(info.remote, os.join(output))
        if status == 0 then
            print(":: Downloaded " .. info.remote .. " to " .. output)
        else
            print("download failed")
        end
    else
        print("pkg: download: " .. pkg .. ": not found")
    end
end
local function remove(pkg, verbose)
    if verbose then
        print(":: Removing " .. pkg .. "...")
    end

    local info = mirror[pkg]

    local status = os.remove(info.here)

    if verbose then
        if status == 0 then
            print(":: Removed " .. info.here)
        elseif status == 127 then
            print(":: Package not installed")
        else
            print(":: Removing error for " .. pkg)
        end
    end

    return status == 0
end


os.setproc("name", "pkg")
if arg[1] == nil or arg[1] == "help" then
    print("Yang Package Manager v" .. version)
    print("Usage: pkg <command> [package]")
    print("Commands:")
    print("  install <package>  - Install a package")
    print("  download <package> - Download a package")
    print("  remove <package>   - Remove a package")
    print("  update             - Check for updates")
    print("  list               - List available packages")
    print("  info <package>     - Show package information")
    print("  help               - Show this help")
elseif arg[1] == "install" then
    if os.getuid() > 0 then print("Permission denied!") os.exit(13) end

    if arg[2] then
        for i = 2, #arg do
            local pkg = arg[i]

            if mirror[pkg] then
                if mirror[pkg].remote then
                    install(pkg, true)
                else
                    print("pkg: install: " .. pkg .. ": invalid package (not remote source)")
                    os.exit(1)
                end
            elseif pkg == "*" then
                print(":: Installing " .. (#mirror - 4) .. " packages...")
                for pkg2,_ in pairs(mirror) do
                    if mirror[pkg2].remote then
                       install(pkg2, true)
                    end
                end
                return
            else
                print("pkg: install: " .. pkg .. ": not found")
                os.exit(127)
            end
        end
    else
        print("pkg: usage: pkg remove [package]")
    end
elseif arg[1] == "download" then
    if arg[2] and arg[3] then
        download(arg[2], arg[3])
    else
        print("pkg: usage: pkg download [pkg] [file]")
    end
elseif arg[1] == "run" then
    if arg[2] then
        local file = io.read(os.join(arg[2]))
        if file ~= "" then
            local pkgs = string.split(file, "\n")
            print(":: Installing " .. #pkgs)
            for _,pkg in pairs(pkgs) do
                install(pkg, true)
            end
        else
            print("pkg: nothing to run")
            os.exit(1)
        end
    else
        print("pkg: usage: pkg run [requirements.txt]")
    end
elseif arg[1] == "remove" then
    if os.getuid() > 0 then print("Permission denied!") os.exit(13) end

    if arg[2] then
        for i = 2, #arg do
            local pkg = arg[i]

            if mirror[pkg] then
                if mirror[pkg].packages then
                    print(":: Removing collection: " .. pkg)
                    print("")

                    local remove_count = 0

                    for _, query in ipairs(mirror[pkg].packages) do
                        if remove(query, false) then
                            remove_count = remove_count + 1
                        end
                    end

                    print(":: Removed " .. remove_count .. " packages from collection '" .. pkg .. "'")
                elseif mirror[pkg].remote then
                    remove(pkg, true)
                else
                    print("pkg: remove: " .. pkg .. ": invalid package")
                    os.exit(2)
                end
            else
                print("pkg: remove: " .. pkg .. ": not found")
                os.exit(127)
            end
        end
    else
        print("pkg: usage: pkg remove [package]")
    end
elseif arg[1] == "update" then
    local response = fetch("res/latest")
    if response then
        print(":: OpenTTY latest version: " .. response)
    else
        print(":: Connection error")
        os.exit(101)
    end
elseif arg[1] == "list" then
    print("Available packages:")
    for name, info in pairs(mirror) do
        if not info.packages then
            print("- " .. name .. ": " .. info.description)
        end
    end
elseif arg[1] == "info" then
    if arg[2] then
        local pkg = arg[2]

        if mirror[pkg] then
            local info = mirror[pkg]
            if info.packages then
            else
                print("Package: " .. pkg)
                print("Description: " .. info.description)
                print("Path: " .. info.here)
                
                if info.depends then
                    print("Dependencies: " .. table.concat(info.depends, ", "))
                end
            end
        else
            print(":: " .. pkg .. " not found")
            os.exit(127)
        end
    else
        print("pkg: usage: pkg info [package]")
    end
else
    print("pkg: " .. arg[1] .. ": not found")
    os.exit(127)
end
