dofile("lua/utils.lua")
dofile(ProcessSkinFilePath("$/menus.lua"))
LoadGU()

-- Load the font.
Font = GetDefaultBitmapFont()
BitmapFont_EnableShadow(Font, true)
BitmapFont_EnableLineWrap(Font, false)
LineHeight = BitmapFont_GetLineHeight(Font)

ShowLoadingScreen(Font, 6)
LoadStartupSound()

-- Load wallpaper.
LoadingStep("Loading wallpaper")
BGTexture, e = LoadImage(WallpaperPath, IMG_PAD_NONE)
if not BGTexture then 
	ShowError(e)
	return
end

-- If we can't fit it in VRAM, still optimize it
LoadingStep("Moving to VRAM")
if not Image_SwizzleToVRAM(BGTexture) then Image_Swizzle(BGTexture) end

BG = AllocVertices(4)
bg1 = {u=0.0, v=0.0, r=255, g=255, b=255, a=255, x=  0, y=  0, z=0} -- top left
bg2 = {u=0.0, v=272, r=255, g=255, b=255, a=255, x=  0, y=272, z=0} -- bottom left
bg3 = {u=480, v=0.0, r=255, g=255, b=255, a=255, x=480, y=  0, z=0} -- top right
bg4 = {u=480, v=272, r=255, g=255, b=255, a=255, x=480, y=272, z=0} -- bottom right
SetVertices(BG, 0, bg1, bg2, bg3, bg4)


-- Read notes file.
LoadingStep("Reading notes file")
Notes = {}
for line in io.lines("notes.txt") do table.insert(Notes, line) end


-- Get installed apps
LoadingStep("Reading app list")
_Apps = GetInstalledPrograms()
NumApps = TableSize(_Apps)
if not _Apps then
	ShowError("Error getting program list!")
	return
elseif NumApps == 0 then
	_Apps = {["No programs found!"] = ""}
end
Apps = NestTable(_Apps, "dir", "name")
SortBySubTable(Apps, "name")

-- Get built-in apps
_BuiltinApps = GetBuiltinApps()
NumBuiltinApps = TableSize(_BuiltinApps)
if not _BuiltinApps then
	ShowError("Error getting built-in program list!")
	return
elseif NumBuiltinApps == 0 then
	_BuiltinApps = {["No programs found!"] = ""}
end
BuiltinApps = NestTable(_BuiltinApps, "id", "name")
SortBySubTable(BuiltinApps, "name")


-- Set up the menus.
LoadingStep("Initializing menus")
AppBase = "ms0:/PSP/GAME/"
AppExt = "/EBOOT.PBP"
CurDir = "ms0:/"
MenuID = 1
MenuTitle = {"Select Program", "Select Program", "Select File", "Notes"}
MenuX = {160, 160, 160, 8} -- You might want to move the file menu over if you have long file names; 8 is good
MenuY = {16, 16, 16, 16}
MenuTable = {Apps, BuiltinApps, Files, Notes}
MenuNumOpts = {TableSize(Apps), TableSize(BuiltinApps), 0, 0}
MenuDispKey = {"name", "name", nil, nil}
MenuAcceptFunc = {RunThisApp, RunThisBuiltinApp, OpenThisFile, DoNothing} -- run when Accept pressed
MenuCancelFunc = {DoNothing, DoNothing, PrevDir, DoNothing} -- when Cancel pressed
MenuTriangleFunc = {DoNothing, DoNothing, PrevDir, DoNothing}
MenuSquareFunc = {DoNothing, DoNothing, DoNothing, DoNothing}
NumMenus = #MenuNumOpts -- max valid menu ID
MenuYMax = 260
Cursor = 0 -- Cursor index
IgnoreCursor = false -- Used to make the cursor move only once each button press
IgnoreButtons = false -- And the accept/cancel/etc buttons
Skip = 0 -- Number of menu items to leave off the top of the menu.
NumLines = 22 -- Number of menu items to display at once.
SelectedItem = 1
FileMenuID = 3 -- Which menu ID is the Select File menu

if DebugMode then MenuCancelFunc[1] = ExitProgram end

if Buttons.up or Buttons.left or Buttons.down or Buttons.right then
	IgnoreCursor = true
end

Days = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}

