#!/usr/bin/env lua

function show_help()
print(string.format([[!! THIS PROGRAM HAS BEEN DEPRECATED !!
Consider rolling your own update checks that aren't as hacky.

This program will help you keep your satellites fresh and up to date.
Usage: %s <sat|dir> <check> <save>

sat = Check only one satellite
dir = Check a dir full of satellites
check = What file (sat) or directory (dir) to check
save = What file (sat) or directory (dir) to save known versions into]], arg[0]))
end

-- Shamelessly stolen from http://stackoverflow.com/questions/15706270/sort-a-table-in-lua
function spairs(t)
    -- collect the keys
    local keys = {}
    for k in pairs(t) do keys[#keys+1] = k end

    table.sort(keys)

    -- return the iterator function
    local i = 0
    return function()
        i = i + 1
        if keys[i] then
            return keys[i], t[keys[i]]
        end
    end
end

function call(command)
    --[[--
    --  Runs a command, returns all output except trailing newline on success.
    --]]--

    local pipe = io.popen(command)
    if pipe then
        local output = pipe:read("*all")
        if pipe:close() then
            if #output > 0 then
                return output:match("(.-)%s*$")
            end
        end
    end
end

function astrohelp(satellite, func)
    -- satellite_dir is an example of me being lazy and wanting to keep output clean in other functions.
    if satellite_dir then
        return call("astrohelp '" .. satellite_dir .. "/" .. satellite .. "' " .. func)
    else
        return call("astrohelp '" .. satellite .. "' " .. func)
    end
end

function download(url)
    return call("curl -Ls '" .. url .. "'")
end

function escape_regex(string)
    -- http://www.lua.org/manual/5.3/manual.html#6.4.1
    local magic = "^$().[]*+-?"
    for i = 1, #magic do
        local char = magic:sub(i, i)
        string = string:gsub("%" .. char, "%%" .. char)
    end

    return string
end

function make_regex(filename, version)
    --[[--
    --  Converts a string into regex, by escaping all special (magic) characters.
    --  Returns two regexes, meant to be used in a loop. (See code using this function to know what I mean)
    --]]--

    local temp_filename = filename:gsub(escape_regex(version), "{{version}}")
    if temp_filename == filename then
        -- If we can't find version in filename, return.
        return nil, nil
    end

    local regex = escape_regex(temp_filename:gsub("%%", "%%%%"))
    return regex:gsub("{{version}}", "(.-") .. ")", regex:gsub(".*{{version}}", "(.-)")
end

function get_available_versions(satellite)
    --[[--
    --  Returns a set table containing all available versions on update_url
    --]]--

    local version = astrohelp(satellite, "variable version")
    local update_url = astrohelp(satellite, "variable update_url")

    if not version or not update_url then
        return
    end

    io.stderr:write("\x1B[1K\r")
    io.stderr:write(satellite .. ": Downloading update file...")
    local update_file = download(update_url)
    if not update_file then
        io.stderr:write("\x1B[1K\r")
        print("WARNING: " .. satellite .. ": Failed to download " .. update_url)
        return
    end

    io.stderr:write("\x1B[1K\r")
    io.stderr:write(satellite .. ": Getting available versions...")
    -- Try getting variable update_names, get downloads otherwise.
    local update_names = astrohelp(satellite, "variable update_names")
    if not update_names then
        local tmp_update_names = astrohelp(satellite, "downloads")
        assert(tmp_update_names, "Could not get update names for " ..  satellite)
        update_names = ""
        -- Take the basename of every download url
        for name in tmp_update_names:gmatch("[^\n]+") do
            update_names = update_names .. name:match("[^/]+$") .. ","
        end
    end

    local versions = {}
    local count = 0
    for update_name in update_names:gmatch("[^,]+") do
        local loop_regex, end_regex = make_regex(update_name, version)

        if loop_regex and end_regex then
            for x in update_file:gmatch(loop_regex) do
                -- Sometimes, the match starts way too soon, and we get a very long "version" number.
                -- Here we make sure we really get the shortest possible match.
                local version
                repeat
                    version = x
                    x = x:match(loop_regex)
                until not x
                version = version:match(end_regex)

                versions[version] = true
                count = count + 1
            end
        end
    end

    io.stderr:write("\x1B[1K\r")

    if count < 1 then
        print("WARNING: " .. satellite .. ": Couldn't retrieve any versions")
        return
    elseif not versions[version] then
        print("WARNING: " .. satellite .. ": Current version isn't available")
    end

    return versions, update_url
end

function check_new_versions(satellite, version_file_path)
    --[[--
    --  Check if new versions are available for a satellite.
    --  Logs new versions to log_file, which should be a file descriptor.
    --  Uses version_file_path to know where to store all known versions.
    --]]--

    local versions, update_url = get_available_versions(satellite)
    if not versions then
        return
    end

    local version_file = io.open(version_file_path, "r+")

    local old_versions = nil
    if version_file then
        old_versions = {}
        for version in version_file:lines() do
            old_versions[version] = true
        end
    else
        print(satellite .. ": First check")
        version_file = io.open(version_file_path, "w")
        assert(version_file, "Failed to create " .. version_file_path)
    end

    local new_versions = {}
    if old_versions then
        for version, _ in spairs(versions) do
            if not old_versions[version] then
                table.insert(new_versions, version)
            end
        end
    else
        for version, _ in spairs(versions) do
            table.insert(new_versions, version)
        end
    end

    if #new_versions > 0 then
        print(satellite .. ": From '" .. update_url .. "'")

        for _, version in ipairs(new_versions) do
            version_file:write(version .. "\n")
            print(satellite .. ": " .. version)
        end
    end

    version_file:close()
end

if #arg < 3 then
    show_help()
    os.exit(1)
end

if arg[1] == "sat" then
    check_new_versions(arg[2], arg[3])
elseif arg[1] == "dir" then
    satellite_dir = arg[2]
    local version_dir = arg[3]
    assert(os.execute("mkdir -p " .. version_dir), "Failed to create " .. version_dir)

    local find = call("find " .. satellite_dir .. " -type f -name '*.sat' -printf '%P\n'")
    assert(find, "Failed to run find")

    local total = select(2, find:gsub("\n", "\n"))
    local cols = call("tput cols")
    local count = 1

    for satellite in find:gmatch("(.-)\n") do
        if cols then
            cols = tonumber(cols)
            local countstr = tostring(count) .. "/" .. tostring(total)
            io.stderr:write("\r")
            for i = 1, cols - countstr:len() - 1 do
                io.stderr:write(" ")
            end
            io.stderr:write(countstr .. "\r")
        end

        local version_file = version_dir .. "/" .. satellite
        assert(os.execute("mkdir -p " .. version_file:match("^.+/")), "Failed to create dir for " .. satellite)
        check_new_versions(satellite, version_file)

        count = count + 1
    end
else
    show_help()
    os.exit(1)
end