local episodePath = mem(0x00B2C61C,FIELD_STRING)

-- The following code makes the loading screen slightly less restricted
do
    local function exists(path)
        local f = io.open(path,"r")

        if f ~= nil then
            f:close()
            return true
        else
            return false
        end
    end

    Misc.resolveFile = (function(path)
        local inScriptPath = getSMBXPath().. "\\scripts\\".. path
        local inEpisodePath = episodePath.. path

        return (exists(path) and path) or (exists(inEpisodePath) and inEpisodePath) or (exists(inScriptPath) and inScriptPath) or nil
    end)

    Misc.resolveGraphicsFile = Misc.resolveFile -- good enough lol

    -- Make require work better
    local oldRequire = require

    function require(path)
        local inScriptPath = getSMBXPath().. "\\scripts\\".. path.. ".lua"
        local inScriptBasePath = getSMBXPath().. "\\scripts\\base\\".. path.. ".lua"
        local inEpisodePath = episodePath.. path.. ".lua"

        local path = (exists(inEpisodePath) and inEpisodePath) or (exists(inScriptPath) and inScriptPath) or (exists(inScriptBasePath) and inScriptBasePath)
        assert(path ~= nil,"module '".. path.. "' not found.")

        return oldRequire(path)
    end


    -- classexpander stuff
    function string.split(s, p, exclude, plain)
        if  exclude == nil  then  exclude = false; end;
        if  plain == nil  then  plain = true; end;

        local t = {};
        local i = 0;
    
        if(#s <= 1) then
            return {s};
        end
    
        while true do
            local ls,le = s:find(p, i, plain);	--find next split pattern
            if (ls ~= nil) then
                table.insert(t, string.sub(s, i,le-1));
                i = ls+1;
                if  exclude  then
                    i = le+1;
                end
            else
                table.insert(t, string.sub(s, i));
                break;
            end
        end
        
        return t;
    end

    function table.clone(t)
		local rt = {};
		for k,v in pairs(t) do
			rt[k] = v;
		end
		setmetatable(rt, getmetatable(t));
		return rt;
	end

    function table.ishuffle(t)
		for i=#t,2,-1 do 
			local j = RNG.randomInt(1,i)
			t[i], t[j] = t[j], t[i]
		end
		return t
	end

    function math.clamp(a,mi,ma)
		mi = mi or 0;
		ma = ma or 1;
		return math.min(ma,math.max(mi,a));
	end

    function math.lerp(a,b,t)
		return a*(1-t) + b*t
	end


    
    _G.lunatime = require("engine/lunatime")
    _G.Color = require("engine/color")
    _G.RNG = require("rng")
    _G.vector = require("vectr")

    io.exists = exists

    function lunatime.tick()
        return loadscreenTimer
    end
    function lunatime.drawtick()
        return loadscreenTimer
    end
    function lunatime.time()
        return lunatime.toSeconds(loadscreenTimer)
    end
    function lunatime.drawtime()
        return lunatime.toSeconds(loadscreenTimer)
    end
end


local textplus = require("textplus")


local mainFont = textplus.loadFont("resources/font/mainFont.ini")

local modeIconPaths = {
    [0] = "resources/modeIcon_arena.png",
    [1] = "resources/modeIcon_stars.png",
    [2] = "resources/modeIcon_stone.png",
    [3] = "resources/modeIcon_special.png",
    [4] = "resources/modeIcon_phanto.png",
    [5] = "resources/modeIcon_bombs.png",
}

local dataFilePath = episodePath.. "loadscreenData.txt"


local loadedDataFile

local levelFolderPath
local levelIcon
local levelName,levelAuthor,levelMode

local levelNameLayout,levelAuthorLayout

local playerPlateGroups = {}
local widestPlayerPlateGroupWidth = 0
local tallestPlayerPlateGroupHeight = 0


local loadIconFrames = 7
local loadIconFrameDelay = 8
local loadIconFadeDelay = 12
local loadIconFadeTime = 42

local playerPlateGroupNameGap = 8
local playerPlateGroupGap = 16
local playerPlateHeadGap = 40
local playerPlateGap = 28
local playerPlateDividerColor = Color.darkgrey
local playerPlateDividerMargin = 12
local playerPlateDividerHeight = 2


local loadscreenTimer = 0

local fadeOut = 0


local screenWidth = 800
local screenHeight = 600


local function setUpLevelLayout()
    levelIcon = Graphics.loadImage(episodePath.. levelFolderPath.. "/battleIcon.png") or Graphics.loadImage(episodePath.. "/resources/randomLevel.png")
    modeIcon = Graphics.loadImage(episodePath.. modeIconPaths[tonumber(levelMode)])

    local maxWidth = levelIcon.width - ((modeIcon ~= nil and (modeIcon.width + 8)) or 0)

    levelNameLayout = textplus.layout(levelName,maxWidth,{font = mainFont,plaintext = true})
    levelAuthorLayout = textplus.layout(levelAuthor,maxWidth,{font = mainFont,color = Color.lightgrey,plaintext = true})
end


local function loadHeadImage(characterName,costumeName)
    if costumeName ~= "" then
        -- Use costume image from its own folder, if available
        local costumePath = episodePath.. "\\costumes\\".. characterName.. "\\".. costumeName.. "\\battleHead.png"

        if io.exists(costumePath) then
            return Graphics.loadImage(costumePath)
        end

        -- Use costume image from the character head folder, if available
        costumePath = episodePath.. "resources/characterHeads/".. costumeName.. ".png"

        if io.exists(costumePath) then
            return Graphics.loadImage(costumePath)
        end
    end

    -- Use character image
    local characterPath = episodePath.. "resources/characterHeads/".. characterName.. ".png"

    if io.exists(characterPath) then
        return Graphics.loadImage(characterPath)
    end

    return nil
end

local function loadPlayerData(f)
    local groupCount = tonumber(f:read())

    for groupIndex = 1,groupCount do
        local plateGroup = {plates = {}}
        local groupHeight = 0

        local groupName = f:read()
        local groupNameColor = Color.parse(f:read())

        local playerCount = tonumber(f:read())


        if groupName ~= "" then
            plateGroup.nameLayout = textplus.layout(groupName,nil,{font = mainFont,color = groupNameColor,xscale = 2,yscale = 2,plaintext = true})
            widestPlayerPlateGroupWidth = math.max(widestPlayerPlateGroupWidth,plateGroup.nameLayout.width + playerPlateDividerMargin*2)

            groupHeight = groupHeight + plateGroup.nameLayout.height + playerPlateGroupNameGap
        end


        for playerIndex = 1,playerCount do
            local playerPlate = {}

            local username = f:read()
            local color = Color.parse(f:read())
            local characterName = f:read()
            local costumeName = f:read()

            playerPlate.nameLayout = textplus.layout(username,nil,{font = mainFont,color = color,xscale = 2,yscale = 2,plaintext = true})
            playerPlate.headImage = loadHeadImage(characterName,costumeName)

            playerPlate.y = groupHeight

            widestPlayerPlateGroupWidth = math.max(widestPlayerPlateGroupWidth,playerPlate.nameLayout.width + playerPlateHeadGap + playerPlateDividerMargin*2)
            groupHeight = groupHeight + playerPlateGap

            table.insert(plateGroup.plates,playerPlate)
        end

        tallestPlayerPlateGroupHeight = math.max(tallestPlayerPlateGroupHeight,groupHeight)

        table.insert(playerPlateGroups,plateGroup)
    end
end


local function loadDataFile()
    -- Load the file
    local f = io.open(dataFilePath,"r")

    if f ~= nil then
        levelFolderPath = f:read()

        if levelFolderPath ~= nil then
            levelName = f:read()
            levelAuthor = f:read()
            levelMode = f:read()

            setUpLevelLayout()
            loadPlayerData(f)
        end

        f:close()
    end

    -- Then, delete its contents
    f = io.open(dataFilePath,"w")

    if f ~= nil then
        f:close()
    end
end


local function drawLoadIcon()
    if loadscreenTimer <= loadIconFadeDelay then
        return
    end

    local image = Graphics.sprites.hardcoded["30-5"].img
    local width = image.width
    local height = image.height/loadIconFrames

    local frame = math.floor((loadscreenTimer - loadIconFadeDelay)/loadIconFrameDelay)%loadIconFrames
    local opacity = math.min(1,(loadscreenTimer - loadIconFadeDelay)/loadIconFadeTime)

    Graphics.drawBox{
        texture = image,
        priority = -10,
        color = Color.white.. opacity,
        x = screenWidth - width,
        y = screenHeight - height,
        sourceWidth = width,
        sourceHeight = height,
        sourceY = frame*height,
    }
end

local function drawLevel()
    if loadscreenTimer <= loadIconFadeDelay or levelFolderPath == nil then
        return
    end

    local opacity = math.min(1,(loadscreenTimer - loadIconFadeDelay)/loadIconFadeTime)

    local x = 16
    local y = screenHeight - 16

    -- Author
    y = y - levelAuthorLayout.height

    textplus.render{
        layout = levelAuthorLayout,
        priority = -10,
        color = Color.white*opacity,
        x = x,y = y,
    }

    -- Name
    y = y - levelNameLayout.height - 2

    textplus.render{
        layout = levelNameLayout,
        priority = -10,
        color = Color.white*opacity,
        x = x,y = y,
    }

    -- Mode icon
    Graphics.drawBox{
        texture = modeIcon,
        priority = -10,
        color = Color.white.. opacity,
        x = x + levelIcon.width - modeIcon.width,
        y = y,
    }

    -- Level icon
    y = y - levelIcon.height - 8

    Graphics.drawBox{
        texture = levelIcon,
        priority = -10,
        color = Color.white.. opacity,
        x = x,y = y,
    }
end

local function drawPlayerPlates()
    local y = (screenHeight - tallestPlayerPlateGroupHeight)*0.5 - 48

    for groupIndex,plateGroup in ipairs(playerPlateGroups) do
        local x = (screenWidth - widestPlayerPlateGroupWidth)*0.5 + ((groupIndex - 1) - (#playerPlateGroups - 1)*0.5)*(widestPlayerPlateGroupWidth + playerPlateGroupGap)

        local groupDelay = (groupIndex - 1)*16
        local dividerOpacity = math.clamp((loadscreenTimer - loadIconFadeDelay - groupDelay)/32)

        if plateGroup.nameLayout ~= nil then
            local nameOpacity = math.clamp((loadscreenTimer - loadIconFadeDelay - groupDelay)/16)

            textplus.render{
                layout = plateGroup.nameLayout,
                color = Color.white*nameOpacity,
                priority = -60,
                x = x + (widestPlayerPlateGroupWidth - plateGroup.nameLayout.width)*0.5,
                y = y,
            }
        end

        for plateIndex,playerPlate in ipairs(plateGroup.plates) do
            local plateDelay = (plateIndex - 1)*4 + groupDelay

            local enterTimer = math.clamp((loadscreenTimer - loadIconFadeDelay - plateDelay)/16)
            local enterOffset = -(1 - enterTimer)*(1 - enterTimer)*16
            local opacity = enterTimer

            Graphics.drawBox{
                color = playerPlateDividerColor.. dividerOpacity,
                priority = -60,
                x = x,
                y = y + playerPlate.y,
                width = widestPlayerPlateGroupWidth,
                height = playerPlateDividerHeight,
            }

            if plateIndex == #plateGroup.plates then
                Graphics.drawBox{
                    color = playerPlateDividerColor.. dividerOpacity,
                    priority = -60,
                    x = x,
                    y = y + playerPlate.y + playerPlateGap,
                    width = widestPlayerPlateGroupWidth,
                    height = playerPlateDividerHeight,
                }
            end

            if playerPlate.headImage ~= nil then
                Graphics.drawBox{
                    texture = playerPlate.headImage,
                    priority = -55,
                    color = Color.white.. opacity,
                    x = x + math.max(0,playerPlateHeadGap*0.5 - playerPlate.headImage.width)*0.5 + playerPlateDividerMargin + enterOffset,
                    y = y + playerPlate.y + (playerPlateGap - playerPlate.headImage.height)*0.5 + 1,
                }
            end

            textplus.render{
                layout = playerPlate.nameLayout,
                priority = -50,
                color = Color.white*opacity,
                x = x + playerPlateHeadGap + playerPlateDividerMargin + enterOffset,
                y = y + playerPlate.y + (playerPlateGap - playerPlate.nameLayout.height)*0.5 + 3,
            }
        end
    end
end


function onDraw()
    if not loadedDataFile then
        loadDataFile()
        loadedDataFile = true
    end

    drawLoadIcon()
    drawLevel()
    drawPlayerPlates()

    if levelFolderPath ~= nil then
        if Misc.getLoadingFinished() then
            fadeOut = math.min(1,fadeOut + 1/8)
        end

        if fadeOut < 1 then
            Misc.setLoadScreenTimeout(15)
        else
            Misc.setLoadScreenTimeout(0)
        end
    end

    if fadeOut > 0 then
        Graphics.drawBox{
            color = Color.black.. fadeOut,priority = 0,
            x = 0,y = 0,width = screenWidth,height = screenHeight,
        }
    end

    loadscreenTimer = loadscreenTimer + 1
end