-- Load sound effects.
LoadingStep("Loading sounds")
if EnableSound then
	CursorSound, e = LoadSoundEffect("$/cursor.wav");
	if not CursorSound then
		ShowError(e)
		return
	end

	ActivateSound, e = LoadSoundEffect("$/select.wav");
	if not ActivateSound then
		ShowError(e)
		return
	end

	SwitchSound, e = LoadSoundEffect("$/switch.wav");
	if not SwitchSound then
		ShowError(e)
		return
	end

	BackSound, e = LoadSoundEffect("$/back.wav");
	if not BackSound then
		ShowError(e)
		return
	end

	PlayStartupSound()
end

function FrameBegin()
	Buttons = GetButtons()
	
	-- Handle menu controls
	if not IgnoreCursor then
		local Moved = true
		if Buttons.up then
			Cursor = Cursor - 1
			SoundEffect_Replay(CursorSound)
		elseif Buttons.down then
			Cursor = Cursor + 1
			SoundEffect_Replay(CursorSound)
		elseif Buttons.left then
			Cursor = Cursor - 10
			SoundEffect_Replay(CursorSound)
		elseif Buttons.right then
			Cursor = Cursor + 10
			SoundEffect_Replay(CursorSound)
		elseif Buttons.l then
			Cursor = 0
			MenuID = MenuID - 1
			SoundEffect_Replay(SwitchSound)
		elseif Buttons.r then
			Cursor = 0
			MenuID = MenuID + 1
			SoundEffect_Replay(SwitchSound)
		elseif Buttons.accept then MenuAcceptFunc[MenuID]()
		elseif Buttons.cancel then MenuCancelFunc[MenuID]()
		elseif Buttons.triangle then MenuTriangleFunc[MenuID]()
		elseif Buttons.square then MenuSquareFunc[MenuID]()
		else Moved = false end
		
		if Moved then
			IgnoreCursor = true -- Don't move again until button is released
			-- todo: play sound
			
			-- Ensure we're in a menu that exists
			if MenuID > NumMenus then MenuID = 1
			elseif MenuID < 1 then MenuID = NumMenus
			end
			
			-- If we just switched to the file selector for the first time,
			-- read the file list.
			if MenuID == FileMenuID and not Files then ReadCurDir() end
			
			-- Ensure the cursor is visible and within the menu
			if Cursor < 0 then Cursor = MenuNumOpts[MenuID] - 1
			elseif Cursor > MenuNumOpts[MenuID] - 1 then Cursor = 0
			end
				
			if Cursor < Skip then Skip = Cursor
			elseif Cursor >= (Skip + NumLines)
			then Skip = (Cursor - NumLines) + 1
			end
		end
	else
		-- If all above buttons are released then the cursor can respond again
		-- "if ... then else" is shorter than writing "and not" several times
		-- when checking that several variables are false
		if Buttons.up or Buttons.down or Buttons.left or Buttons.right
		or Buttons.l or Buttons.r or Buttons.accept or Buttons.cancel
		or Buttons.triangle or Buttons.square then else
			IgnoreCursor = false
		end
	end
	
	-- Set up frame
	sceGuClearDepth(0)
	sceGuClear(GU_DEPTH_BUFFER_BIT)
	ResetMatrices()
	
	-- Draw background
	sceGuDisable(GU_DEPTH_TEST)
	UseTexture(BGTexture)
	RenderVertices(BG, 0, 4, GU_TRIANGLE_STRIP, GU_TRANSFORM_2D)
end

function FrameEnd()
	local Time = GetLocalTime()
	local Battery = GetBattery()
	local Speed = GetCPUSpeed()
	FrameBuf = GetFrameBuf()
	
	-- Draw menu
	DrawMenu(MenuX[MenuID], MenuY[MenuID], MenuTable[MenuID], MenuDispKey[MenuID])
	BitmapFont_DrawString(FrameBuf, Font, 160, 2, FONT_DRAW_NORMAL, nil,
		MenuTitle[MenuID])
	
	-- Draw status
	BitmapFont_DrawString(FrameBuf, Font, 410, 1, FONT_DRAW_NORMAL, nil,
		string.format("%3u%% %u:%02u", Battery.percent, Battery.time / 60,
			Battery.time % 60),
		string.format("%2dC %1.2fV", Battery.temperature, Battery.voltage))
	
	BitmapFont_DrawString(FrameBuf, Font, 0, 262, FONT_DRAW_NORMAL, nil,
		string.format("%s %d/%d/%02d %02d:%02d:%02d", Days[DayOfWeek(Time)],
			Time.year, Time.month, Time.day, Time.hour, Time.minute,
			Time.second))
			
	BitmapFont_DrawString(FrameBuf, Font, 395, 262, FONT_DRAW_NORMAL, nil,
		string.format("%3.0f/%3.0fmhz", Speed.cpu, Speed.bus))
end