-- Draws a menu.
-- Inputs:
-- x, y: Position of top left
-- Items: Table of items
-- Title: Menu title
-- Key: If values of Items are tables, specifies the key of those tables to
--  print.
-- Notes: Sets global SelectedItem to the key of the selected item.
function DrawMenu(x, y, Items, key)
	local i, line = 0, 0
	for a, d in pairs(Items) do
		if i >= Skip and i < (Skip + NumLines) then
			BitmapFont_DrawString(FrameBuf, Font, x, y, FONT_DRAW_NORMAL,
				nil, key and d[key] or d)
			if i == Cursor then
				SelectedItem = a
				BitmapFont_DrawString(FrameBuf, Font, x - 8, y,
					FONT_DRAW_NORMAL, nil, ">")
			end
			y = y + LineHeight
			if y >= MenuYMax then break end
			
			line = line + 1
			if line >= NumLines then break end
		end
		i = i + 1
	end
end


-- Draws builtin apps menu.
-- Notes: Sets global SelectedItem to the key of the selected item.
function DrawBuiltinAppsMenu()
	local i = 0
	local ix, iy = 0, 0
	local p = math.pi / 180
	local r = (360 / NumBuiltinApps) * -p
	local dst = 0.5
		
	for a, d in pairs(BuiltinApps) do
		ResetMatrices()
		UseTexture(BuiltinAppIcons[d])
		local spin = (-90 - r)
		
		-- Comment these lines to disable spinning the rings
		if Use3DIconRing then spin = spin + (Cursor * -r) - IconRingRotation
		elseif UseIconRing then spin = spin - (Cursor * -r) + IconRingRotation
		end
		
		-- Move to the position the icon will be drawn
		if UseIconRing then
			if Use3DIconRing then
				dst = 0.8
				Translate(dst * math.cos((i * r) + spin),
					((dst / 1.7) * math.sin((i * r) + spin) + 0.2),
					-2 - math.sin((i * r) + spin))
			else
				Translate(dst * math.cos((i * r) - spin),
					dst * math.sin((i * r) - spin),
					-1)
				
				-- This will cause all icons to wave back and forth
				--RotateDegrees(math.sin(IconRotation) * 20,
				--	math.sin(IconRotation) * 20, 0)
			end
		else
			-- This will make the whole grid appear to be laying on a plane,
			-- which might be a floor or something in the background image.
			-- RotateDegrees(-10, 0, 0)
			
			-- Tweak the divisors to adjust the gap between icons, and the offsets
			-- to adjust the position of the grid.
			Translate((ix / 4) - 0.8, (-iy / 4) + 0.4, -1)
		end
		
		if i == Cursor then
			SelectedItem = a
			if not UseIconRing then -- animate selected icon
				RotateDegrees(0, 0, math.sin(-IconRotation) * 30)
			end
		end
		
		-- Draw the icon
		RenderVertices(Icons, 0, 4, GU_TRIANGLE_STRIP, GU_TRANSFORM_3D)
		
		i = i + 1
		ix = ix + 1
		if ix >= IconGridNumCols then
			ix = 0
			iy = iy + 1
		end
	end
end


-- Checks and resets the IgnoreCursor flag.
function CheckIgnoreCursor()
	if IgnoreCursor then
		-- 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
		return true
	end
	return false
end


-- Validates cursor position and menu ID.
function CheckCursorPos()
	-- 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 MenuID == BuiltinAppMenuID and Use3DIconRing then
		if Cursor < 0 then Cursor = NumBuiltinApps + Cursor
		elseif Cursor >= NumBuiltinApps then
			Cursor = Cursor - NumBuiltinApps
		end
	else
		if Cursor < 0 then Cursor = MenuNumOpts[MenuID] - 1
		elseif Cursor >= MenuNumOpts[MenuID] then Cursor = 0
		end
	end
		
	if MenuID == BuiltinAppMenuID then return end
	
	if Cursor < Skip then Skip = Cursor
	elseif Cursor >= (Skip + NumLines)
	then Skip = (Cursor - NumLines) + 1
	end
