
include ( "panel/animation.lua" )
include ( "panel/dragdrop.lua" )
include ( "panel/selections.lua" )
include ( "panel/scriptedpanels.lua" )

local meta = FindMetaTable( "Panel" )

AccessorFunc( meta, "m_strCookieName",  "CookieName" )

meta.SetFGColorEx = meta.SetFGColor
meta.SetBGColorEx = meta.SetBGColor


--[[---------------------------------------------------------
   Name:	SetFGColor
   Desc:	Override to make it possible to pass Color's
-----------------------------------------------------------]]  
function meta:SetFGColor( r, g, b, a )

	if ( istable( r ) ) then
		return self:SetFGColorEx( r.r, r.g, r.b, r.a )
	end

	return self:SetFGColorEx( r, g, b, a )

end


--[[---------------------------------------------------------
   Name:	SetBGColor
   Desc:	Override to make it possible to pass Color's
-----------------------------------------------------------]]  
function meta:SetBGColor( r, g, b, a )

	if ( istable( r ) ) then
		return self:SetBGColorEx( r.r, r.g, r.b, r.a )
	end

	return self:SetBGColorEx( r, g, b, a )

end


--[[---------------------------------------------------------
	Name: SetHeight
-----------------------------------------------------------]]  
function meta:SetHeight( h )

	self:SetSize( self:GetWide(), h )

end

meta.SetTall = meta.SetHeight


--[[---------------------------------------------------------
	Name: SetHeight
-----------------------------------------------------------]]  
function meta:SetWidth( w )

	self:SetSize( w, self:GetTall() )

end

meta.SetWide = meta.SetWidth


--[[---------------------------------------------------------
	Name: StretchToParent (borders)
-----------------------------------------------------------]]  
function meta:StretchToParent( l, u, r, d )

	local w, h = self:GetParent():GetSize()

	if ( l != nil ) then
		self.x = l
	end
	
	if ( u != nil ) then
		self.y = u
	end
	
	if ( r != nil ) then
		self:SetWide( w - self.x - r )
	end
	
	if ( d != nil ) then
		self:SetTall( h - self.y - d )
	end
	
	--self:SetPos( l, u )
	--self:SetSize( w - (r + l), h - (d + u) )

end

--[[---------------------------------------------------------
	Name: CopyHeight
-----------------------------------------------------------]]  
function meta:CopyHeight( pnl )

	self:SetTall( pnl:GetTall() )

end

--[[---------------------------------------------------------
	Name: CopyWidth
-----------------------------------------------------------]]  
function meta:CopyWidth( pnl )

	self:SetWide( pnl:GetWide() )

end

--[[---------------------------------------------------------
	Name: CopyPos
-----------------------------------------------------------]]  
function meta:CopyPos( pnl )

	self:SetPos( pnl:GetPos() )

end

--[[---------------------------------------------------------
	Name: Align with the edge of the parent
-----------------------------------------------------------]]  
function meta:AlignBottom( m ) m = m or 0; self:SetPos( self.x, self:GetParent():GetTall() - self:GetTall() - m ) end 
function meta:AlignRight( m ) m = m or 0; self:SetPos( self:GetParent():GetWide() - self:GetWide() - m, self.y ) end
function meta:AlignTop( m ) m = m or 0; self:SetPos( self.x, m ) end
function meta:AlignLeft( m ) m = m or 0; self:SetPos( m, self.y ) end

--[[---------------------------------------------------------
	Name: Move relative to another panel
-----------------------------------------------------------]]  
function meta:MoveAbove( pnl, m ) m = m or 0; self:SetPos( self.x, pnl.y - self:GetTall() - m ) end
function meta:MoveBelow( pnl, m ) m = m or 0; self:SetPos( self.x, pnl.y + pnl:GetTall() + m ) end
function meta:MoveRightOf( pnl, m ) m = m or 0; self:SetPos( pnl.x + pnl:GetWide() + m, self.y ) end
function meta:MoveLeftOf( pnl,  m ) m = m or 0; self:SetPos( pnl.x - self:GetWide() - m, self.y ) end


--[[---------------------------------------------------------
	Name: StretchRightTo
-----------------------------------------------------------]]  
function meta:StretchRightTo( pnl, m ) m = m or 0; self:SetWide( pnl.x - self.x -  m ) end
function meta:StretchBottomTo( pnl, m ) m = m or 0; self:SetTall( pnl.y - self.y - m ) end

--[[---------------------------------------------------------
	Name: CenterVertical
-----------------------------------------------------------]]  
function meta:CenterVertical( fraction )

	fraction = fraction or 0.5
	self:SetPos( self.x, self:GetParent():GetTall() * fraction -  self:GetTall() * 0.5 )

end

