local function Profile(pn)
	if PROFILEMAN:IsPersistentProfile(pn) then
		return PROFILEMAN:GetProfile(pn);
	else
		return PROFILEMAN:GetMachineProfile();
	end;
end;

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)
			local song = GAMESTATE:GetCurrentSong();
			local course = GAMESTATE:GetCurrentCourse();
			
			local scorelist;
			local text = "";
			local profile;
			if song then
				local steps = GAMESTATE:GetCurrentSteps(pn);
				
				if steps then
					profile = Profile(pn);
					scorelist = profile:GetHighScoreList(song,steps);
					assert(scorelist);
					local scores = scorelist:GetHighScores();
					
					local topscore = scores[1];
					if not topscore then
						text = string.format("%.2f%%", 0);
					else
						text = string.format("%.2f%%", topscore:GetPercentDP()*100.0);
					end;
				else
					return;
				end;
			elseif course then
				local trail = GAMESTATE:GetCurrentTrail(pn);
				if trail then
					profile = Profile(pn);
					scorelist = profile:GetHighScoreList(course,trail);
					assert(scorelist);
					
					local scores = scorelist:GetHighScores();
					local topscore = scores[1];
					if not topscore then
						text = string.format("%.2f%%", 0);
					else
						text = string.format("%.2f%%", topscore:GetPercentDP()*100.0);
					end;
				else
					return;
				end;
			else
				text = string.format("%.2f%%", 0);
			end;
			self:settext(text);
		end;
		CurrentSongChangedMessageCommand=cmd(playcommand,"Set");
		CurrentCourseChangedMessageCommand=cmd(playcommand,"Set");
		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;

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)
			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;

local t = Def.ActorFrame{
	-- banner section
	Def.ActorFrame{
		Name="BannerSection";
		InitCommand=cmd(x,SCREEN_LEFT+164;y,SCREEN_CENTER_Y-102);
		
		Def.Banner {
			Name="Banner";
			OffCommand=cmd(linear,0.2;diffusealpha,0);
			SetCommand=function(self)
				local song = GAMESTATE:GetCurrentSong();
				if not song then self:Load( THEME:GetPathG("common fallback","banner") );
				else self:LoadFromSong(song);
				end;
				self:ScaleToClipped(256,80);
			end;
			InitCommand=cmd(playcommand,"Set");
			CurrentSongChangedMessageCommand=cmd(playcommand,"Set");
		};
		
		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);
		};
	};
	
	-- song length
	Def.ActorFrame{
		Name="LengthSection";
		InitCommand=cmd(x,SCREEN_LEFT+48;y,SCREEN_CENTER_Y-156;horizalign,right;zoom,0.55);
		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;
		};
	};
	
	-- bpm display
	Def.ActorFrame{
		Name="BPMSection";
		InitCommand=cmd(x,SCREEN_LEFT+164;y,SCREEN_CENTER_Y-156;horizalign,left;zoom,0.55);
		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);
		};
	};
	
	-- 
	Def.ActorFrame{
		Name="Difficulties";
		InitCommand=cmd(x,SCREEN_LEFT+164;y,SCREEN_CENTER_Y+132);
		
		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(x,SCREEN_LEFT+164;y,SCREEN_CENTER_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");
	};
};

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