Newer
Older
Kristify / src / init.lua
@Erb3 Erb3 on 14 Nov 2022 10 KB Get ready to develop backend
-- make a copy of package.path
local sSrc = fs.getDir(shell.getRunningProgram())
local sRoot = fs.combine(sSrc, "..")
local sData = fs.combine(sRoot, "data")
local sFont = fs.combine(sData, "fonts")
local sImg = fs.combine(sData, "images")
local sPage = fs.combine(sData, "pages")

local basalt = require("libs/basalt")
local bdf = require("libs/basalt/libraries/miniBDF")
local ctx

local function purchase(self, event, button, x, y)
    if event == "mouse_click" and button == 1 then
        local metadata = self:getMetadata()
        if metadata.id then
            -- TODO Open popup that tells user where to send krist to get item
            basalt.debug("Item: " .. ctx.products[metadata.id].displayName)
        end
    end
end

local function init(...)
    -- [Context]
    ctx = { gui = {}, image = {}, products = {}, theme = {}, config = {}, pages = {}, current = 1, scroll = 0,
        redraw = true }

    -- [Fonts]
    fontBDF = {}
    if fs.exists(sFont) and fs.isDir(sFont) then
        local fonts = fs.list(sFont)
        for i = 1, #fonts do
            local tmp = bdf.loadBDF(fs.combine(sFont, fonts[i]))
            local count, msg = bdf.checkFont(tmp)
            if count > 0 then
                printError("Could not load font \'" .. fonts[i] .. "\'!")
                for j = 1, #msg do
                    printError(" * " .. msg[j])
                end
                sleep(1)
            else
                fontBDF[tmp.name] = tmp
            end
        end
    end

    -- [Pages]
    local pages = fs.list(sPage)
    for i = 1, #pages do
        local f = fs.open(fs.combine(sPage, pages[i]), 'r')
        ctx.pages[pages[i]] = f.readAll()
        f.close()
    end

    -- [Data] (config,theme,products, etc.)
    local function loadStr(str, begin, env)
        if not begin then begin = "" end
        local success, result = pcall(loadstring(begin .. str, "t", env))
        if success and type(result) == "table" then
            return result
        end
        return false
    end

    local function loadLuaFile(path, env)
        if fs.exists(path) and not fs.isDir(path) then
            local f = fs.open(path, 'r')
            local c = f.readAll()
            f.close()
            local result = loadStr(c, "", env)
            if not result then
                printError("Could not load \'" .. path .. "\' properly!\n" .. tostring(result))
                sleep(1)
            end
            return result
        else
            printError("Could not load \'" .. path .. "\'!")
            sleep(0.5)
        end
        return false
    end

    -- theme
    local col = { white = 0x1, orange = 0x2, magenta = 0x4, lightBlue = 0x8, yellow = 0x10, lime = 0x20, pink = 0x40,
        grey = 0x80, lightGrey = 0x100, cyan = 0x200, purple = 0x400, blue = 0x800, brown = 0x1000, green = 0x2000,
        red = 0x4000, black = 0x8000 }
    local inferiorcol = col;
    inferiorcol.gray = col.grey;
    inferiorcol.lightGray = col.lightGrey
    local result = loadLuaFile(fs.combine(sPage, "theme.lua"), { colours = col, colors = inferiorcol })
    if result then
        ctx.theme = result
    end
    -- bimg
    local files = fs.list(sImg)
    for i = 1, #files do
        if files[i]:sub(-4) == "bimg" then
            local f = fs.open(fs.combine(sImg, files[i]), 'r')
            local c = f.readAll()
            f.close()
            local result = loadStr(c, "return ", { colours = col, colors = inferiorcol })
            if result then
                ctx.image[files[i] or (#ctx.image + 1)] = result
            end
        end
    end
    -- config
    result = loadLuaFile(fs.combine(sData, "config.lua"))
    if result then
        ctx.config = result
    end
    -- products
    result = loadLuaFile(fs.combine(sData, "products.lua"))
    if result then
        ctx.products = result
    end

    -- [Purchase]
    basalt.setVariable("purchase", purchase)

    return ctx
end

---Saves the current proucts states into the file
---and loads them into the interface.
local function updateProducts()
end

---Checks if a given table has all keys inside it, building a chain with each other.
---@param ctx table The table to inspect
---@param ... any The keys to check
---@return boolean status true if all keys are there and false if not
local function checkTableTree(ctx, ...)
    local tmp = ctx
    for _, key in pairs({ ... }) do
        if tmp[key] then
            tmp = tmp[key]
        else
            return false
        end
    end
    return true
end

---Go through layout and replace stuff with our content
---@param page table The Basalt frame layout
local function insertData(page, ctx)
    local layout = page:getLastLayout()
    local function repalceContent(layout)
        for _, object in pairs(layout) do
            -- Is frame?
            if object.addLayout then
                id = object:getName()
                -- Is frame for products?
                if id:find("products") then
                    local listHeight = 1
                    local nW, nH = object:getSize()
                    for i, product in pairs(ctx.products) do
                        if product.displayName and product.price and product.description then
                            local item = object:addFrame(tostring(i))
                                :setPosition(1, listHeight)
                                :setSize(nW, 3)
                                :setBackground(colors.gray)
                            local offX = 0
                            -- Image
                            local bimg = {}
                            for k, v in pairs(ctx.image["missing.bimg"]) do
                                bimg[k] = v
                            end
                            if product.bimg then
                                if type(product.bimg) == "table" then
                                    bimg[1] = product.bimg
                                elseif type(product.bimg) == "string" then
                                    bimg = fs.combine(sImg, product.bimg)
                                end
                            end
                            if bimg then
                                local img = item:addBimg()
                                    :setPosition(1, 1)
                                    :loadImage(bimg)
                                local x, y = img:getSize()
                                offX = x + 2
                            end
                            -- Title
                            item:addLabel()
                                :setText(product.displayName)
                                :setPosition(2 + offX, 1)
                                :setSize(1, nW - (6 + offX), 1)
                                :setBackground(colors.gray)
                            -- Description
                            item:addLabel()
                                :setText(product.description)
                                :setPosition(2 + offX, 2)
                                :setSize(2, nW - (6 + offX), 1)
                                :setForeground(colors.lightGray)
                                :setBackground(colors.gray)
                            -- Button
                            item:addButton()
                                :setText("BUY")
                                :setPosition(nW - 6, 1)
                                :setSize(7, 3)
                                :setForeground(colors.white)
                                :setBackground(colors.green)
                                :setMetadata({
                                    id = i
                                })
                                :onClick(purchase)


                            listHeight = listHeight + 4
                        end
                    end
                    object:setSize(nW, listHeight)
                    -- Look into frame for other stuff
                else
                    local subLayout = object:getLastLayout()
                    repalceContent(subLayout)
                end
                -- Is something else we are looking for?
            elseif object.setText then
                local id = object:getName()
                local txt = object:getValue()
                if id:find("subtitle") then
                    if checkTableTree(ctx, "config", "details", "description") then
                        object:setText(string.format(txt, ctx.config.details.description))
                    end
                elseif id:find("title") then
                    if checkTableTree(ctx, "config", "details", "title") then
                        object:setText(string.format(txt, ctx.config.details.title))
                    end
                end
            end
        end
    end

    repalceContent(layout)
end

local function main(ctx)
    local base = basalt.createFrame()
        :setTheme(ctx.theme)
        :addLayout(fs.combine(sPage, "index.xml"))

    local sCurPage = ""
    parallel.waitForAny(
        basalt.autoUpdate,
        function()
            local menubar = base:getDeepObject("main-menubar")
            local displayPage = base:getDeepObject("main-content")

            if not (menubar or displayPage) then
                return printError("Content is missing in index.xml!\nMake sure the following things are there:\n * Menubar | id=\"main-menubar\"\n * Frame   | id=\"main-content\"")
            end

            while true do
                -- Change pages
                local tmpPage = menubar:getItem(menubar:getItemIndex()).text
                if tmpPage ~= sCurPage then
                    sCurPage = tmpPage
                    local oldLayout = displayPage:getLastLayout()
                    for _, v in pairs(oldLayout) do
                        displayPage:removeObject(v)
                    end

                    displayPage:addLayoutFromString(ctx.pages[sCurPage:lower() .. ".xml"])
                    insertData(displayPage, ctx)
                end

                -- Update products
                if updateContent then

                    updateContent = false
                end
                sleep()
            end
        end
    )
end

-- INIT
local noErrors = true
local args = table.pack(...)
xpcall(function()
    init(table.unpack(args, 1, args.n))
end, function(err)
    printError(err)
    noErrors = false
end)

-- MAIN
if noErrors then
    xpcall(function()
        main(ctx)
    end, function(err)
        printError(err)
    end)
end