local FrameStretchOnCommand = function(self, ...)
	self:stoptweening();
	self:playcommand( "SetSize", { Width=200 } );

	self:RunCommandsRecursively( cmd(sleep,1.5) );
	self:RunCommandsRecursively( cmd(linear,0.1) );
	self:playcommand( "SetSize", { Width=SCREEN_WIDTH, tween = cmd(linear,0.1); } );
end

local function LoadLifeMeterFramePart(f)
	return LoadActor( THEME:GetPathG("_frame", "1D"),
		{ 34/168, 4/168, 92/168, 4/168, 34/168 },
		LoadActor( f )
	) .. {
		InitCommand = cmd(x,SCREEN_CENTER_X;y,SCREEN_TOP+32);
		OnCommand = FrameStretchOnCommand;
	};
end

local function SingleLifeMeter()
	local style = GAMESTATE:GetCurrentStyle();
	return style and style:GetStyleType() == "StyleType_OnePlayerTwoSides";
end

local function VersusMeter()
	local c;
	local PlayerStats;

	local function Shown()
		if not GAMESTATE:IsHumanPlayer(PLAYER_1) or not GAMESTATE:IsHumanPlayer(PLAYER_2) then
			return false;
		end

		if PlayerStats[1]:GetPossibleDancePoints() ~= PlayerStats[2]:GetPossibleDancePoints() then
			return false;
		end

		if GAMESTATE:IsCourseMode() then
			if GAMESTATE:GetCurrentTrail(PLAYER_1) ~= GAMESTATE:GetCurrentTrail(PLAYER_2) then
				return false;
			end
		else
			if GAMESTATE:GetCurrentSteps(PLAYER_1) ~= GAMESTATE:GetCurrentSteps(PLAYER_1) then
				return false;
			end
		end
		return true;
	end

	local Meter = Def.ActorFrame {
		Name = "VersusHighlightFrame";
		LoadActor( "versus highlight" ) ..  {
			Name = "VersusHighlight";
			OnCommand=cmd(diffusealpha,0;sleep,1.9;smooth,0.3;diffusealpha,1);
		};

		-- Delay processing JudgmentMessageCommand a bit, so we don't
		-- jitter a lot when two inputs are only slightly apart.
		Def.Actor {
			JudgmentMessageCommand=function(self, param)
				self:stoptweening();
				self:sleep(.080);
				self:queuecommand("SendUpdate");
			end;
			SendUpdateCommand=function(self, param)
				self:GetParent():playcommand("Update");
			end;
		};

		InitCommand = function(self)
			c = self:GetChildren();
			(cmd(x,SCREEN_CENTER_X;y,SCREEN_TOP+32-24))(self);
		end;

		BeginCommand=function(self, param)
			local Stats = STATSMAN:GetCurStageStats();
			PlayerStats = { Stats:GetPlayerStageStats(PLAYER_1), Stats:GetPlayerStageStats(PLAYER_2) };

			if not Shown() then
				self:visible(false);
				return;
			end

			self:visible(true);
		end;

		UpdateCommand=function(self, param)
			local Actual = { PlayerStats[1]:GetActualDancePoints(), PlayerStats[2]:GetActualDancePoints() };
			local iDiffPoints = Actual[2] - Actual[1];

			local bNegative = (iDiffPoints < 0);
			iDiffPoints = math.abs(iDiffPoints);

			--
			-- Scale the motion.  A large portion of the inside of the meter is expanded
			-- significantly, so individual points can be distinguished; if one player
			-- gets a W1 and the other gets a W2, the change in points should be visible.
			-- If one player is winning by one point, it should be clearly visible.
			--
			-- Outside that area, motion slows down.
			--
			-- Scale motion based on the actual points, and not based on possible
			-- points, so motion is constant based on the judgments received, and not
			-- scaled down and harder to read for harder songs.
			--
			
			local iPixelsPerPoint = 8; -- the width of the bright part of the highlight
			local iMaxWidthPixels = (SCREEN_WIDTH-35) / 2; -- in one direction
			local iCloseCallPixels = iMaxWidthPixels * 0.25;
			local iCloseCallPoints = iCloseCallPixels / iPixelsPerPoint;

			local iX;
			if iDiffPoints < iCloseCallPoints then
				-- Near the center, expand the distance per point, so individual
				-- points can be distinguished; if one player gets a W1 and the
				-- other player gets a W2, the change in points should be visible.
				iX = scale( iDiffPoints, 0, iCloseCallPoints, 0, iCloseCallPixels );
			else
				local iNonCloseCallPoints = 100;

				iDiffPoints = iDiffPoints - iCloseCallPoints;
				iDiffPoints = iDiffPoints / iNonCloseCallPoints;
				iDiffPoints = clamp( iDiffPoints, 0, 1 );

				-- Decelerate as we approach the edge.
				iDiffPoints = 1-((1-iDiffPoints)*(1-iDiffPoints));
				iX = scale( iDiffPoints, 0, 1, iCloseCallPixels, iMaxWidthPixels );
			end

			if bNegative then iX = -iX; end

			c.VersusHighlight:stoptweening();
			local fOldX = c.VersusHighlight:GetX();
			
			local fDistX = math.abs(iX - fOldX);
			c.VersusHighlight:linear(0.05);
			c.VersusHighlight:x( iX );
		end;
	};
	return Meter;
