-- life meter.
local Player = ...;
assert( Player );

local meterBaseLength = SCREEN_CENTER_X+4;
local meterFillLength = meterBaseLength-16; -- was -11
local meterFillOffset = 6.5; -- was 3
local meterFillHeight = 23;

local newBPS;
local oldBPS;

local dpMeterWidth = 160;

-- only used for oni mode:
smLifeMeter = nil;
local oldHealth = nil;

local NumPlayers = GAMESTATE:GetNumPlayersEnabled();
local NumSides = GAMESTATE:GetNumSidesJoined();
local IsDoubles = (NumPlayers == 1 and NumSides == 2);

local function CourseMarkers(barWidth,lineHeight)
	local pm = GAMESTATE:GetPlayMode();
	local ret = Def.ActorFrame{};
	-- don't draw these in course mode, or Endless (too many, inaccurate)
	if not GAMESTATE:IsCourseMode() then return ret;
	elseif pm == 'PlayMode_Endless' then return ret;
	else
		-- find out number of songs in course, which may be wrong
		local songs = GAMESTATE:GetCurrentCourse():GetEstimatedNumStages();
		for i=1,songs do
			local position = scale(i, 1, songs, 0, barWidth);
			ret[#ret+1] = Def.Quad{
				InitCommand=cmd(zoomto,1,lineHeight;diffusealpha,0.5);
				OnCommand=function(self)
					if Player == PLAYER_1 then self:addx(-(SCREEN_WIDTH/4)+5+position);
					else self:addx((SCREEN_WIDTH/4)-5-position);
					end;
				end;
			};
		end;
	end;
	return ret;
end;

local meter = Def.ActorFrame{
	BeginCommand=function(self)
		if IsDoubles and Player ~= GAMESTATE:GetMasterPlayerNumber() then
			self:visible(false);
			return;
		else
			-- old
			--if GAMESTATE:IsSideJoined(Player) or GAMESTATE:IsPlayerEnabled(Player)

			if GAMESTATE:IsPlayerEnabled(Player) then
				self:visible(true);
				-- oni
				if GAMESTATE:IsCourseMode()
					and GAMESTATE:GetPlayMode() == 'PlayMode_Oni' then
					smLifeMeter = SCREENMAN:GetTopScreen():GetLifeMeter(Player);
				end;
			elseif GAMESTATE:IsDemonstration() then
				-- don't hook up oni on demonstration
				self:visible(true);
			end;
		end;
	end;
	OnCommand=cmd(runcommandsonleaves,cmd(addy,-40;linear,0.2;addy,40));
	OffCommand=cmd(runcommandsonleaves,cmd(linear,1;diffusealpha,0));

	-- frame
	LoadActor( THEME:GetPathB("","_frame 3x3"), "_lifebar", meterBaseLength-28, 10);

	-- // start meter proper //
	Def.Quad{
		Name="MeterFill";
		InitCommand=cmd(zoomto,0,meterFillHeight;diffuse,PlayerColor(Player);diffusebottomedge,PlayerMidColor(Player));
		BeginCommand=function(self)
			-- don't bother.
			if not GAMESTATE:IsPlayerEnabled(Player) then
				self:visible(false);
				return;
			end;

			if Player == PLAYER_1 then self:horizalign(left);
			else self:horizalign(right);
			end;

			if Player == PLAYER_1 then self:addx(-(SCREEN_WIDTH/4)+meterFillOffset);
			else self:addx((SCREEN_WIDTH/4)-meterFillOffset+1);
			end;
		end;

		-- check state of mind
		HealthStateChangedMessageCommand=function(self,params)
			if(params.PlayerNumber == Player) then
				if(params.HealthState == 'HealthState_Hot') then
					self:glowshift();
					self:effectcolor1(color("1,1,1,0"));
					self:effectcolor2(color("1,1,1,0.25"));
					self:effectclock("beatnooffset");
				else
					self:stopeffect();
				end;
			end;
		end;

		-- check life (LifeMeterBar)
		LifeChangedMessageCommand=function(self,params)
			if(params.Player == Player) then
				local life = params.LifeMeter:GetLife() * (meterFillLength);
				self:finishtweening();
				self:bouncebegin(0.1);
				self:zoomx( life );
			end;
		end;

		-- check life (LifeMeterBattery)
		BatteryLifeChangedMessageCommand=function(self,params)
			local life = params.CurrentHealth * meterFillLength;
			self:finishtweening();
			self:accelerate(0.05);
			self:zoomx( life );
		end;
	};
	-- hot meter fill
	LoadActor( HDActor("hot") )..{
		Name="MeterHotOverlay";
		InitCommand=cmd(diffusealpha,0;blend,'BlendMode_Add';setsize,meterFillLength,32); --zoomx,1.225;

		HealthStateChangedMessageCommand=function(self,params)
			if(params.PlayerNumber == Player) then
				if(params.HealthState == 'HealthState_Hot') then
					self:finishtweening();
					self:linear(0.1);
					self:diffusealpha(0.5);
				else
					self:finishtweening();
					self:linear(0.1);
					self:diffusealpha(0);
				end;
			else return;
			end;
		end;
	};

	-- dance points bg
	Def.ActorFrame{
		InitCommand=cmd(y,8);
		BeginCommand=function(self)
			-- test for pref
			if GetUserPref("DPMeter") == nil then
				-- write fallback
				SetUserPref("DPMeter",true);
			end;

			if GetUserPref("DPMeter") == "true" then
				self:visible(true);
			else
				self:visible(false);
			end;
		end;

		LoadActor( HDActor("dpmeter") )..{
			Name="DancePointFrame";
			BeginCommand=function(self)
				if Player == PLAYER_1 then self:horizalign(left);
				else self:horizalign(right);
				end;

				if Player == PLAYER_1 then self:addx(-(SCREEN_WIDTH/4)+5);
				else self:addx((SCREEN_WIDTH/4)-5);
				end;
			end;
		};

		-- dancce points meter separators in course mode
		CourseMarkers(120,4);

		-- dance points meter
		Def.Quad{
			Name="DancePointMeter";
			InitCommand=cmd(zoomto,0,3;diffuse,PlayerColor(Player));
			BeginCommand=function(self)
				if Player == PLAYER_1 then self:horizalign(left);
				else self:horizalign(right);
				end;

				if Player == PLAYER_1 then self:addx(-(SCREEN_WIDTH/4)+8);
				else self:addx((SCREEN_WIDTH/4)-8);
				end;
			end;

			JudgmentMessageCommand=cmd(playcommand,"Update");
			UpdateCommand=function(self)
				local actualDP = STATSMAN:GetCurStageStats():GetPlayerStageStats(Player):GetActualDancePoints();
				local totalDP = STATSMAN:GetCurStageStats():GetPlayerStageStats(Player):GetPossibleDancePoints();
				self:finishtweening();
				self:zoomx( (actualDP / totalDP) * (dpMeterWidth-4),0,(dpMeterWidth-4) );
			end;
		};

		-- dance points overlay
		Def.Quad{
			Name="DancePointOverlay";
			InitCommand=cmd(zoomto,dpMeterWidth-5,4;diffuse,color("1,1,1,0.1");blend,'BlendMode_Add');
			BeginCommand=function(self)
				if Player == PLAYER_1 then self:horizalign(left);
				else self:horizalign(right);
				end;

				if Player == PLAYER_1 then self:addx(-(SCREEN_WIDTH/4)+5);
				else self:addx((SCREEN_WIDTH/4)-7);
				end;
			end;
		};
	};
};

