-- bimg_player.lua
-- BIMG Player (Shell Version)
-- Usage: bimg_player <file.bimg> [options]

-- Initialize settings
local running = false
local currentAnimation = nil
local httpEnabled = http and http.get

-- Show help message
local function showHelp()
    print("BIMG Player - Usage:")
    print("bimg_player <file.bimg> [options]")
    print()
    print("Options:")
    print("  --loop       Loop animation")
    print("  --url        Load from URL")
    print("  --monitor <side> Play on specific monitor (top/bottom/left/right/front/back)")
    print("  --scale <num>   Set display scale (0.5-3)")
    print("  --fps <num>     Set frame rate (1-60)")
    print("  --help       Show this help")
    print()
    print("Examples:")
    print("bimg_player animation.bimg --loop")
    print("bimg_player http://example.com/image.bimg --url --monitor top")
end

-- Draw single frame
local function drawFrame(frame, termObj)
    for y, row in ipairs(frame) do
        termObj.setCursorPos(1, y)
        termObj.blit(table.unpack(row))
    end
    if frame.palette then 
        for i = 0, #frame.palette do
            local c = frame.palette[i]
            if type(c) == "table" then 
                termObj.setPaletteColor(2^i, table.unpack(c))
            else 
                termObj.setPaletteColor(2^i, c) 
            end
        end 
    end
end

-- Load image file
local function loadImageFile(path)
    local file, err = fs.open(shell.resolve(path), "rb")
    if not file then error("Cannot open file: " .. err) end
    local data = file.readAll()
    file.close()
    local success, img = pcall(textutils.unserialize, data)
    if not success then error("Invalid BIMG file: " .. img) end
    return img
end

-- Load image from URL
local function loadImageURL(url)
    if not httpEnabled then
        error("HTTP API not available, cannot load from URL")
    end
    
    print("Downloading from URL: "..url)
    local response = http.get(url)
    if not response then
        error("Download failed: " .. url)
    end
    
    local data = response.readAll()
    response.close()
    local success, img = pcall(textutils.unserialize, data)
    if not success then error("Invalid BIMG data: " .. img) end
    return img
end

-- Monitor calibration
local function calibrateMonitors(width, height)
    term.clear()
    term.setCursorPos(1, 1)
    print("Multi-Monitor Calibration Mode")
    print("Please right-click each monitor in order")
    print("From top-left to bottom-right, left to right first")
    
    local monitors = {}
    local names = {}
    
    for y = 1, height do
        monitors[y] = {}
        for x = 1, width do
            local _, oy = term.getCursorPos()
            
            -- Draw calibration UI
            for ly = 1, height do
                term.setCursorPos(3, oy + ly - 1)
                term.clearLine()
                for lx = 1, width do 
                    term.blit('\x8F ', (lx == x and ly == y) and '00' or '77', 'ff') 
                end
            end
            
            term.setCursorPos(3, oy + height)
            term.write(string.format("Position (%d, %d)", x, y))
            term.setCursorPos(1, oy)
            
            -- Wait for monitor click
            repeat
                local _, name = os.pullEvent('monitor_touch')
                monitors[y][x] = name
            until not names[name]
            
            names[monitors[y][x]] = true
            sleep(0.25)
        end
    end
    
    settings.set('bimg_player.multimonitor', monitors)
    settings.save()
    print("Calibration complete. Settings saved.")
    
    return monitors
end

-- Stop playback
local function stopPlayback()
    if currentAnimation then
        currentAnimation.running = false
        os.queueEvent("bimg_stop")
    end
    running = false
end

-- Clean up terminal
local function cleanupTerminal(termObj)
    termObj.setBackgroundColor(colors.black)
    termObj.setTextColor(colors.white)
    termObj.clear()
    termObj.setCursorPos(1, 1)
    for i = 0, 15 do 
        termObj.setPaletteColor(2^i, term.nativePaletteColor(2^i)) 
    end
end