end

local TopFrame = Def.ActorFrame {
	OnCommand = function(self, ...)
		self:addy(-64);
		self:sleep(1.0);
		self:linear(0.3);
		self:addy(64);
	end;

	-- progress bar off mask
--[[ 	Def.Quad {
		InitCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_TOP+8;setsize,SCREEN_WIDTH,32;blend,"BlendMode_NoEffect";zwrite,true;zbias,1);
	};

	-- progress bar on mask
	Def.SongMeterDisplay {
		InitCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_TOP+8);
		StreamWidth=SCREEN_WIDTH-30;
		Stream = Def.Quad 
		{
			InitCommand=cmd(setsize,128,8;blend,"BlendMode_NoEffect";zwrite,true;zbias,-1);
		};
	};

	LoadActor( THEME:GetPathG("_frame", "1D"),
		{ 34/168, 4/168, 46/168, 0, 46/168, 4/168, 34/168 },
		LoadActor("progress bar")
	) .. {
		InitCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_TOP+8;ztest,true);
		OnCommand = function(self, ...)
			self:playcommand( "SetSize", { Width=SCREEN_WIDTH } );
		end
	};

	Def.SongMeterDisplay {
		InitCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_TOP+8);
		StreamWidth=SCREEN_WIDTH-30;
		Stream = Def.Actor { };
		Tip = LoadActor( "progress tip" ) .. {
			InitCommand=cmd();
			OnCommand=cmd(diffusealpha,0;sleep,1.9;diffusealpha,1);
		};
	};

	LoadActor( THEME:GetPathG("_frame", "1D"),
		{ 34/168, 4/168, 46/168, 0, 46/168, 4/168, 34/168 },
		LoadActor("progress bevel")
	) .. {
		InitCommand = cmd(x,SCREEN_CENTER_X;y,SCREEN_TOP+32);
		OnCommand = function(self, ...)
			self:stoptweening();
			self:playcommand( "SetSize", { Width=0 } );
			self:y(SCREEN_TOP+32-24);

			self:RunCommandsRecursively( cmd(sleep,1.9;diffusealpha,0) );
			self:playcommand( "SetSize", { Width=SCREEN_WIDTH, tween = cmd(smooth,0.3); } );
			self:RunCommandsRecursively( cmd(diffusealpha,1) );
		end
	};

	--LoadLifeMeterFramePart("top shadow");

	VersusMeter();

	Def.Quad { InitCommand=cmd(diffusealpha,0;clearzbuffer,true); }; --]]
};

local function LifeMeterWidth()
	return SingleLifeMeter() and ((SCREEN_WIDTH)-32) or ((SCREEN_WIDTH/2)-16)
end

local X = {
	[PLAYER_1] = SCREEN_LEFT+16,
	[PLAYER_2] = SCREEN_RIGHT-16,
};

local function BothSides()
	return (GAMESTATE:GetCurrentStyle() and GAMESTATE:GetCurrentStyle():GetStyleType() == "StyleType_OnePlayerTwoSides");
end

TopFrame[#TopFrame+1] = LoadActor( THEME:GetPathS(Var "LoadingScreen", "player failed") ) .. {
	SupportPan = true;

	PlayerFailedMessageCommand=function(self, params)
		self:playforplayer(params.PlayerNumber);
	end;
};

for pn in ivalues(PlayerNumber) do
	TopFrame[#TopFrame+1] = Def.ActorFrame {
		InitCommand=cmd(x,X[pn]);
		LoadActor("meter under") .. {
			Name = "Under"; 
			InitCommand=cmd(
				y,SCREEN_TOP+30;
				zoomx,pn == PLAYER_1 and 1 or -1
			);
			BeginCommand=cmd(
				zoomtowidth,LifeMeterWidth();
				x,LifeMeterWidth()/2 * (pn == PLAYER_1 and 1 or -1);
				visible,GAMESTATE:IsHumanPlayer(pn)
				);
			OnCommand=cmd(diffusealpha,0;sleep,1.7;linear,0.2;diffusealpha,1);
			OffCommand=cmd(zoomx,1;linear,0.3;zoomx,0);
		};
	};