--[[---------------------------------------------------------
	Name: CenterHorizontal
-----------------------------------------------------------]]  
function meta:CenterHorizontal( fraction )

	fraction = fraction or 0.5
	self:SetPos( self:GetParent():GetWide() * fraction - self:GetWide() * 0.5, self.y )

end

--[[---------------------------------------------------------
	Name: CenterHorizontal
-----------------------------------------------------------]]  
function meta:Center()

	self:CenterVertical()
	self:CenterHorizontal()

end

--[[---------------------------------------------------------
	Name: CopyBounds
-----------------------------------------------------------]]  
function meta:CopyBounds( pnl )

	local x, y, w, h = pnl:GetBounds()
	
	self:SetPos( x, y )
	self:SetSize( w, h )

end

--[[---------------------------------------------------------
	Name: GetCookieNumber
-----------------------------------------------------------]]  
function meta:SetCookieName( cookiename )

	self.m_strCookieName = cookiename
	
	-- If we have a loadcookies function, call it.
	if ( self.LoadCookies ) then
		self:LoadCookies()
		self:InvalidateLayout()
	end

end


--[[---------------------------------------------------------
	Name: GetCookieNumber
-----------------------------------------------------------]]  
function meta:GetCookieNumber( cookiename, default )

	local name = self:GetCookieName()
	if ( !name ) then return default end
	
	return cookie.GetNumber( name.."."..cookiename, default )

end

--[[---------------------------------------------------------
	Name: GetCookie
-----------------------------------------------------------]]  
function meta:GetCookie( cookiename, default )

	local name = self:GetCookieName()
	if ( !name ) then return default end
	
	return cookie.GetString( name.."."..cookiename, default )

end

--[[---------------------------------------------------------
	Name: SetCookie
-----------------------------------------------------------]]  
function meta:SetCookie( cookiename, value )

	local name = self:GetCookieName()
	if ( !name ) then return end
	
	return cookie.Set( name.."."..cookiename, value )

end


--[[---------------------------------------------------------
	Name: InvalidateParent
-----------------------------------------------------------]]  
function meta:InvalidateParent( layoutnow )

	local parent = self:GetParent()
	if ( !parent ) then return end
	if ( self.LayingOutParent ) then return end
	
	self.LayingOutParent = true
	parent:InvalidateLayout( layoutnow )
	self.LayingOutParent = false

end

--[[---------------------------------------------------------
	Name: PositionLabel
-----------------------------------------------------------]] 
function meta:PositionLabel( LabelWidth, X, Y, Lbl, Control )

	Lbl:SetWide( LabelWidth )
	Lbl:SetPos( X, Y )
	
	Control.y = Y
	Control:MoveRightOf( Lbl, 0 )

	return Y + math.max( Lbl:GetTall(), Control:GetTall() )

end




--[[---------------------------------------------------------
   Name: SetTooltip
-----------------------------------------------------------]]
function meta:SetTooltip( tooltip )
	self.strTooltipText = tooltip
end

meta.SetToolTip = meta.SetTooltip

--[[---------------------------------------------------------
   Name: SetTooltipPanel
-----------------------------------------------------------]]
function meta:SetTooltipPanel( panel )
	self.pnlTooltipPanel = panel
end

meta.SetToolTipPanel = meta.SetTooltipPanel


--[[---------------------------------------------------------
   Name: SizeToContentsY (Only works on Labels)
-----------------------------------------------------------]]
function meta:SizeToContentsY( addval )
	local w, h = self:GetContentSize()
	if (!w || !h ) then return end
	addval = addval or 0
	self:SetTall( h )
end

--[[---------------------------------------------------------
   Name: SizeToContentsX (Only works on Labels)
-----------------------------------------------------------]]
function meta:SizeToContentsX( addval )
	local w, h = self:GetContentSize()
	if (!w || !h) then return end
	addval = addval or 0
	self:SetWide( w + addval )
end

--[[---------------------------------------------------------
   Name: SetSkin
-----------------------------------------------------------]]
function meta:SetSkin( strSkin )
	
	if ( self.m_ForceSkinName == strSkin ) then return end
	
	self.m_ForceSkinName = strSkin
	self.m_iSkinIndex = nil
	derma.RefreshSkins()
	
end

--[[---------------------------------------------------------
   Name: GetSkin
-----------------------------------------------------------]]
function meta:GetSkin()

	local skin = nil
	
	if ( derma.SkinChangeIndex() == self.m_iSkinIndex ) then
	
		skin = self.m_Skin
		if ( skin ) then return skin end

	end

	-- We have a default skin
	if ( !skin && self.m_ForceSkinName ) then
		skin = derma.GetNamedSkin( self.m_ForceSkinName )
	end
	
	-- No skin, inherit from parent
	local parent = self:GetParent()
	if ( !skin && IsValid( parent ) ) then
		skin = parent:GetSkin()
	end
	
	-- Parent had no skin, use default
	if ( !skin) then
		skin = derma.GetDefaultSkin()
	end
	
	-- Save skin details on us so we don't have to keep looking up
	self.m_Skin 		= skin
	self.m_iSkinIndex 	= derma.SkinChangeIndex()
	
	self:InvalidateLayout( false )
	
	return skin
	
