--import config.lua
--import utils.lua
--import uiLib.lua

--import userConfig.lua

MemoryVisualizer = {}

MemoryVisualizer.GRID_COLS = 10
MemoryVisualizer.GRID_ROWS = 14
MemoryVisualizer.SPRITE_W = 7
MemoryVisualizer.SPRITE_H = 7
MemoryVisualizer.GAP = 1

POLYEDITOR_PROGID = 3

function start()
	MemoryVisualizer.updateTick = 0
	MemoryVisualizer.usedIndices = {}
	MemoryVisualizer.processedCount = 0
	MemoryVisualizer.lastBatchIndices = {}

	UILib.init()

	MemoryVisualizer.backgroundSprite = allocSprite()
	MemoryVisualizer.backgroundSpriteData = {}
	MemoryVisualizer.backgroundSpriteData.position = vec3(0.0, 0.0, UILib.uiDepth_views + 20)
	MemoryVisualizer.backgroundSpriteData.size = vec2(8 * 10, 8 * 17 + 2)
	MemoryVisualizer.backgroundSpriteData.color = Config.uiColors.memoryVisualizerBackground

	setSprite(MemoryVisualizer.backgroundSprite, MemoryVisualizer.backgroundSpriteData)

	local cellZ = MemoryVisualizer.backgroundSpriteData.position.z + 0.1

	MemoryVisualizer.allSprites = {}
	MemoryVisualizer.pageSprites = {}
	for i = 0, 7 do
		local data = {}
		data.sprite = allocSprite()
		data.position = vec3(i * 10, 0, cellZ)
		data.size = vec2(9, 9)
		data.color = Config.uiColors.memoryVisualizerBackgroundInactive
		setSprite(data.sprite, data)
		MemoryVisualizer.pageSprites[i + 1] = data
		MemoryVisualizer.allSprites[#MemoryVisualizer.allSprites + 1] = data

		data.info = {"page " .. (i + 1), ""}
	end

	MemoryVisualizer.cellSprites = {}
	MemoryVisualizer.diskOccupancySprites = {}
	local cellW = MemoryVisualizer.SPRITE_W + MemoryVisualizer.GAP
	local cellH = MemoryVisualizer.SPRITE_H + MemoryVisualizer.GAP
	local gridOffsetY = 10
	local circleZ = cellZ + 0.1
	local circleSize = vec2(MemoryVisualizer.SPRITE_W - 2, MemoryVisualizer.SPRITE_W - 2)
	local circleColor = Config.uiColors.memoryVisualizerBackgroundInactive

	local maxCells = MESH_MEM_SLOTS + 2

	MemoryVisualizer.assetsSpriteData = {}

	for row = 0, MemoryVisualizer.GRID_ROWS - 1 do
		for col = 0, MemoryVisualizer.GRID_COLS - 1 do
			if #MemoryVisualizer.cellSprites >= maxCells then break end
			local x = col * cellW
			local y = row * cellH + gridOffsetY
			local idx = #MemoryVisualizer.cellSprites + 1

			local cellData = {}
			cellData.sprite = allocSprite()
			cellData.position = vec3(x, y, cellZ)
			cellData.size = vec2(MemoryVisualizer.SPRITE_W, MemoryVisualizer.SPRITE_H)
			cellData.color = Config.uiColors.memoryVisualizerBackgroundInactive
			setSprite(cellData.sprite, cellData)
			MemoryVisualizer.cellSprites[idx] = cellData
			MemoryVisualizer.allSprites[#MemoryVisualizer.allSprites + 1] = cellData

			cellData.info = {"mesh " .. idx, ""}

			if idx > MESH_MEM_SLOTS then
				cellData.info = {"", ""}				
			end

			cellData.switchedOn = false
			cellData.isMeshMemSlot = true

			MemoryVisualizer.assetsSpriteData[idx] = cellData

			local diskData = {}
			diskData.sprite = allocSprite()
			diskData.position = vec3(x + 1, y + 1, circleZ)
			diskData.size = circleSize
			diskData.color = vec4(Config.uiColors.memoryVisualizerBackgroundMemSlotAsset)
			diskData.color.w = 0
			diskData.roundCorner = 2.0
			setSprite(diskData.sprite, diskData)
			MemoryVisualizer.diskOccupancySprites[idx] = diskData
		end

		if #MemoryVisualizer.cellSprites >= maxCells then break end
	end

	MemoryVisualizer.spriteMemBarData = {}
	MemoryVisualizer.spriteMemBarData.sprite = allocSprite()
	MemoryVisualizer.spriteMemBarData.position = vec3(0, 8 * 14 + 2, cellZ)
	MemoryVisualizer.spriteMemBarData.size = vec2(80, 8)
	MemoryVisualizer.spriteMemBarData.color = Config.uiColors.memoryVisualizerBackgroundInactive
	setSprite(MemoryVisualizer.spriteMemBarData.sprite, MemoryVisualizer.spriteMemBarData)
	MemoryVisualizer.allSprites[#MemoryVisualizer.allSprites + 1] = MemoryVisualizer.spriteMemBarData
	MemoryVisualizer.spriteMemBarData.info = {"sprite mem", "0%"}

	MemoryVisualizer.spriteMemIndicatorData = {}
	MemoryVisualizer.spriteMemIndicatorData.sprite = allocSprite()
	MemoryVisualizer.spriteMemIndicatorData.position = vec3(0, 8 * 14 + 2, cellZ + 0.1)
	MemoryVisualizer.spriteMemIndicatorData.size = vec2(0, 8)
	MemoryVisualizer.spriteMemIndicatorData.color = Config.uiColors.memoryVisualizerBackgroundOccupancy
	setSprite(MemoryVisualizer.spriteMemIndicatorData.sprite, MemoryVisualizer.spriteMemIndicatorData)

	MemoryVisualizer.statusLine1 = allocSprite()
	MemoryVisualizer.statusLine1Data = {}
	MemoryVisualizer.statusLine1Data.position = vec3(0, MemoryVisualizer.backgroundSpriteData.size.y - 16, cellZ)
	MemoryVisualizer.statusLine1Data.size = vec2(80, 8)
	MemoryVisualizer.statusLine1Data.color = vec4(0, 0, 0, 0)
	MemoryVisualizer.statusLine1Data.textColor = Config.uiColors.memoryVisualizerBackgroundTextColor
	MemoryVisualizer.statusLine1Data.text = ""
	setSprite(MemoryVisualizer.statusLine1, MemoryVisualizer.statusLine1Data)

	MemoryVisualizer.statusLine2 = allocSprite()
	MemoryVisualizer.statusLine2Data = {}
	MemoryVisualizer.statusLine2Data.position = vec3(0, MemoryVisualizer.backgroundSpriteData.size.y - 8, cellZ)
	MemoryVisualizer.statusLine2Data.size = vec2(80, 8)
	MemoryVisualizer.statusLine2Data.color = vec4(0, 0, 0, 0)
	MemoryVisualizer.statusLine2Data.textColor = Config.uiColors.memoryVisualizerBackgroundTextColor
	MemoryVisualizer.statusLine2Data.text = ""
	setSprite(MemoryVisualizer.statusLine2, MemoryVisualizer.statusLine2Data)

	MemoryVisualizer.statusText = ""

	local programId = getProgramId()
	invokeProgram(MAIN_PROGRAMID, "setViewTitle", programId, "memory")
end

function updateStatusBar()
	local mousePos = getMousePosition()
	local hoveredInfo = nil

	for i = 1, #MemoryVisualizer.allSprites do
		local entry = MemoryVisualizer.allSprites[i]
		if intersectRect(mousePos, vec2(entry.position.x, entry.position.y), entry.size) then
			if entry.info ~= nil then
				hoveredInfo = entry.info
			end
			break
		end
	end

	if hoveredInfo ~= nil then
		if MemoryVisualizer.statusText ~= hoveredInfo[1] .. hoveredInfo[2] then
			MemoryVisualizer.statusText = hoveredInfo[1] .. hoveredInfo[2]

			MemoryVisualizer.statusLine1Data.text = hoveredInfo[1]
			setSprite(MemoryVisualizer.statusLine1, MemoryVisualizer.statusLine1Data)

			MemoryVisualizer.statusLine2Data.text = hoveredInfo[2]
			setSprite(MemoryVisualizer.statusLine2, MemoryVisualizer.statusLine2Data)
		end
	elseif MemoryVisualizer.statusText ~= "" then
		MemoryVisualizer.statusText = ""

		MemoryVisualizer.statusLine1Data.text = ""
		setSprite(MemoryVisualizer.statusLine1, MemoryVisualizer.statusLine1Data)

		MemoryVisualizer.statusLine2Data.text = ""
		setSprite(MemoryVisualizer.statusLine2, MemoryVisualizer.statusLine2Data)
	end
end

function updateMemory()
	local programId = invokeProgram(MAIN_PROGRAMID, "getCurrentProgram")

	if MemoryVisualizer.processedCount == 0 then
		memScan(programId)

		MemoryVisualizer.usedPages = {}
		for i = 1, MEM_PAGES do
			if memCheckPageId(i, programId) == true then
				MemoryVisualizer.usedPages[#MemoryVisualizer.usedPages + 1] = i
			end
		end
	end

	if MemoryVisualizer.updateTick < 10 then
		MemoryVisualizer.updateTick = MemoryVisualizer.updateTick + 1
		return
	end

	MemoryVisualizer.updateTick = 0

	for _, prevIdx in ipairs(MemoryVisualizer.lastBatchIndices) do
		local prev = MemoryVisualizer.allSprites[prevIdx]
		if prev.switchedOn == true then
			if prev.isMeshMemSlot == true then
				prev.color = Config.uiColors.memoryVisualizerBackgroundMemSlot
			else
				prev.color = Config.uiColors.memoryVisualizerBackgroundPage
			end
		else
			prev.color = Config.uiColors.memoryVisualizerBackgroundInactive
		end
		setSprite(prev.sprite, prev)
	end

	local total = #MemoryVisualizer.allSprites
	local remaining = total - MemoryVisualizer.processedCount
	local batchSize = math.min(10, remaining)

	MemoryVisualizer.lastBatchIndices = {}

	for b = 1, batchSize do
		local idx
		repeat
			idx = math.random(1, total)
		until not MemoryVisualizer.usedIndices[idx]
		MemoryVisualizer.usedIndices[idx] = true
		MemoryVisualizer.processedCount = MemoryVisualizer.processedCount + 1
		MemoryVisualizer.lastBatchIndices[b] = idx

		local entry = MemoryVisualizer.allSprites[idx]
		entry.color = Config.uiColors.memoryVisualizerBackgroundInactive

		if idx <= 8 then
			if memCheckPageId(idx, programId) == true then
				entry.color = Config.uiColors.memoryVisualizerBackgroundPage
				entry.switchedOn = true
			else
				entry.switchedOn = false
			end
		end

		if entry.isMeshMemSlot == true then
			local meshSlotId = idx - 8
			local diskSprite = MemoryVisualizer.diskOccupancySprites[meshSlotId]
			if memCheckMeshSlot(meshSlotId, programId) == true then
				entry.switchedOn = true
				entry.color = Config.uiColors.memoryVisualizerBackgroundMemSlot

				local path = memCheckMeshPath(meshSlotId - 1, programId)
				if path ~= "" then
					entry.info[2] = path
					diskSprite.color.w = 255
				else
					entry.info[2] = ""
					diskSprite.color.w = 0
				end
			else
				entry.switchedOn = false
				diskSprite.color.w = 0
			end
			setSprite(diskSprite.sprite, diskSprite)
		end

		entry.color = entry.color + 12
		setSprite(entry.sprite, entry)
	end

	if MemoryVisualizer.processedCount >= total then
		MemoryVisualizer.usedIndices = {}
		MemoryVisualizer.processedCount = 0
	end

	local spriteCount = 0

	if programId >= GAME_PROGRAMID then
		spriteCount = memGetSpriteCount(programId)
	end

	local spriteRatio = SPRITE_MEM_SLOTS > 0 and (spriteCount / SPRITE_MEM_SLOTS) or 0
	local spriteFullWidth = MemoryVisualizer.spriteMemBarData.size.x
	MemoryVisualizer.spriteMemIndicatorData.size.x = spriteRatio * spriteFullWidth
	setSprite(MemoryVisualizer.spriteMemIndicatorData.sprite, MemoryVisualizer.spriteMemIndicatorData)
	local spritePct = math.floor(spriteRatio * 100)
	MemoryVisualizer.spriteMemBarData.info[2] = spritePct .. "%"
end

function update()
	updateMemory()

	drawSprite2D(MemoryVisualizer.backgroundSprite)

	for i = 1, #MemoryVisualizer.pageSprites do
		drawSprite2D(MemoryVisualizer.pageSprites[i].sprite)
	end

	drawSprite2D(MemoryVisualizer.spriteMemBarData.sprite)
	drawSprite2D(MemoryVisualizer.spriteMemIndicatorData.sprite)

	for i = 1, #MemoryVisualizer.cellSprites do
		drawSprite2D(MemoryVisualizer.cellSprites[i].sprite)
		drawSprite2D(MemoryVisualizer.diskOccupancySprites[i].sprite)
	end

	updateStatusBar()

	drawSprite2D(MemoryVisualizer.statusLine1)
	drawSprite2D(MemoryVisualizer.statusLine2)
end