end


-- Handles menu controls.
function DoMenuControls()
	if CheckIgnoreCursor() then return end
	
	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 end -- Don't move again until button is released
	CheckCursorPos()
end


-- Handles menu controls in built-in app list.
function BuiltinAppMenuControls()
	if CheckIgnoreCursor() then return end
	
	local p = (math.pi / 180)
	local Moved = true
	if Buttons.up then
		Cursor = Cursor - IconGridNumCols
		SoundEffect_Replay(CursorSound)
		IconRingRotation = (360 / NumBuiltinApps) * -p * IconGridNumCols
	elseif Buttons.down then
		Cursor = Cursor + IconGridNumCols
		SoundEffect_Replay(CursorSound)
		IconRingRotation = (360 / NumBuiltinApps) * p * IconGridNumCols
	elseif Buttons.left then
		Cursor = Cursor - 1
		SoundEffect_Replay(CursorSound)
		IconRingRotation = (360 / NumBuiltinApps) * -p
	elseif Buttons.right then
		Cursor = Cursor + 1
		SoundEffect_Replay(CursorSound)
		IconRingRotation = (360 / NumBuiltinApps) * p
	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
		IconRotation = 0
	end -- Don't move again until button is released
	CheckCursorPos()
end


-- Read current directory.
function ReadCurDir()
	Files, e = ReadDirectory(CurDir, true)
	if not Files then Files = {"Error reading directory:", e} end
	table.sort(Files)
	MenuNumOpts[FileMenuID] = TableSize(Files)
	MenuTable[FileMenuID] = Files
end


-- Change to parent directory in file select.
function PrevDir()
	local Idx = FindLast(CurDir, "/")
	if Idx == #CurDir then
		Idx = FindLast(string.sub(CurDir, 1, #CurDir - 1), "/")
	end
	
	if Idx then
		SoundEffect_Replay(BackSound)
		CurDir = string.sub(CurDir, 1, Idx - 1)
		print(CurDir)
		if(string.sub(CurDir, -1) == ":") then CurDir = CurDir .. "/" end
		ReadCurDir()
	end
end


-- Open selected file in file select.
function OpenThisFile()
	if not Files[SelectedItem] then return end
	SoundEffect_Replay(ActivateSound)
	
	-- The kernel will puke with error 80010016 if there are any "//" in
	-- the path of a program we try to execute.
	File = string.gsub(CurDir .. Files[SelectedItem], "//+", "/")
	print("File: " .. File)
	
	-- If this is a directory, switch to it.
	if(string.sub(Files[SelectedItem], 1, 1) == '/') then
		CurDir = CurDir .. Files[SelectedItem] .. "/"
		Cursor = 0
		SelectedItem = 1
		ReadCurDir()
		return
	end
	
	
	Ext = string.lower(string.sub(File, -4))
	if Ext == ".pbp" or Ext == ".prx" then -- Program; launch it
		e = LaunchProgram(File)
		if not e == true then
			ShowError("Error " .. string.format("%X", e) .. " launching program")
		end
	
	elseif Ext == ".mp3" then -- Music; play it
		if Music then MP3_Stop(Music) end
		Music = nil -- delete so the MP3 player is shut down properly
		Music, e = LoadMP3File(File)
		if Music then
			MP3_Loop(Music, -1) --loop forever
			MP3_Play(Music)
		else ShowError(e)
		end
	end
end


-- Run selected program in app select.
function RunThisApp()
	SoundEffect_Replay(ActivateSound)
	e = LaunchProgram(AppBase .. Apps[Cursor + 1].dir .. AppExt)
	if not e == true then
		ShowError("Error " .. string.format("%X", e) ..
			" launching program " .. tostring(Apps[Cursor + 1].dir))
	end
end


-- Run selected program in built-in app select.
function RunThisBuiltinApp()
	SoundEffect_Replay(ActivateSound)
	Result, e = RunBuiltinApp(BuiltinApps[Cursor + 1].id)
	if not Result == true then
		ShowError(e)
	end
end