end

--[[---------------------------------------------------------
   Name: ToggleVisible
-----------------------------------------------------------]]
function meta:ToggleVisible()
	
	self:SetVisible( !self:IsVisible() )
	
end

function meta:Distance( pnl )
	
	if ( !IsValid( pnl ) ) then return 0 end	
	return self:DistanceFrom( pnl.x + pnl:GetWide() * 0.5, pnl.y + pnl:GetTall() * 0.5 )
	
end

function meta:DistanceFrom( x, y )
	
	local x = self.x + self:GetWide() * 0.5 - x;
	local y = self.y + self:GetTall() * 0.5 - y;
	
	return math.sqrt( x * x + y *y )
	
end


--[[---------------------------------------------------------
   Name: Retusn the child position on this panel. Even if its parented to children of children.
-----------------------------------------------------------]]
function meta:GetChildPosition( pnl )


	local x = 0;
	local y = 0;

	while ( IsValid( pnl ) && pnl != self ) do
		
		x = x + pnl.x
		y = y + pnl.y
		
		pnl = pnl:GetParent();

	end

	return x, y;

end

--[[---------------------------------------------------------
   Name: Returns true if the panel is valid. This does not
		check the type. If the passed object is anything other
		than a panel or nil, this will error. (speed)
-----------------------------------------------------------]]
function ValidPanel( pnl )

	if (!pnl) then return false end
	
	return pnl:IsValid() 

end


function meta:InvalidateChildren( bRecurse )

	local children = self:GetChildren()
	for k, v in pairs( children ) do
	
		if ( bRecurse ) then
			v:InvalidateChildren( true )
		else
			v:InvalidateChildren( false )
		end

	end
	
	self:InvalidateLayout( true )

end

function meta:IsOurChild( pPanel )

	if ( pPanel == self ) then return true end

	local children = self:GetChildren()
	for k, v in pairs( children ) do
	
		if ( v:IsOurChild( pPanel ) ) then return true end

	end
	
	return false

end

function meta:CopyBase( pnl )

	self:CopyBounds( pnl )
	self:Dock( pnl:GetDock() )
	
	// TODO. More.

end

function meta:Add( pnl )

	if ( isstring( pnl ) ) then
	
		local pnl = vgui.Create( pnl, self )
		return pnl	
		
	end
	
	if ( istable( pnl ) ) then
	
		local pnl = vgui.CreateFromTable( pnl, self )
		return pnl	
		
	end

	pnl:SetParent( self )
	return pnl

end

function meta:GetClosestChild( x, y )

	local distance = 9999
	local closest = nil

	local children = self:GetChildren()
	for k, v in pairs( children ) do
	
		local dist = v:DistanceFrom( x, y )
		if ( dist < distance ) then
			distance = dist;
			closest = v
		end
	end
	
	return closest, distance

end

function meta:LocalCursorPos()
			
	return self:ScreenToLocal( gui.MouseX(), gui.MouseY() )
		
end

function meta:MoveToAfter( pnl )

	local children = self:GetParent():GetChildren()
	
	-- remove us from the table
	table.RemoveByValue( children, self )
		
	-- find the key, where we want to be
	local key = table.KeyFromValue( children, pnl )
	
	if ( key ) then
		-- insert us where we wanna be
		table.insert( children, key+1, self )
	else
		return false;
	end
	
	for k, v in pairs( children ) do
		v:SetZPos( k )
	end
		
end

function meta:MoveToBefore( pnl )

	local children = self:GetParent():GetChildren()
	
	-- remove us from the table
	table.RemoveByValue( children, self )
		
	-- find the key, where we want to be
	local key = table.KeyFromValue( children, pnl )
	
	if ( key ) then
		-- insert us where we wanna be
		table.insert( children, key, self )
	else
		return false
	end
	
	for k, v in pairs( children ) do
		v:SetZPos( k )
	end
		
end

function meta:Clear()

	for k, panel in pairs( self:GetChildren() ) do
	
		panel:Remove()
	
	end

end

function meta:IsHovered()

	return vgui.GetHoveredPanel() == self
	
end

function meta:Show()
	self:SetVisible( true )
end

function meta:Hide()
	self:SetVisible( false )
end


function meta:IsChildHovered( iDepth )

	local Hovered = vgui.GetHoveredPanel()
	if ( !IsValid( Hovered ) ) then return false end

	for i=1, iDepth do

		Hovered = Hovered:GetParent()
		if ( !IsValid( Hovered ) ) then return false end
		if ( Hovered == self ) then return true end

	end

	return false

end
