-- First time remaking this from scratch. Wish me luck. -AJ

-- Profile(pn)
-- This returns a profile, preferably a player one.
-- If there isn't one, we fall back on the machine profile.
local function Profile(pn)
	if PROFILEMAN:IsPersistentProfile(pn) then
		-- player profile
		return PROFILEMAN:GetProfile(pn);
	else
		-- machine profile
		return PROFILEMAN:GetMachineProfile();
	end;
end;

-- PlayerScore(pn)
-- This function gets the top score for a player.
-- This is one beast of a function, so I'm going to go through it to help
-- newer themers wrap their head around it.
-- WARNING: I quote DVS's Bust Back in here. If you don't know what that is,
-- then I suggest you look it up before you scroll down, as you will assume
-- the worst when you see it.
local function PlayerScore(pn)
	local s = LoadFont("_frutiger lt std 55 roman 24px")..{
		Name="Score"..pn;
		InitCommand=cmd(shadowlength,1;diffuse,PlayerColor(pn);player,pn);
		OnCommand=cmd(linear,0.2;diffusealpha,1;playcommand,"Set");
		OffCommand=cmd(linear,0.2;diffusealpha,0);
		
		SetCommand=function(self)
			-- Grab the song and course. We don't know if this is going to
			-- be called on SSMusic or SSCourse, so it's good to get both.
			-- We'll filter this later.
			local song = GAMESTATE:GetCurrentSong();
			local course = GAMESTATE:GetCurrentCourse();
			
			-- set up some variables that are used in both functions.
			local scorelist;
			local text = "";
			local profile;
			if song then
				-- we have a song, we need some steps
				local steps = GAMESTATE:GetCurrentSteps(pn);
				
				if steps then
					-- get profile
					profile = Profile(pn);
					-- populate high score list
					scorelist = profile:GetHighScoreList(song,steps);
					-- a little bird told me this trick.
					-- I ain't sayin' nothin.
					assert(scorelist);
					-- get all the scores from the score list
					local scores = scorelist:GetHighScores();
					
					-- get the top score
					local topscore = scores[1];
					-- if it doesn't exist, set it to 0. this took me forever
					-- to find a workaround since if there are no high scores
					-- for a song this will BREAK.
					if not topscore then
						text = string.format("%.2f%%", 0);
					else
						-- There IS a high score, format it to percentages
						-- and be on our merry way.
						text = string.format("%.2f%%", topscore:GetPercentDP()*100.0);
					end;
				else
					-- no steps. do nothing.
					return;
				end;
			-- the course code is pretty much the same, aside from the args to
			-- Profile:GetHighScoreList.
			elseif course then
				-- we have a course, get a trail
				local trail = GAMESTATE:GetCurrentTrail(pn);
				if trail then
					-- get profile
					profile = Profile(pn);
					-- populate high score list
					scorelist = profile:GetHighScoreList(course,trail);
					assert(scorelist);
					
					local scores = scorelist:GetHighScores();
					-- get the top score
					local topscore = scores[1];
					if not topscore then
						text = string.format("%.2f%%", 0);
					else
						text = string.format("%.2f%%", topscore:GetPercentDP()*100.0);
					end;
				else
					-- no trail. do nothing.
					return;
				end;
			else
				-- FUCK THAT, A NIGGA WANNA COME IN MY FACE?
				text = string.format("%.2f%%", 0); -- BUST BACK
			end;
			self:settext(text);
		end;
		-- song and course changes
		CurrentSongChangedMessageCommand=cmd(playcommand,"Set");
		CurrentCourseChangedMessageCommand=cmd(playcommand,"Set");
		-- steps and trail changes
		CurrentStepsP1ChangedMessageCommand=function(self)
			if pn == PLAYER_1 then self:playcommand("Set"); end;
		end;
		CurrentStepsP2ChangedMessageCommand=function(self)
			if pn == PLAYER_2 then self:playcommand("Set"); end;
		end;
		
		CurrentTrailP1ChangedMessageCommand=function(self)
			if pn == PLAYER_1 then self:playcommand("Set"); end;
		end;
		CurrentTrailP2ChangedMessageCommand=function(self)
			if pn == PLAYER_2 then self:playcommand("Set"); end;
		end;
	};
	
	return s;
