if ( SERVER ) then return end

local meta = FindMetaTable( "Panel" )

--[[---------------------------------------------------------
   Name:	SetTerm
   Desc:	Kill the panel at this time
-----------------------------------------------------------]]  
function meta:SetTerm( term )

	self.Term = SysTime() + term
	self:SetAnimationEnabled( true )

end

--[[---------------------------------------------------------
   Name:	AnimationThinkInternal
   Desc:	anim
-----------------------------------------------------------]]  
function meta:AnimationThinkInternal()

	local systime = SysTime()

	if ( self.Term && self.Term <= systime ) then self:Remove() return end
	if ( !self.m_AnimList ) then return end -- This can happen if we only have term

	for k, anim in pairs( self.m_AnimList ) do
		
		if ( systime >= anim.StartTime ) then
		
			local Fraction = math.TimeFraction( anim.StartTime, anim.EndTime, systime )
			Fraction = math.Clamp( Fraction, 0, 1 )
			
			if ( anim.Think ) then
			
				local Frac = Fraction ^ anim.Ease
				
				-- Ease of -1 == ease in out
				if ( anim.Ease < 0 ) then
					Frac = Fraction ^ (1.0 - ((Fraction-0.5)))				
				end
			
				anim:Think( self, Frac )
			end
			
			if ( Fraction == 1 ) then
			
				if ( anim.OnEnd ) then anim:OnEnd( self ) end
			
				self.m_AnimList[k] = nil				
			end
			
		end

	end
	
end

--[[---------------------------------------------------------
   Name:	SetAnimationEnabled
   Desc:	anim
-----------------------------------------------------------]]  
function meta:SetAnimationEnabled( b )

	if (!b) then
		self.AnimationThink = nil
		return
	end
	
	if ( self.AnimationThink ) then return end
	
	self.AnimationThink = self.AnimationThinkInternal

end

function meta:Stop()

	self.m_AnimList = {}

end

function meta:Queue()

	self.m_AnimQueue = true

end

function meta:AnimTail()

	local last = SysTime()

	for k, anim in pairs( self.m_AnimList ) do
		last = math.max( last, anim.EndTime )
	end

	return last;
	
end

--[[---------------------------------------------------------
   Name:	NewAnimation
   Desc:	anim
-----------------------------------------------------------]]  
function meta:NewAnimation( length, delay, ease, callback )

	if ( delay == nil ) then delay = 0 end
	if ( ease == nil ) then ease = -1 end
	
	if ( self.m_AnimQueue ) then
	
		delay = delay + self:AnimTail()
		self.m_AnimQueue = false;
		
	else
	
		delay = delay + SysTime()
		
	end
	
	local anim = 
	{
		EndTime = delay + length, 
		StartTime = delay,
		Ease = ease,
		OnEnd = callback
	}
					
	self:SetAnimationEnabled( true )
	if (self.m_AnimList == nil) then self.m_AnimList = {} end
	
	table.insert( self.m_AnimList, anim )
	
	return anim

end

local function MoveThink( anim, panel, fraction )

	if ( !anim.StartPos ) then anim.StartPos = Vector( panel.x, panel.y, 0 ) end
	local pos = LerpVector( fraction, anim.StartPos, anim.Pos )
	panel:SetPos( pos.x, pos.y )
	
end

--[[---------------------------------------------------------
   Name:	MoveTo
-----------------------------------------------------------]]  
function meta:MoveTo( x, y, length, delay, ease, callback )

	if ( self.x == x && self.y == y ) then return end
	
	local anim = self:NewAnimation( length, delay, ease, callback )
	anim.Pos = Vector( x, y, 0 )
	anim.Think = MoveThink

end

local function SizeThink( anim, panel, fraction )

	if ( !anim.StartSize ) then local w, h = panel:GetSize() anim.StartSize = Vector( w, h, 0 ) end
	
	local size = LerpVector( fraction, anim.StartSize, anim.Size )
	
	if ( anim.SizeX && anim.SizeY ) then
		panel:SetSize( size.x, size.y )
	elseif ( anim.SizeX ) then
		panel:SetWide( size.x )
	else
		panel:SetTall( size.y )
	end
		
	if ( panel:GetDock() > 0 ) then
		panel:InvalidateParent()
	end
	
end

--[[---------------------------------------------------------
   Name:	SizeTo
-----------------------------------------------------------]]  
function meta:SizeTo( w, h, length, delay, ease, callback )

	local anim = self:NewAnimation( length, delay, ease, callback )
	
	anim.SizeX = w != -1;
	anim.SizeY = h != -1;
	
	if ( !anim.SizeX ) then  w = self:GetWide() end
	if ( !anim.SizeY ) then  h = self:GetTall() end
	
	anim.Size = Vector( w, h, 0 )

	anim.Think = SizeThink
	return anim