-- Main playback function
local function playAnimation(img, options)
    options = options or {}
    local termObj = options.terminal or term
    local loop = options.loop or false
    local fps = options.fps or (img.secondsPerFrame and (1/img.secondsPerFrame)) or 20
    local frameDelay = 1/fps
    
    -- Set running state
    running = true
    currentAnimation = {
        running = true,
        loop = loop
    }
    
    -- Playback function
    local function playFrames()
        repeat
            if img.multiMonitor then
                local width, height = img.multiMonitor.width, img.multiMonitor.height
                local monitors = settings.get('bimg_player.multimonitor')
                
                -- Calibrate if needed
                if not monitors or #monitors < height or #monitors[1] < width then
                    monitors = calibrateMonitors(width, height)
                end
                
                -- Multi-monitor playback
                for i = 1, #img, width * height do
                    if not currentAnimation.running then break end
                    
                    for y = 1, height do
                        for x = 1, width do
                            local frameIndex = i + (y-1) * width + x-1
                            if frameIndex <= #img then
                                local monitor = peripheral.wrap(monitors[y][x])
                                if monitor then
                                    drawFrame(img[frameIndex], monitor)
                                end
                            end
                        end
                    end
                    
                    sleep(frameDelay)
                end
            else
                -- Single monitor playback
                termObj.clear()
                for _, frame in ipairs(img) do
                    if not currentAnimation.running then break end
                    drawFrame(frame, termObj)
                    sleep(frameDelay)
                end
            end
        until not loop or not currentAnimation.running
    end
    
    -- Start playback
    parallel.waitForAny(
        function()
            local ok, err = pcall(playFrames)
            if not ok then printError("Playback error: "..err) end
            cleanupTerminal(termObj)
            running = false
        end,
        function()
            while currentAnimation.running do
                os.pullEvent("bimg_stop")
                currentAnimation.running = false
            end
        end
    )
end

-- Parse command line arguments
local function parseArguments(args)
    local options = {}
    local path = nil
    
    local i = 1
    while i <= #args do
        local arg = args[i]
        if arg == "--loop" then
            options.loop = true
        elseif arg == "--url" then
            options.isURL = true
        elseif arg == "--monitor" then
            i = i + 1
            options.terminal = peripheral.wrap(args[i])
            if not options.terminal then
                error("Invalid monitor side: " .. (args[i] or "nil"))
            end
        elseif arg == "--scale" then
            i = i + 1
            local scale = tonumber(args[i])
            if scale and scale >= 0.5 and scale <= 3 then
                options.scale = scale
            else
                error("Invalid scale value (should be 0.5-3)")
            end
        elseif arg == "--fps" then
            i = i + 1
            local fps = tonumber(args[i])
            if fps and fps >= 1 and fps <= 60 then
                options.fps = fps
            else
                error("Invalid FPS value (should be 1-60)")
            end
        elseif arg == "--help" then
            showHelp()
            return nil
        elseif not path and not arg:find("^-") then
            path = arg
        else
            error("Unknown option: " .. arg)
        end
        i = i + 1
    end
    
    if not path then
        showHelp()
        return nil
    end
    
    return path, options
end

-- Main program entry
local function main(...)
    local args = {...}
    
    -- Show help if no arguments
    if #args == 0 then
        showHelp()
        return
    end
    
    -- Parse arguments
    local ok, path, options = pcall(parseArguments, args)
    if not ok then
        printError(path) -- Here path contains the error message
        showHelp()
        return
    end
    
    if not path then return end -- User requested help
    
    -- Load image
    local img
    if options.isURL then
        img = loadImageURL(path)
    else
        img = loadImageFile(path)
    end
    
    -- Apply display scale
    if options.scale and img.multiMonitor then
        img.multiMonitor.scale = options.scale
    end
    
    -- Start playback
    playAnimation(img, options)
    
    -- Wait for keypress if not looping
    if not options.loop then
        stopPlayback()
    end
end

-- Start program
main(...)