end

	local function Quantize( f, fRoundInterval ) return math.floor( (f + fRoundInterval/2)/fRoundInterval ) * fRoundInterval end

for pn in ivalues(PlayerNumber) do
	local c;
	local meter;
	local fMeterWidth;
	local fMeterRatio;
	local function Update(self)
		if not meter then
			return;
		end

		local life = meter:GetLife();

		if c.TweenHelper:GetTweenTimeLeft() ~= 0 then
			life = c.TweenHelper:getaux();
		end;

		local fBeat = GAMESTATE:GetSongBeatVisible();
		local fBeatFract = fBeat % 1;

		if c.Grad:GetTweenTimeLeft() == 0 then
			-- Each section in the texture is 8 texels wide, including 2 pixels
			-- of separator on the right edge.  The whole life meter is ideally
			-- LifeMeterWidth wide, and exactly fMeterWidth wide (fMeterWidth*fMeterRatio
			-- pixels).  full width (no crop) = fMeterWidth; 
			local fSectionWidth = 8*fMeterRatio;
			local fSeparatorWidth = 2*fMeterRatio;
			local fPulseSections = 4;

			local fPos = life * fMeterWidth;
			fPos = Quantize(fPos, fSectionWidth);
			fPos = fPos - fSeparatorWidth;
			local fPulseDistance = (fPulseSections * fSectionWidth);
			fPulseDistance = fPulseDistance / fMeterWidth;
			fPos = fPos / fMeterWidth;

			local fTipPos = math.min(life, 1.0) * fMeterWidth;
			fTipPos = Quantize(fTipPos, fSectionWidth);
			fTipPos = fTipPos - fSeparatorWidth/2;
			fTipPos = fTipPos;

			local fPulse = scale( fBeatFract, 0.0, 0.25, 0, fPulseDistance );
			fPulse = clamp( fPulse, 0, fPulseDistance );
			if c.TweenHelper:GetTweenTimeLeft() ~= 0 then
				fPulse = fPulseDistance;
			end;
			fPos = fPos - fPulse;

			c.Grad:cropright( 1-fPos );

			local fFlip = (pn == PLAYER_1 and 1 or -1 );
			c.Tip:x( X[pn] + (fTipPos * fFlip) );
			c.Tip:visible( fTipPos >= 0.01 );
			local fVisible = scale( life, 0.95, 1.00, 1, 0 );
			c.Tip:basealpha(fVisible);
		end

		-- This doesn't follow aux.
		local life = meter:GetLife();
		local fVisible = scale( life, 0.80, 1.00, 0, 1 );
		c.Filled:diffusealpha(fVisible);

		local fDist;
		fDist = scale( fBeatFract, 0.0, 1.00, 0, 0.25 );
		fDist = clamp( fDist, 0, 0.25 );
		c.Filled:SetCustomImageRect( 0.25 - fDist, 0, 0.75 + fDist, 1 );
	end

	TopFrame[#TopFrame+1] = Def.ActorFrame {
		LoadActor("meter grad") .. { Name = "Grad"; };
		LoadActor("meter danger") .. { Name = "Danger"; };
		LoadActor("meter tip") .. { Name = "Tip"; };
		LoadActor("meter filled") .. { Name = "Filled"; };
		LoadActor("meter tick") .. { Name = "Tick"; };
		Def.Actor {
			Name = "TweenHelper";
		};

		-- sort of ugly
		--[[	LoadFont("_venacti shadow 15px normal") .. {
				Name = "PlayerName";
				InitCommand=cmd(x,X[pn]);
				BeginCommand=function(self)
					self:player(pn);
					local profile = PROFILEMAN:GetProfile( pn );
					local name = profile:GetDisplayName();
					self:settext( name );
					self:shadowlength(0);
				end;

				OnCommand=cmd(x,140 * (pn == PLAYER_1 and 1 or -1);y,32);
			};
		]]
		--OnCommand=cmd(zoomx,0;sleep,2.2;linear,0.3;zoomx,1);
		--OffCommand=cmd(zoomx,1;linear,0.3;zoomx,0);
		
		InitCommand = function(self)
			self:SetUpdateFunction( Update );

			c = self:GetChildren();
			local f = cmd(
				x,X[pn]+LifeMeterWidth()/2 * (pn == PLAYER_1 and 1 or -1);
				y,SCREEN_TOP+30;
				zoomx,pn == PLAYER_1 and 1 or -1
			);
			f(c.Grad);
			f(c.Danger);
			f(c.Tick);
			f(c.Filled);
		--	f(c.PlayerName);
		end;

		BeginCommand=function(self)
			self:player(pn);
			if not GAMESTATE:IsHumanPlayer(pn) then
				return;
			end

			meter = SCREENMAN:GetTopScreen():GetLifeMeter(pn);

			c.Tick:player(pn);
			c.Tick:halign(0);
			c.Tick:SetTextureFiltering(false);
			c.Tick:x( X[pn] );
			c.Tick:y( SCREEN_TOP+30 );
			c.Tick:diffusealpha(0.4);
			local fFlip = (pn == PLAYER_1 and 1 or -1 );

			local fDisplayWidth = DISPLAY:GetDisplayWidth();
			local fScreenWidth = SCREEN_WIDTH;
			local fRatio = fScreenWidth/fDisplayWidth;
			-- 1/4 -> *4 is fine (== 1)
			-- 1/3 -> *3
			-- 2/3 -> *2 -> 4/3
			local fAdjusted = math.floor( 1/fRatio + 0.5 );
			if fAdjusted > 0.001 then
				fRatio = fRatio*fAdjusted;
			end;
			c.Tick:zoomx(fRatio * fFlip);

			local fTicksWidth = LifeMeterWidth();
			fTicksWidth = fTicksWidth/fRatio;
			fTicksWidth = math.ceil(fTicksWidth / 8) * 8;
			c.Tick:SetCustomImageRect( 0, 0, fTicksWidth/8, 1 );
			c.Tick:SetWidth( fTicksWidth );
			fMeterWidth = fTicksWidth*fRatio;
			fMeterRatio = fRatio;

			c.Grad:halign(0);
			c.Grad:x( X[pn] );
			c.Grad:SetWidth( fMeterWidth );
			c.Grad:zoomx(fFlip);
			c.Grad:diffusealpha(1);
			c.Danger:halign(0);
			c.Danger:x( X[pn] );
			c.Danger:SetWidth( fMeterWidth );
			c.Danger:zoomx(fFlip);
			c.Danger:diffuseshift();
			c.Danger:effectcolor1(color("#FFFFFF40"));
			c.Danger:effectcolor2(color("#FFFFFFC0"));
			c.Danger:diffusealpha(0);
			c.Filled:halign(0);
			c.Filled:x( X[pn] );
			c.Filled:SetWidth( fMeterWidth );
			c.Filled:zoomx(fFlip);
			c.Filled:glowshift();
			c.Filled:effectclock("beat");
			c.Filled:effectperiod(1.000);
			c.Filled:effectcolor1(color("#FFFFFF00"));
			c.Filled:effectcolor2(color("#FFFFFF40"));

			c.Tip:zoomx(fRatio);
			c.Tip:y( SCREEN_TOP+30 );

			c.Tick:x( math.floor( c.Tick:GetX()/fRatio )*fRatio );
		end;
		HealthStateChangedMessageCommand = function(self, params)
			if params.PlayerNumber ~= pn then return; end
			local bDanger = params.HealthState == "HealthState_Danger" or
				params.HealthState == "HealthState_Dead";
			c.Danger:stoptweening();
			c.Danger:linear(0.3);
			c.Danger:diffusealpha(bDanger and 1 or 0);
		end;

		OnCommand=function(self)
			(cmd(finishtweening;diffusealpha,1;fadeleft,0.25;cropleft,1;sleep,2.0;linear,0.3;cropleft,-0.25;
			     linear,0.2;diffusealpha,0.4))(c.Tick);

			c.TweenHelper:aux(0.0);
			c.TweenHelper:sleep(2.0);
			c.TweenHelper:decelerate(0.3);
			c.TweenHelper:aux(0.5);
		end;

		PlayerFailedMessageCommand=function(self, params)
			if params.PlayerNumber ~= pn then return; end

			c.TweenHelper:aux(0.0);
			c.TweenHelper:decelerate(0.3);
			c.TweenHelper:aux(1.0);
			c.Grad:diffusealpha(0);
			c.Danger:effectcolor1(color("#FFFFFFC0"));
			c.Danger:effectcolor2(color("#FFFFFFC0"));
			(cmd(finishtweening;fadeleft,0;cropleft,0;decelerate,0.3;cropleft,1))(c.Tick);
		end;
	};