end;
-- p.s. not racist

-- OptionIcons(pn)
-- Generates OptionIcons for a specified player.
-- Using the setup I have now, I can only put 12 icons on screen at once
-- (6 icons/row x 2 rows).
-- A lot of code here is stolen from the implementation of the Mexi Mexi
-- difficulty-based speed modifier, written by myself.
-- Note to themers: code reuse is helpful.

-- todo: make it update when mashing in mod codes... either that or remove 'em.
local function OptionIcons(pn)
	-- make the ActorFrame to store the icons.
	local u = Def.ActorFrame{
		Name="OptionIconRow"..pn;
	};
	
	-- throw it in here because I thought I could make it update easier.
	local function UpdateIcons()
		-- get the options.
		local PlayerState = GAMESTATE:GetPlayerState(pn);
		-- grab the song options from this PlayerState.
		local options = PlayerState:GetPlayerOptions('ModsLevel_Preferred');
		-- now using split, let's put them into a table for comparison
		local listOfOptions = split(", ", options);
		-- find out how many options are in play.
		local numOptions = #listOfOptions;
		
		-- perform some checks here.
		if #listOfOptions == 0 then
			-- no options, just draw empty ones.
			for i=0,6 do
				u[#u+1] = Def.ActorFrame{
					Name="EmptyIcon"..i;
					InitCommand=cmd(x,(i*47)-1;y,0;diffusealpha,0.8);
					
					LoadActor("_optionicon");
				};
			end;
			return;
		elseif #listOfOptions > 6 then
			-- we need two rows.
			for i=1,#listOfOptions do
				-- set text
				local text = listOfOptions[i];
				
				-- find out which row we're in and set y accordingly.
				local yPos = 0;
				-- shift up row #1.
				if i < 7 then
					yPos = -16;
				end;
				
				local xPos;
				if i > 6 then xPos = i-6;
				else xPos = i;
				end;
				
				-- make an icon for each option.
				u[#u+1] = Def.ActorFrame{
					Name="Icon"..i;
					InitCommand=cmd(x,(xPos*47)-1;y,yPos);
					
					LoadActor("_optionicon")..{
						InitCommand=cmd(diffuse,PlayerColor(pn);diffusealpha,0.8);
					};
					LoadFont("_frutiger lt std 55 roman 24px")..{
						Text=text;
						InitCommand=cmd(zoom,0.5;diffuse,color("#FFFFFF");shadowlength,1;maxwidth,90);
					};
				};
			end;
			-- draw emptys
			for i=#listOfOptions+1,12 do
				-- find out which row we're in and set y accordingly.
				local yPos = 0;
				-- shift up row #1.
				if i < 7 then
					yPos = -16;
				end;
				
				local xPos;
				if i > 6 then xPos = i-6;
				else xPos = i;
				end;
				
				u[#u+1] = Def.ActorFrame{
					Name="EmptyIcon"..i;
					InitCommand=cmd(x,(xPos*47)-1;y,yPos;diffusealpha,0.8);
					
					LoadActor("_optionicon");
				};
			end;
		else
			-- the fallback, one row.
			for i=1,#listOfOptions do
				-- set text
				local text = listOfOptions[i];
				
				-- make an icon for each option.
				u[#u+1] = Def.ActorFrame{
					Name="Icon"..i;
					InitCommand=cmd(x,(i*47)-1;y,0;diffusealpha,0.8);
					
					LoadActor("_optionicon")..{
						InitCommand=cmd(diffuse,PlayerColor(pn));
					};
					LoadFont("_frutiger lt std 55 roman 24px")..{
						Text=text;
						InitCommand=cmd(zoom,0.5;diffuse,color("#FFFFFF");shadowlength,1;maxwidth,90);
					};
				};
			end;
			-- draw emptys
			for i=#listOfOptions+1,6 do
				u[#u+1] = Def.ActorFrame{
					Name="EmptyIcon"..i;
					InitCommand=cmd(x,(i*47)-1;y,0;diffusealpha,0.8);
					
					LoadActor("_optionicon");
				};
			end;
		end;
	end;
	
	u.InitCommand=UpdateIcons();
	u.PlayerOptionsChangedP1MessageCommand=function(self)
		if pn == PLAYER_1 then
			Trace("AJ DEBUG [OptionIcons]: Updating P1 Options...");
			UpdateIcons();
			Trace("AJ DEBUG [OptionIcons]: Supposedly updated P1 Options.");
		end;
	end;
	u.PlayerOptionsChangedP2MessageCommand=function(self)
		if pn == PLAYER_2 then
			Trace("AJ DEBUG [OptionIcons]: Updating P2 Options...");
			UpdateIcons();
			Trace("AJ DEBUG [OptionIcons]: Supposedly updated P2 Options.");
		end;
	end;
	
	return u;