end

--[[---------------------------------------------------------
   Name:	SlideUp
-----------------------------------------------------------]]  
function meta:SlideUp( length )

	local height = self:GetTall();
	local anim = self:SizeTo( -1, 0, length )
	anim.OnEnd = function()
		self:SetVisible( false )
		self:SetTall( height )
	end

end

--[[---------------------------------------------------------
   Name:	SlideDown
-----------------------------------------------------------]] 
function meta:SlideDown( length )

	local height = self:GetTall();
	self:SetVisible( true )
	self:SetTall( 0 )
	
	local anim = self:SizeTo( -1, height, length )

end

local function ColorThink( anim, panel, fraction )

	if ( !anim.StartColor ) then anim.StartColor = panel:GetColor() end
	
	panel:SetColor( Color( Lerp( fraction, anim.StartColor.r, anim.Color.r ),
					Lerp( fraction, anim.StartColor.g, anim.Color.g ),
					Lerp( fraction, anim.StartColor.b, anim.Color.b ),
					Lerp( fraction, anim.StartColor.a, anim.Color.a ) ) )
	
end

--[[---------------------------------------------------------
   Name:	ColorTo
-----------------------------------------------------------]]  
function meta:ColorTo( col, length, delay, callback )

	-- We can only use this on specific panel types!
	if ( !self.SetColor ) then return end
	if ( !self.GetColor ) then return end

	local anim = self:NewAnimation( length, delay, nil, callback )
	anim.Color = col
	anim.Think = ColorThink

end

local function AlphaThink( anim, panel, fraction )

	if ( !anim.StartAlpha ) then anim.StartAlpha = panel:GetAlpha() end
	
	panel:SetAlpha( Lerp( fraction, anim.StartAlpha, anim.Alpha ) )
	
end

--[[---------------------------------------------------------
   Name:	AlphaTo
-----------------------------------------------------------]]  
function meta:AlphaTo( alpha, length, delay, callback )

	local anim = self:NewAnimation( length, delay, nil, callback )
	anim.Alpha = alpha
	anim.Think = AlphaThink

end


local function MoveByThink( anim, panel, fraction )

	if ( !anim.StartPos ) then 
		anim.StartPos = Vector( panel.x, panel.y, 0 ) 
		anim.Pos = anim.StartPos + anim.Pos
	end
	
	local pos = LerpVector( fraction, anim.StartPos, anim.Pos )
	panel:SetPos( pos.x, pos.y )
	
end

--[[---------------------------------------------------------
   Name:	MoveBy
-----------------------------------------------------------]]  
function meta:MoveBy( x, y, length, delay, ease, callback )

	local anim = self:NewAnimation( length, delay, ease, callback )
	anim.Pos = Vector( x, y, 0 )
	anim.Think = MoveByThink

end


local function LerpPositions( anim, panel )

	if ( !panel.TargetPos ) then return end
	
	local Speed = FrameTime() * 100 * anim.Speed
	local Pos = Vector( panel.x, panel.y, 0 )
	local Distance = panel.TargetPos - Pos
	local Length = Distance:Length()
	
	if ( anim.UseGravity && Length > 1 ) then
		Speed = Speed * (Length * 0.1)
	end
		
	if ( Length < Speed ) then
		panel:SetPosReal( panel.TargetPos.x, panel.TargetPos.y )
		panel.TargetPos = nil
		return
	end
	
	Distance:Normalize()
	Distance = Pos + (Distance * Speed)
	
	panel:SetPosReal( Distance.x, Distance.y )
	
end

local function NewSetPos( self, x, y )

	self.TargetPos = Vector( x, y )

end

--[[---------------------------------------------------------
   Name:	LerpPositions
-----------------------------------------------------------]]  
function meta:LerpPositions( speed, usegravity )

	if ( self.SetPosReal ) then return end
	
	self.SetPosReal = self.SetPos
	self.SetPos = NewSetPos

	self.LerpAnim = self:NewAnimation( 60 * 60 * 24 )
	self.LerpAnim.Speed = speed
	self.LerpAnim.UseGravity = usegravity
	self.LerpAnim.Think = LerpPositions

end

--
-- DisableLerp
--
function meta:DisableLerp()

	self.LerpAnim = nil
	self.SetPos = self.SetPosReal

end