-- Pathfinder Card Game — TTS Deck Code Bag v1.8.2
-- Attach this script to bag GUID 6bb1e6.
-- Drop one Card/Deck into the bag, then press Copy Code.
-- The clean PFTCG2 code is shown in the on-bag text field and printed only to
-- the pressing player's chat. The stored deck is then deleted from the bag.

local EXPECTED_GUID = "6bb1e6"
local CODE_INPUT_INDEX = 0
local lastCode = ""
local lastObjectGuid = ""

local function stripTags(value)
    value = tostring(value or "")
    value = value:gsub("%b[]", "")
    value = value:gsub("^%s+", ""):gsub("%s+$", "")
    return value
end

local function metadataValue(text, label)
    for line in tostring(text or ""):gmatch("[^\r\n]+") do
        local clean = stripTags(line)
        local value = clean:match(label .. "%s*:%s*(.+)")
        if value then return value end
    end
    return nil
end

local function percentEncode(value)
    return tostring(value or ""):gsub("([^%w%-_%.~])", function(char)
        return string.format("%%%02X", string.byte(char))
    end)
end

local function cardKey(name)
    name = tostring(name or "")
    local id = name:match("#([A-Z]+%d+)") or name:match("%(([A-Z]+%d+)%)")
    if not id then return nil end
    if name:lower():find("(alt)", 1, true) or name:lower():find("alternative art", 1, true) then
        return id .. "(Alt)"
    end
    return id
end

local function addCard(counts, order, name)
    local key = cardKey(name)
    if not key then return end
    if not counts[key] then table.insert(order, key); counts[key] = 0 end
    counts[key] = counts[key] + 1
end

local function readDeckObject(object)
    local counts, order = {}, {}
    if object and object.tag == "Deck" then
        for _, info in ipairs(object.getObjects() or {}) do
            addCard(counts, order, tostring(info.nickname or info.name or "") .. " " .. tostring(info.description or ""))
        end
    elseif object then
        addCard(counts, order, tostring(object.getName() or "") .. " " .. tostring(object.getDescription() or ""))
    end
    return counts, order
end

local function buildCode(object)
    local rawName = object and object.getName() or ""
    local rawDescription = object and object.getDescription() or ""
    local combined = rawName .. "\n" .. rawDescription
    local deckName = metadataValue(combined, "Deck Name") or stripTags(rawName)
    if deckName == "" or deckName == "Deck" or deckName == "Card Deck" then deckName = "Imported TTS Deck" end
    local player = metadataValue(combined, "Player") or "PFTCG Player"
    local counts, order = readDeckObject(object)
    local parts = {}
    for _, key in ipairs(order) do table.insert(parts, key .. "=" .. tostring(counts[key])) end
    return "PFTCG2|" .. percentEncode(player) .. "|" .. percentEncode(deckName) .. "|" .. table.concat(parts, ",")
end

local function removeStoredDeck()
    if lastObjectGuid == "" then return end
    local guid = lastObjectGuid
    lastObjectGuid = ""
    local ok = pcall(function()
        self.takeObject({
            guid = guid,
            position = {self.getPosition().x, self.getPosition().y + 3, self.getPosition().z},
            smooth = false,
            callback_function = function(object) if object then destroyObject(object) end end
        })
    end)
    if not ok then
        for _, entry in ipairs(self.getObjects() or {}) do
            if tostring(entry.guid or "") == guid then
                pcall(function() self.takeObject({guid=guid,callback_function=function(object) if object then destroyObject(object) end end}) end)
                break
            end
        end
    end
end

function ignoreCodeInput() end

function copyCode(_, playerColor)
    if lastCode == "" then
        printToColor("Drop a PFTCG deck into this bag first.", playerColor, {1,0.4,0.4})
        return
    end
    -- TTS Lua cannot directly write arbitrary text to the operating-system
    -- clipboard. Printing it privately and keeping it in the input field makes
    -- it selectable with Ctrl+C without exposing it to other players.
    printToColor(lastCode, playerColor, {0.95,0.85,0.35})
    self.editInput({index=CODE_INPUT_INDEX,value=lastCode})
    removeStoredDeck()
    printToColor("Code ready to copy. The deck was removed from the bag.", playerColor, {0.35,1,0.45})
end

function onLoad()
    self.clearInputs(); self.clearButtons()
    self.setDescription("Drop a deck into this bag, then press Copy Code. The deck is deleted after the code is produced.")
    self.setGMNotes("")
    self.createInput({
        input_function="ignoreCodeInput",function_owner=self,label="Generated PFTCG2 code",
        position={0,0.35,-0.48},rotation={0,180,0},width=4700,height=420,font_size=150,
        color={0.96,0.93,0.84},font_color={0.10,0.07,0.04},alignment=2,validation=1,value="",
        tooltip="Click inside and press Ctrl+C after pressing Copy Code."
    })
    self.createButton({
        click_function="copyCode",function_owner=self,label="Copy Code",
        position={0,0.35,0.38},rotation={0,180,0},width=1900,height=520,font_size=240,
        color={0.18,0.12,0.07},font_color={1,0.82,0.42},
        tooltip="Print the code privately, leave it in the text field, and delete the stored deck."
    })
    if self.getGUID() ~= EXPECTED_GUID then print("PFTCG Deck Code Bag: expected GUID " .. EXPECTED_GUID .. ", current GUID is " .. self.getGUID()) end
end

function onObjectEnterContainer(container, object)
    if container ~= self or not object then return end
    lastCode = buildCode(object)
    lastObjectGuid = tostring(object.getGUID() or "")
    Wait.frames(function()
        self.editInput({index=CODE_INPUT_INDEX,value=lastCode})
        broadcastToAll("PFTCG deck code generated. Press Copy Code on the bag when ready.", {0.95,0.78,0.25})
    end, 1)
end
