-- 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.
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:
					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
				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;

-- 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.

-- [v1.4] SVN todo: make it update when mashing in mod codes

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);
			local trail = GAMESTATE:GetCurrentTrail(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;
			--[[
			elseif trail then
				local difficulty = trail:GetDifficulty();
				local diffstring = ( string.sub(difficulty,12) );
				self:diffuse( DifficultyColor(trail:GetDifficulty()) );
				self:settext(diffstring .. " [" ..trail:GetMeter().."]");
			]]
			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;

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 (this belongs in the underlay but it's easier to work with here
	--        since so many objects are placed in it.)
	
	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.
				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.
				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.
				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;