end;

-- Difficulty(pn)
-- Makes difficulty for specified player.
local function Difficulty(pn)
	local diff = LoadFont("_frutiger lt std 55 roman 24px")..{
		Name="Difficulty"..pn;
		InitCommand=cmd(zoom,0.6;shadowlength,1);
		BeginCommand=function(self)
			if GAMESTATE:IsSideJoined(pn) or GAMESTATE:IsPlayerEnabled(pn) then
				self:visible(true)
			else self:visible(false);
			end;
		end;
		OnCommand=cmd(playcommand,"Set");
		
		SetCommand=function(self)
			-- get difficulty
			local song = GAMESTATE:GetCurrentSong();
			local course = GAMESTATE:GetCurrentCourse();
			if not song and not course then self:visible(false);
			else self:visible(true);
			end;
			
			local steps = GAMESTATE:GetCurrentSteps(pn);
			if steps then
				local difficulty = steps:GetDifficulty();
				local diffstring = ( string.sub(difficulty,12) );
				
				self:diffuse( DifficultyColor(steps:GetDifficulty()) );
				
				if difficulty == 'Difficulty_Edit' then
					diffstring = steps:GetDescription();
					self:settext(diffstring .. " [" ..steps:GetMeter().."]");
				else
					self:settext(diffstring .. " [" ..steps:GetMeter().."]");
				end;
			else
				self:settext("");
			end;
		end;
		OffCommand=cmd(linear,0.2;diffusealpha,0);
		CurrentSongChangedMessageCommand=cmd(playcommand,"Set");
		
		CurrentStepsP1ChangedMessageCommand=function(self)
			if pn == PLAYER_1 then self:playcommand("Set"); end;
		end;
		CurrentTrailP1ChangedMessageCommand=function(self)
			if pn == PLAYER_1 then self:playcommand("Set"); end;
		end;
		CurrentStepsP2ChangedMessageCommand=function(self)
			if pn == PLAYER_2 then self:playcommand("Set"); end;
		end;
		CurrentTrailP2ChangedMessageCommand=function(self)
			if pn == PLAYER_2 then self:playcommand("Set"); end;
		end;
	};
	return diff;
end;

--[[
todo at this point:
	* ScreenSelectCourse niceties.
]]