meter[#meter+1] = LoadActor("absence")..{
	InitCommand=cmd(zoomto,SCREEN_CENTER_X*0.978,28);
	BeginCommand=function(self)
		if GAMESTATE:IsPlayerEnabled(Player) then
			self:visible(true);
			self:diffusealpha(0);
		else
			self:visible( not GAMESTATE:IsDemonstration() );
		end;
	end;
	-- todo: what if the player can unfail? idk
	PlayerFailedMessageCommand=function(self,params)
		if params.PlayerNumber == Player then
			self:accelerate(1);
			self:diffusealpha(1);
		end;
	end;
};

local function Update(self)
	-- oni mode has to update the lifebar differently.
	if GAMESTATE:IsCourseMode() and GAMESTATE:GetPlayMode() == 'PlayMode_Oni' then
		local newHealth = smLifeMeter:GetLife();

		if newHealth ~= oldHealth then
			MESSAGEMAN:Broadcast("BatteryLifeChanged", { CurrentHealth=newHealth });
		end;
		-- and update oldHealth
		oldHealth = newHealth;
	end;

	local hot = self:GetChild("MeterHotOverlay");

	newBPS = GAMESTATE:GetSongBPS();
	local move = (newBPS*-1)/2;
	if GAMESTATE:GetSongFreeze() then move = 0; end;
	hot:texcoordvelocity(move,0);

	oldBPS = newBPS;
end;

meter.InitCommand=cmd(SetUpdateFunction,Update)

return meter;

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

This code is licensed under AJ's StepMania Theme License.

Terms of the license are as follows:
1) Redistributions of this code must include this license.

2) Under this license, you agree to not claim the theme as your own or
use parts of it without permission. Please contact me if you wish to use this
code in your own themes.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--]]