end


local function MakeStageCreditIcons(pn)
	local Icon = LoadActor("stage credit icon");
	local XSpacing = 14;
	local t = Def.ActorFrame {
	};
	for i = 1,7 do
		local function Shown()
			if not GAMESTATE:IsHumanPlayer(pn) then return false; end;
			if GAMESTATE:IsCourseMode() then
				return GAMESTATE:GetCurrentCourse():GetEstimatedNumStages() - GAMESTATE:GetLoadingCourseSongIndex() - 1 >= i;
			else
				return GAMESTATE:GetNumStagesLeft(pn) >= i;
			end
		end;
		t[#t+1] = Icon .. {
			InitCommand = cmd(x,(i-1)*XSpacing);
			BeginCommand=cmd(visible,Shown());
			ChangeCourseSongOutMessageCommand=cmd(visible,Shown());
			OnCommand=cmd(diffusealpha,0;zoom,0;sleep,1.7+0.1*(i-1);linear,0.3;zoom,1;diffusealpha,1);
		};
	end
	return t;
end;

--[[ TopFrame[#TopFrame+1] = MakeStageCreditIcons(PLAYER_1) .. {
	InitCommand=cmd(x,SCREEN_LEFT+42;y,SCREEN_TOP+55);
};
TopFrame[#TopFrame+1] = MakeStageCreditIcons(PLAYER_2) .. {
	InitCommand=cmd(x,SCREEN_RIGHT-42;y,SCREEN_TOP+55;zoomx,-1);
}; --]] --]]
local function SingleLifeMeter()
	local style = GAMESTATE:GetCurrentStyle();
	return style and style:GetStyleType() == "StyleType_OnePlayerTwoSides";
