#!/opt/lnxall_app/bin/lua

-- Created by jiaqiang.ye@lnxall.com
-- LNXALL Version List Generator
-- 2025/09/01

local cjson      = require "cjson"
local invoker    = require "invoker"
local gfmt       = string.format

local g_VERDIR   = "/app/upstart/verlist"
local g_VERLOCK  = "/tmp/.lock-lnxall-version"
local g_VEROUT   = "/tmp/.lnxall-version-list.json"

local g_syslist  = {
	[1] = "sysmonitor",
	[2] = "deb4gnet",
	[3] = "ucinet",
}

local g_verlist  = {}

local function bad_str(s)
	if type(s) ~= "string" then return true end
	if string.len(s) == 0 then return true end
	return false
end

local function good_str(s)
	if type(s) == "string" and #s > 0 then
		return true
	end
	return false
end

local function verlist_add(pkgname, pkgver, pkgdesc)
	if bad_str(pkgname) or bad_str(pkgver) then
		return false
	end

	local idx, found = 0, nil
	while true do
		idx = idx + 1
		local verinfo = g_verlist[idx]
		if verinfo == nil then break end
		if verinfo.name == pkgname then
			found = idx
			break
		end
	end

	if bad_str(pkgdesc) then pkgdesc = nil end
	if found then
		local vi = g_verlist[found]
		vi.version = pkgver
		vi.description = pkgdesc
	else
		g_verlist[#g_verlist + 1] = {
			["name"]           = pkgname,
			["version"]        = pkgver,
			["description"]    = pkgdesc,
		}
	end

	io.stdout:write(gfmt("Package version found, name: %s, version: %s, description: %s\n",
		pkgname, pkgver, pkgdesc or ""))
	io.stdout:flush()
	return found or #g_verlist
end

local function glob_verlist()
	local vlist = invoker.glob(gfmt("%s/*.json", g_VERDIR))
	if type(vlist) ~= "table" then
		io.stderr:write(gfmt("No JSON file found in '%s'\n", g_VERDIR))
		io.stderr:flush()
		return false
	end

	for _, vj in ipairs(vlist) do
		local okay, jver = false, nil
		local vstr = invoker.readfile(vj, 0x40000)
		if not bad_str(vstr) then
			okay, jver = pcall(cjson.decode, vstr)
		end

		if okay and type(jver) == "table" then
			verlist_add(jver.name, jver.version, jver.description)
		else
			io.stderr:write(gfmt("Error, failed to decode as JSON: %s\n", vj))
			io.stderr:flush()
		end
	end
end

local function opkg_version()
	for _, pkg in ipairs(g_syslist) do
		local okay, output = invoker.invoke(invoker.OUTPUT, "opkg", "status", pkg)
		if okay and type(output) == "string" and #output > 0 then
			local verstr = nil
			for line in string.gmatch(output, "[^\r\n]+") do
				verstr = string.match(line, "^Version:%s+([^%s]+)%s*$")
				if good_str(verstr) then break end
			end
			if good_str(verstr) then verlist_add(pkg, verstr, nil) end
		else
			io.stderr:write(gfmt("Warning, package seems to be uninstalled: %s\n", pkg))
			io.stderr:flush()
		end
	end
end

local function lnxver_main(arg1)
	if good_str(arg1) then g_VEROUT = arg1 end
	local lockfd = invoker.waitlock(g_VERLOCK)
	if not lockfd then
		io.stderr:write(gfmt("Error, failed to lock file: %s\n", g_VERLOCK))
		io.stderr:flush()
	end

	glob_verlist()
	opkg_version()
	local vnum = #g_verlist
	if vnum == 0 then
		io.stderr:write("Error, no package version found.\n")
		io.stderr:flush()
		return false
	end

	local vhdl = io.open(g_VEROUT, "wb")
	if vhdl then
		local vbuf = cjson.encode(g_verlist)
		if vbuf:sub(1, 1) ~= "{" then vbuf = gfmt('{"verlist":%s}', vbuf) end
		vhdl:write(vbuf)
		vhdl:close(); vhdl = nil
		io.stdout:write(gfmt("Version file written to: %s\n", g_VEROUT))
		io.stdout:flush()
		return true
	end
	io.stderr:write(gfmt("Error, failed to open file for writing: %s\n", g_VEROUT))
	io.stderr:flush()
	return false
end

os.exit(lnxver_main(arg[1]) and 0 or 1)
