-- Game Session Recorder & Replay Browser
-- Records game sessions to .cam files and provides a replay browser

local replayBrowserWindow
local recordingsList
local emptyLabel
local recIndicator
local isRecording = false

function init()
    connect(g_game, {
        onGameStart = onGameStart,
        onGameEnd = onGameEnd
    })
end

function terminate()
    disconnect(g_game, {
        onGameStart = onGameStart,
        onGameEnd = onGameEnd
    })

    hideReplayBrowser()
    destroyRecIndicator()
end

-- Called from characterlist.lua to check if recording is enabled
function isRecordingEnabled()
    return g_settings.getBoolean('recordSession', false)
end

-- Called from characterlist.lua to generate the recording filename
function getRecordFilename(characterName)
    if not isRecordingEnabled() then
        return ''
    end
    return characterName .. '_' .. os.date('%Y%m%d_%H%M%S') .. '.cam'
end

-- Game state signals
function onGameStart()
    if isRecordingEnabled() then
        isRecording = true
        createRecIndicator()
    end
end

function onGameEnd()
    isRecording = false
    destroyRecIndicator()
end

-- Recording indicator (red "REC" label in top-left)
function createRecIndicator()
    if recIndicator then
        return
    end

    local mapPanel = modules.game_interface and modules.game_interface.getMapPanel and modules.game_interface.getMapPanel()
    if not mapPanel then
        return
    end

    recIndicator = g_ui.createWidget('UILabel', mapPanel)
    recIndicator:setId('recIndicator')
    recIndicator:setText('[REC]')
    recIndicator:setColor('#ff0000')
    recIndicator:setFont('verdana-11px-monochrome')
    recIndicator:addAnchor(AnchorTop, 'parent', AnchorTop)
    recIndicator:addAnchor(AnchorLeft, 'parent', AnchorLeft)
    recIndicator:setMarginTop(5)
    recIndicator:setMarginLeft(5)
    recIndicator:setOpacity(0.85)
end

function destroyRecIndicator()
    if recIndicator then
        recIndicator:destroy()
        recIndicator = nil
    end
end

-- Replay Browser
function showReplayBrowser()
    if replayBrowserWindow then
        replayBrowserWindow:show()
        replayBrowserWindow:raise()
        replayBrowserWindow:focus()
        refreshRecordingsList()
        return
    end

    replayBrowserWindow = g_ui.displayUI('game_recorder')
    replayBrowserWindow:show()
    replayBrowserWindow:raise()
    replayBrowserWindow:focus()
    recordingsList = replayBrowserWindow:getChildById('recordingsList')
    emptyLabel = replayBrowserWindow:getChildById('emptyLabel')

    refreshRecordingsList()
end

function hideReplayBrowser()
    if replayBrowserWindow then
        replayBrowserWindow:destroy()
        replayBrowserWindow = nil
        recordingsList = nil
        emptyLabel = nil
    end
end

-- List .cam files from the records/ directory
-- PacketRecorder writes via std::ofstream with relative path "records/" from CWD
-- We use os.execute + temp file to avoid io.popen freezes on Wine/Windows
local function listCamFiles()
    local camFiles = {}
    local tmpFile = 'records/.cam_list.tmp'

    local cmd
    if g_app.getOs() == 'windows' then
        cmd = 'dir /b "records\\*.cam" > "' .. tmpFile .. '" 2>nul'
    else
        cmd = 'ls -1 records/ > "' .. tmpFile .. '" 2>/dev/null'
    end

    os.execute(cmd)

    local f = io.open(tmpFile, 'r')
    if f then
        for line in f:lines() do
            local filename = line:match('^%s*(.-)%s*$') or line
            if filename:match('%.cam$') then
                table.insert(camFiles, filename)
            end
        end
        f:close()
        os.remove(tmpFile)
    end

    return camFiles
end

function refreshRecordingsList()
    if not recordingsList then
        return
    end

    recordingsList:destroyChildren()

    local camFiles = listCamFiles()

    -- Sort by name descending (newest first since names contain timestamps)
    table.sort(camFiles, function(a, b) return a > b end)

    if #camFiles == 0 then
        emptyLabel:show()
        replayBrowserWindow:getChildById('buttonPlay'):disable()
        replayBrowserWindow:getChildById('buttonDelete'):disable()
    else
        emptyLabel:hide()
        replayBrowserWindow:getChildById('buttonPlay'):enable()
        replayBrowserWindow:getChildById('buttonDelete'):enable()

        for _, file in ipairs(camFiles) do
            local widget = g_ui.createWidget('RecordingEntry', recordingsList)
            widget.fileName = file

            -- Parse filename: CharName_20260308_141500.cam -> "CharName - 2026/03/08 14:15:00"
            local displayName = file:gsub('%.cam$', '')
            local name, date, time = displayName:match('^(.+)_(%d%d%d%d%d%d%d%d)_(%d%d%d%d%d%d)$')
            if name and date and time then
                local formatted = name .. ' - ' ..
                    date:sub(1,4) .. '/' .. date:sub(5,6) .. '/' .. date:sub(7,8) .. ' ' ..
                    time:sub(1,2) .. ':' .. time:sub(3,4) .. ':' .. time:sub(5,6)
                widget:getChildById('recordName'):setText(formatted)
            else
                widget:getChildById('recordName'):setText(displayName)
            end
        end
    end
end

function playSelectedRecording()
    if not recordingsList then
        return
    end

    local selected = recordingsList:getFocusedChild()
    if not selected or not selected.fileName then
        displayErrorBox(tr('Error'), tr('Please select a recording to play.'))
        return
    end

    if g_game.isOnline() then
        displayErrorBox(tr('Error'), tr('You must be logged out to play a recording.'))
        return
    end

    local fileName = selected.fileName
    hideReplayBrowser()

    -- Hide login UI before starting playback
    if CharacterList and CharacterList.hide then
        CharacterList.hide()
    end
    if EnterGame and EnterGame.hide then
        EnterGame.hide()
    end

    g_game.playRecord(fileName)
end

function deleteSelectedRecording()
    if not recordingsList then
        return
    end

    local selected = recordingsList:getFocusedChild()
    if not selected or not selected.fileName then
        displayErrorBox(tr('Error'), tr('Please select a recording to delete.'))
        return
    end

    local fileName = selected.fileName
    local confirmBox

    confirmBox = displayGeneralBox(tr('Confirm Delete'),
        tr('Are you sure you want to delete "%s"?', fileName),
        {
            { text = tr('Yes'), callback = function()
                os.remove('records/' .. fileName)
                refreshRecordingsList()
                confirmBox:destroy()
                confirmBox = nil
            end },
            { text = tr('No'), callback = function()
                confirmBox:destroy()
                confirmBox = nil
            end }
        },
        nil, nil, nil)
end