local t = Def.ActorFrame{
	-- OptionIcons
	Def.ActorFrame{
		Name="OptionIcons";
		
		Def.ActorFrame{
			Name="NormalIcons";
			BeginCommand=function(self)
				if GetSSMusicOption() == "Normal" then self:visible(true);
				else self:visible(false);
				end;
			end;
			
			Def.OptionIconRow {
				Name="OptionIconRowP1";
				Condition=GAMESTATE:IsHumanPlayer(PLAYER_1);
				InitCommand=cmd(x,SCREEN_LEFT-20;y,SCREEN_TOP+64;set,PLAYER_1);
				OnCommand=cmd(diffusealpha,0;linear,0.2;diffusealpha,1);
				OffCommand=cmd(linear,0.2;diffusealpha,0);
				PlayerOptionsChangedP1MessageCommand=cmd(set,PLAYER_1);
			};

			Def.OptionIconRow {
				Name="OptionIconRowP2";
				Condition=GAMESTATE:IsHumanPlayer(PLAYER_2);
				InitCommand=cmd(x,SCREEN_LEFT-20;y,SCREEN_BOTTOM-64;set,PLAYER_2);
				OnCommand=cmd(diffusealpha,0;linear,0.2;diffusealpha,1);
				OffCommand=cmd(linear,0.2;diffusealpha,0);
				PlayerOptionsChangedP2MessageCommand=cmd(set,PLAYER_2);
			};
		};
		
		Def.ActorFrame{
			Name="CustomIcons";
			BeginCommand=function(self)
				if GetSSMusicOption() == "Custom" then self:visible(true);
				else self:visible(false);
				end;
			end;
			
			OptionIcons(PLAYER_1)..{
				Condition=GAMESTATE:IsHumanPlayer(PLAYER_1);
				InitCommand=cmd(y,SCREEN_TOP+64;diffusealpha,0);
				OnCommand=cmd(linear,0.2;diffusealpha,1);
				OffCommand=cmd(linear,0.2;diffusealpha,0);
			};
			
			OptionIcons(PLAYER_2)..{
				Condition=GAMESTATE:IsHumanPlayer(PLAYER_2);
				InitCommand=cmd(y,SCREEN_BOTTOM-64;diffusealpha,0);
				OnCommand=cmd(linear,0.2;diffusealpha,1);
				OffCommand=cmd(linear,0.2;diffusealpha,0);
			};
		};
	};
	
	-- frame (should this go in the underlay? it belongs there but it's easier
	-- to manage in here so I'm putting it in here for now.)
	
	Def.ActorFrame{
		Name="Frame";
		InitCommand=cmd(x,SCREEN_LEFT+164;y,SCREEN_CENTER_Y);
		
		LoadActor("_under")..{
			Name="Under";
			InitCommand=cmd(diffusealpha,0);
			OnCommand=cmd(linear,0.2;diffusealpha,1);
			OffCommand=cmd(linear,0.2;zoomy,0;diffusealpha,1);
		};
		
		-- banner section
		Def.ActorFrame{
			Name="BannerSection";
			InitCommand=cmd(y,-102);
			
			-- sort message
			LoadFont("_frutiger lt std 55 roman 24px")..{
				InitCommand=cmd(x,128;y,-54;horizalign,right;diffuse,color("0,0,0,1");shadowlength,0;zoom,0.5);
				OffCommand=cmd(linear,0.2;diffusealpha,0);
				UpdateCommand=function(self)
					-- change the text based on SortOrder
					self:settext(SortOrderToLocalizedString(GAMESTATE:GetSortOrder()));
				end;
				SortOrderChangedMessageCommand=cmd(playcommand,'Update');
			};
			
			Def.ActorProxy {
				Name="BannerProxy";
				BeginCommand=function(self)
					local banner = SCREENMAN:GetTopScreen():GetChild('Banner');
					self:SetTarget(banner);
				end;
				OffCommand=cmd(linear,0.2;diffusealpha,0);
			};
			
			Def.ActorProxy{
				Name="CDTitleProxy";
				InitCommand=cmd(x,8;y,68);
				BeginCommand=function(self)
					local cdtitle = SCREENMAN:GetTopScreen():GetChild('CDTitle');
					self:SetTarget(cdtitle);
				end;
			};
			
			LoadActor( THEME:GetPathB("","_frame 3x1"), "_bannerframe", 214 ) .. {
				InitCommand=cmd(diffusealpha,0);
				OnCommand=cmd(linear,0.2;diffusealpha,1);
				OffCommand=cmd(linear,0.2;diffusealpha,0);
			};
		};
		
		-- bpm display
		Def.ActorFrame{
			Name="BPMSection";
			InitCommand=cmd(x,-96;y,-48;horizalign,left;zoom,0.75);
			CurrentSongChangedMessageCommand=cmd(playcommand,"Set");
			CurrentCourseChangedMessageCommand=cmd(playcommand,"Set");
			SetCommand=function(self)
				-- check for a song or course. if there isn't one, hide it;
				-- otherwise, display it.
				local song = GAMESTATE:GetCurrentSong();
				local course = GAMESTATE:GetCurrentCourse();
				
				if song or course then self:visible(true)
				else self:visible(false);
				end;
			end;
			
			Def.BPMDisplay{
				File=THEME:GetPathF("_frutiger lt std 55 roman","24px");
				Name="BPMDisplay";
				InitCommand=cmd(x,-4;horizalign,right;shadowlength,0;);
				OnCommand=cmd(stoptweening);
				OffCommand=cmd(linear,0.2;diffusealpha,0);
				SetCommand=function(self) self:SetFromGameState() end;
				CurrentSongChangedMessageCommand=cmd(playcommand,"Set");
				CurrentCourseChangedMessageCommand=cmd(playcommand,"Set");
			};
			LoadFont("_frutiger lt std 55 roman 24px")..{
				Name="BPMLabel";
				InitCommand=cmd(horizalign,left;settext,"BPM";shadowlength,0;diffuse,color("#000000"));
				OffCommand=cmd(linear,0.2;diffusealpha,0);
			};
		};
		
		-- song length
		Def.ActorFrame{
			Name="LengthSection";
			InitCommand=cmd(x,108;y,-48;horizalign,right;zoom,0.75);
			CurrentSongChangedMessageCommand=cmd(playcommand,"Set");
			CurrentCourseChangedMessageCommand=cmd(playcommand,"Set");
			SetCommand=function(self)
				-- check for a song or course. if there isn't one, hide it;
				-- otherwise, display it.
				local song = GAMESTATE:GetCurrentSong();
				local course = GAMESTATE:GetCurrentCourse();
				
				if song or course then self:visible(true)
				else self:visible(false);
				end;
			end;
			
			LoadFont("_frutiger lt std 55 roman 24px") .. {
				Name="SongLength";
				InitCommand=cmd(shadowlength,0;diffusealpha,0;diffuse,color("#000000"));
				OnCommand=cmd(linear,0.2;diffusealpha,1);
				OffCommand=cmd(linear,0.2;diffusealpha,0);
				
				CurrentSongChangedMessageCommand=cmd(playcommand,"Set");
				CurrentCourseChangedMessageCommand=cmd(playcommand,"Set");
				
				SetCommand=function(self)
					local song = GAMESTATE:GetCurrentSong()
					local course = GAMESTATE:GetCurrentCourse()
					if song or course then self:visible(true)
					else self:visible(false); return;
					end;
					
					local time;
					if song then time = song:MusicLengthSeconds() end
					if course then
						local st = GAMESTATE:GetCurrentStyle():GetStepsType();
						time = course:GetTotalSeconds( st );
					end;
					if time then
						local finalText = SecondsToMSSMsMs(time);
						self:settext( string.sub(finalText, 0, string.len(finalText)-3) );
					else
						self:settext( "xx:xx" );
					end
				end;
			};
		};
		
		-- DifficultyList
		LoadActor("difficultylist")..{
			InitCommand=cmd(x,-80);
			BeginCommand=function(self)
				if GetSSMusicInfo() == "DifficultyList" and not GAMESTATE:IsCourseMode() then self:visible(true);
				else self:visible(false);
				end;
			end;
		};
		
		-- RadarBars
		LoadActor("radarbars")..{
			InitCommand=cmd(x,-80);
			BeginCommand=function(self)
				if GetSSMusicInfo() == "RadarBars" and not GAMESTATE:IsCourseMode() then self:visible(true);
				else self:visible(false);
				end;
			end;
		};
		
		-- PaneDisplay
		LoadActor("panedisplay")..{
			InitCommand=cmd(x,-80;y,4);
			BeginCommand=function(self)
				if GetSSMusicInfo() == "PaneDisplay" and not GAMESTATE:IsCourseMode() then self:visible(true);
				else self:visible(false);
				end;
			end;
		};
		
		-- Course Contents
		LoadActor("coursecontents")..{
			InitCommand=cmd(x,-80);
			BeginCommand=function(self)
				if GAMESTATE:IsCourseMode() then self:visible(true);
				else self:visible(false);
				end;
			end;
		};
		
		-- RadarBars & PaneDisplay requires difficulties.
		Def.ActorFrame{
			Name="Difficulties";
			InitCommand=cmd(y,128);
			BeginCommand=function(self)
				if GetSSMusicInfo() == "RadarBars" or GetSSMusicInfo() == "PaneDisplay" then
					self:visible(true);
				else self:visible(false);
				end;
			end;
			
			Difficulty(PLAYER_1)..{
				InitCommand=cmd(x,-128;horizalign,left);
			};
			
			Difficulty(PLAYER_2)..{
				InitCommand=cmd(x,128;horizalign,right);
			};
		};
		
		-- scores
		Def.ActorFrame{
			Name="ScoresSection";
			InitCommand=cmd(y,150;zoom,0.75);
			CurrentSongChangedMessageCommand=cmd(playcommand,"Set");
			CurrentCourseChangedMessageCommand=cmd(playcommand,"Set");
			SetCommand=function(self)
				-- check for a song or course. if there isn't one, hide it;
				-- otherwise, display it.
				local song = GAMESTATE:GetCurrentSong();
				local course = GAMESTATE:GetCurrentCourse();
				
				if song or course then self:visible(true)
				else self:visible(false);
				end;
			end;
			
			PlayerScore(PLAYER_1)..{
				InitCommand=cmd(x,-172;horizalign,left);
			};
			
			PlayerScore(PLAYER_2)..{
				InitCommand=cmd(x,172;horizalign,right);
			};
		};
		
		-- song options
		LoadFont("_frutiger lt std 55 roman 24px") .. {
			Name="SongOptions";
			InitCommand=cmd(y,150;playcommand,"Set";zoom,0.5;shadowlength,1;diffusealpha,0);
			OnCommand=cmd(linear,0.2;diffusealpha,1);
			OffCommand=cmd(linear,0.2;diffusealpha,0);

			SetCommand=function(self)
				local sText = GAMESTATE:GetSongOptionsString()
				sText = string.gsub(sText, ", ", "\n")
				self:settext( sText )
				if GAMESTATE:IsAnExtraStage() then
					self:diffuseshift();
				end
			end;
			SongOptionsChangedMessageCommand=cmd(playcommand,"Set");
		};
	};
	
	-- Fade out here; this and the message for the options should appear
	-- over every single object in this screen. The fade is stolen from
	-- Screen cancel because I like how it worked.
	Def.Quad {
		InitCommand=cmd(diffuse,color("#000000");FullScreen;diffusealpha,0);
		OffCommand=cmd(cropbottom,1;fadebottom,.5;linear,0.3;cropbottom,-0.5;diffusealpha,1);
	};
	
	LoadFont("_frutiger lt std 55 roman 24px")..{
		Text=AprilFools() and "CRUNCHITIZE ME, CAPTAIN" or "Press &START; for options";
		InitCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;vertalign,bottom);
		OnCommand=cmd(visible,false);
		ShowPressStartForOptionsCommand=cmd(visible,true;zoom,1.5;decelerate,1;zoom,1);
		ShowEnteringOptionsCommand=cmd(settext,"Entering Options...";hurrytweening,0.1;sleep,1;accelerate,0.1;zoomy,0);
	};
};

return t;

--[[
Copyright © 2008 AJ Kelly
All rights reserved.

This code is licensed under AJ's Generic Use of Code License.
An up to date copy can be found at the following address:
http://kki.ajworld.net/wiki/AJ%27s_Generic_Use_of_Code_License
However, keep in mind that the code in this file is bound to the exact
specifications found in this file.
That is to say, changes are not retroactive.

When using parts of this code in your theme, you agree to the following points:

1. Not hide/require registration for any theme that uses the code from the
public IF there is a public release. (This means private themes and themes for
commercial games can use the code without worry.)

2. Not compile the code down to bytecode (if the code is Lua) in a public
theme. (Yes, it can be reversed, but regardless, this hinders openness and
sharing.)

3. This code is a unique/nontrivial creation, therefore, credit should be
given to the creator of the code if used in another theme.
]]