end

local function MakePercentScore(pn)
    local Percent =  LoadFont("Hayate Numbers") .. {
        Text="0.00%";
        OnCommand=cmd(shadowlength,0;diffuse,PlayerColor(pn););
        JudgmentMessageCommand=function(self,params)
            local UsePercentage = PREFSMAN:GetPreference("PercentageScoring")
            local Stats = STATSMAN:GetCurStageStats():GetPlayerStageStats(pn);
            local fScorePercent = FormatPercentScore( Stats:GetPercentDancePoints() );
            local fScoreDancePoints = Stats:GetActualDancePoints();
            local fScoreDancePointsPossible = Stats:GetPossibleDancePoints();
            self:settext(UsePercentage and fScore or fScoreDancePoints .. "/" .. fScoreDancePointsPossible );
        end
    };
    return Percent
end
local TopFrame = Def.ActorFrame {
    MakePercentScore(PLAYER_1) .. {
        InitCommand=cmd(x,math.floor(scale(0.25,0,1,SCREEN_LEFT,SCREEN_RIGHT));y,SCREEN_TOP+72;player,PLAYER_1);
    };
    MakePercentScore(PLAYER_1) .. {
        InitCommand=cmd(x,math.floor(scale(0.75,0,1,SCREEN_LEFT,SCREEN_RIGHT));y,SCREEN_TOP+72;player,PLAYER_2);
    };
};

--[[ if Var "LoadingScreen" ~= "ScreenDemonstration" then
	local LastCoins = 0;
	local CreditsString = THEME:GetString("ScreenSystemLayer", "CreditsCredits");

	CreditsText = LoadFont("_venacti 26px bold diffuse") .. {
		InitCommand=cmd(
			shadowlength,1;
			visible,GAMESTATE:GetCoinMode() == "CoinMode_Pay";
			x,SCREEN_CENTER_X;y,SCREEN_BOTTOM-10;zoom,0.5;
			playcommand,"Update";
		);

		CoinInsertedMessageCommand=cmd(playcommand,"Update");
		UpdateCommand=function(self)
			local Coins = GAMESTATE:GetCoins();
			if Coins == 0 then
				self:settext( "" );
				return;
			end
			if LastCoins == 0 then
				self:addy(30);
				self:smooth(0.3);
				self:addy(-30);
			end
			LastCoins = Coins;

			local CoinsPerCredit = PREFSMAN:GetPreference("CoinsPerCredit");
			local Credits = math.floor(Coins / CoinsPerCredit);
			local RemainingCoins = Coins % CoinsPerCredit;
			local s = CreditsString .. ": ";
			if Credits > 0 or CoinsPerCredit == 1 then
				s = s .. tostring(Credits);
			end
			
			if CoinsPerCredit > 1 then
				s = s .. "  " .. tostring(RemainingCoins) .. "/" .. tostring(CoinsPerCredit);
			end
			self:settext( s );
		end;
	};
	Overlay[#Overlay+1] = CreditsText;
end --]]

return TopFrame;
