--[[   _                                
	( )                               
   _| |   __   _ __   ___ ___     _ _ 
 /'_` | /'__`\( '__)/' _ ` _ `\ /'_` )
( (_| |(  ___/| |   | ( ) ( ) |( (_| |
`\__,_)`\____)(_)   (_) (_) (_)`\__,_) 


--]]
local PANEL = {}

AccessorFunc( PANEL, "m_bSizeToContents", 		"AutoSize" )
AccessorFunc( PANEL, "m_bStretchHorizontally", 	"StretchHorizontally" )
AccessorFunc( PANEL, "m_bNoSizing", 			"NoSizing" )
AccessorFunc( PANEL, "m_bSortable", 			"Sortable" )
AccessorFunc( PANEL, "m_fAnimTime", 			"AnimTime" )
AccessorFunc( PANEL, "m_fAnimEase", 			"AnimEase" )
AccessorFunc( PANEL, "m_strDraggableName", 		"DraggableName" )

AccessorFunc( PANEL, "Spacing", 	"Spacing" )
AccessorFunc( PANEL, "Padding", 	"Padding" )

--[[---------------------------------------------------------
   Name: Init
-----------------------------------------------------------]]
function PANEL:Init()

	self:SetDraggableName( "GlobalDPanel" );

	self.pnlCanvas 	= vgui.Create( "DPanel", self )
	self.pnlCanvas:SetPaintBackground( false )
	self.pnlCanvas.OnMousePressed = function( self, code ) self:GetParent():OnMousePressed( code ) end
	self.pnlCanvas.OnChildRemoved = function() self:OnChildRemoved() end
	self.pnlCanvas:SetMouseInputEnabled( true )
	self.pnlCanvas.InvalidateLayout = function() self:InvalidateLayout() end
	
	self.Items = {}
	self.YOffset = 0
	self.m_fAnimTime = 0;
	self.m_fAnimEase = -1; -- means ease in out
	self.m_iBuilds = 0
	
	self:SetSpacing( 0 )
	self:SetPadding( 0 )
	self:EnableHorizontal( false )
	self:SetAutoSize( false )
	self:SetDrawBackground( true )
	self:SetNoSizing( false )
	
	self:SetMouseInputEnabled( true )
	
	-- This turns off the engine drawing
	self:SetPaintBackgroundEnabled( false )
	self:SetPaintBorderEnabled( false )

end

function PANEL:OnModified()

	-- Override me

end

function PANEL:SizeToContents()

	self:SetSize( self.pnlCanvas:GetSize() )
	
end

function PANEL:GetItems()

	-- Should we return a copy of this to stop 
	-- people messing with it?
	return self.Items
	
end

--[[---------------------------------------------------------
   Name: EnableHorizontal
-----------------------------------------------------------]]
function PANEL:EnableHorizontal( bHoriz )

	self.Horizontal = bHoriz
	
end

--[[---------------------------------------------------------
   Name: EnableVerticalScrollbar
-----------------------------------------------------------]]
function PANEL:EnableVerticalScrollbar()

	if (self.VBar) then return end
	
	self.VBar = vgui.Create( "DVScrollBar", self )
	
end

--[[---------------------------------------------------------
   Name: GetCanvas
-----------------------------------------------------------]]
function PANEL:GetCanvas()

	return self.pnlCanvas

end

--[[---------------------------------------------------------
   Name: Clear
-----------------------------------------------------------]]
function PANEL:Clear( bDelete )

	for k, panel in pairs( self.Items ) do
	
		if ( !IsValid( panel ) ) then continue end
		
		panel:SetVisible( false )
	
		if ( bDelete ) then
			panel:Remove()
		end
		
	end
	
	self.Items = {}

end


--[[---------------------------------------------------------
   Name: AddItem
-----------------------------------------------------------]]
function PANEL:AddItem( item, strLineState )

	if ( !IsValid( item ) ) then return end

	item:SetVisible( true )
	item:SetParent( self:GetCanvas() )
	item.m_strLineState = strLineState or item.m_strLineState;
	table.insert( self.Items, item )
	
	if ( self.m_bSortable ) then
	
		//local DragSlot = item:MakeDraggable( self:GetDraggableName(), self );
		//DragSlot.OnDrop = self.DropAction
			
	end
	
	item:SetSelectable( self.m_bSelectionCanvas );
	
	self:InvalidateLayout()

end

function PANEL:InsertBefore( before, insert, strLineState )

	table.RemoveByValue( self.Items, insert )
	
	self:AddItem( insert, strLineState )
	
	local key = table.KeyFromValue( self.Items, before )
	
	if ( key ) then
		table.RemoveByValue( self.Items, insert )
		table.insert( self.Items, key, insert )
	end

end

function PANEL:InsertAfter( before, insert, strLineState )

	table.RemoveByValue( self.Items, insert )
	self:AddItem( insert, strLineState )
	
	local key = table.KeyFromValue( self.Items, before )
	
	if ( key ) then
		table.RemoveByValue( self.Items, insert )
		table.insert( self.Items, key+1, insert )
	end

end

function PANEL:InsertAtTop( insert, strLineState )

	table.RemoveByValue( self.Items, insert )
	self:AddItem( insert, strLineState )
	
	local key = 1
	if ( key ) then
		table.RemoveByValue( self.Items, insert )
		table.insert( self.Items, key, insert )
	end
	
end

function PANEL.DropAction( Slot, RcvSlot )

	local PanelToMove = Slot.Panel;	
	if ( dragndrop.m_MenuData == "copy" ) then
	
		if ( PanelToMove.Copy ) then
			
			PanelToMove = Slot.Panel:Copy()
			
			PanelToMove.m_strLineState = Slot.Panel.m_strLineState
		else
			return
		end
	
	end
	
	PanelToMove:SetPos( RcvSlot.Data.pnlCanvas:ScreenToLocal( gui.MouseX() - dragndrop.m_MouseLocalX, gui.MouseY() - dragndrop.m_MouseLocalY ) )
	
	if ( dragndrop.DropPos == 4 || dragndrop.DropPos == 8 ) then
		RcvSlot.Data:InsertBefore( RcvSlot.Panel, PanelToMove )
	else
		RcvSlot.Data:InsertAfter( RcvSlot.Panel, PanelToMove )
	end

end

--[[---------------------------------------------------------
   Name: RemoveItem
-----------------------------------------------------------]]
function PANEL:RemoveItem( item, bDontDelete )

	for k, panel in pairs( self.Items ) do
	
		if ( panel == item ) then
		
			self.Items[ k ] = nil
			
			if (!bDontDelete) then
				panel:Remove()
			end
		
			self:InvalidateLayout()
		
		end
	
	end

end

function PANEL:CleanList()

	for k, panel in pairs( self.Items ) do
	
		if ( !IsValid( panel ) || panel:GetParent() != self.pnlCanvas ) then
			self.Items[k] = nil
		end
	
	end

end

--[[---------------------------------------------------------
   Name: Rebuild
-----------------------------------------------------------]]
function PANEL:Rebuild()

	local Offset = 0
	self.m_iBuilds = self.m_iBuilds + 1;

	self:CleanList()
	
	if ( self.Horizontal ) then
	
		local x, y = self.Padding, self.Padding;
		for k, panel in pairs( self.Items ) do
		
			if ( panel:IsVisible() ) then
			
				local OwnLine = (panel.m_strLineState && panel.m_strLineState == "ownline");
			
				local w = panel:GetWide()
				local h = panel:GetTall()
				
				if ( x > self.Padding && (x + w  > self:GetWide() || OwnLine) ) then
				
					x = self.Padding
					y = y + h + self.Spacing
				
				end
				
				if ( self.m_fAnimTime > 0 && self.m_iBuilds > 1 ) then
					panel:MoveTo( x, y, self.m_fAnimTime, 0, self.m_fAnimEase )
				else
					panel:SetPos( x, y )
				end
				
				x = x + w + self.Spacing
				Offset = y + h + self.Spacing
				
				if ( OwnLine ) then
				
					x = self.Padding
					y = y + h + self.Spacing
				
				end
			
			end
		
		end
	
	else
	
		for k, panel in pairs( self.Items ) do
		
			if ( panel:IsVisible() ) then
				
				if ( self.m_bNoSizing ) then
					panel:SizeToContents()
					if ( self.m_fAnimTime > 0 && self.m_iBuilds > 1 ) then
						panel:MoveTo( (self:GetCanvas():GetWide() - panel:GetWide()) * 0.5, self.Padding + Offset, self.m_fAnimTime, 0, self.m_fAnimEase )
					else
						panel:SetPos( (self:GetCanvas():GetWide() - panel:GetWide()) * 0.5, self.Padding + Offset )
					end
				else
					panel:SetSize( self:GetCanvas():GetWide() - self.Padding * 2, panel:GetTall() )
					if ( self.m_fAnimTime > 0 && self.m_iBuilds > 1 ) then
						panel:MoveTo( self.Padding, self.Padding + Offset, self.m_fAnimTime, self.m_fAnimEase )
					else
						panel:SetPos( self.Padding, self.Padding + Offset )
					end
				end
				
				-- Changing the width might ultimately change the height
				-- So give the panel a chance to change its height now, 
				-- so when we call GetTall below the height will be correct.
				-- True means layout now.
				panel:InvalidateLayout( true )
				
				Offset = Offset + panel:GetTall() + self.Spacing
				
			end
		
		end
		
		Offset = Offset + self.Padding
		
	end
	
	self:GetCanvas():SetTall( Offset + self.Padding - self.Spacing ) 

	-- Although this behaviour isn't exactly implied, center vertically too
	if ( self.m_bNoSizing && self:GetCanvas():GetTall() < self:GetTall() ) then

		self:GetCanvas():SetPos( 0, (self:GetTall()-self:GetCanvas():GetTall()) * 0.5 )
	
	end
	
end

--[[---------------------------------------------------------
   Name: OnMouseWheeled
-----------------------------------------------------------]]
function PANEL:OnMouseWheeled( dlta )

	if ( self.VBar ) then
		return self.VBar:OnMouseWheeled( dlta )
	end
	
end

--[[---------------------------------------------------------
   Name: Paint
-----------------------------------------------------------]]
function PANEL:Paint( w, h )
	
	derma.SkinHook( "Paint", "PanelList", self, w, h )
	return true
	
end

--[[---------------------------------------------------------
   Name: OnVScroll
-----------------------------------------------------------]]
function PANEL:OnVScroll( iOffset )

	self.pnlCanvas:SetPos( 0, iOffset )
	
end

--[[---------------------------------------------------------
   Name: PerformLayout
-----------------------------------------------------------]]
function PANEL:PerformLayout()

	local Wide = self:GetWide()
	local YPos = 0
	
	if ( !self.Rebuild ) then
		debug.Trace()
	end
	
	self:Rebuild()
	
	if ( self.VBar && !m_bSizeToContents ) then

		self.VBar:SetPos( self:GetWide() - 13, 0 )
		self.VBar:SetSize( 13, self:GetTall() )
		self.VBar:SetUp( self:GetTall(), self.pnlCanvas:GetTall() )
		YPos = self.VBar:GetOffset()
		
		if ( self.VBar.Enabled ) then Wide = Wide - 13 end

	end

	self.pnlCanvas:SetPos( 0, YPos )
	self.pnlCanvas:SetWide( Wide )
	
	self:Rebuild()
	
	if ( self:GetAutoSize() ) then
	
		self:SetTall( self.pnlCanvas:GetTall() )
		self.pnlCanvas:SetPos( 0, 0 )
	
	end	

end


function PANEL:OnChildRemoved()

	self:CleanList()
	self:InvalidateLayout()
	
end

--[[---------------------------------------------------------
   Name: ScrollToChild
-----------------------------------------------------------]]
function PANEL:ScrollToChild( panel )

	local x, y = self.pnlCanvas:GetChildPosition( panel )
	local w, h = panel:GetSize()
	
	y = y + h * 0.5;
	y = y - self:GetTall() * 0.5;

	self.VBar:AnimateTo( y, 0.5, 0, 0.5 );
	
end


--[[---------------------------------------------------------
   Name: SortByMember
-----------------------------------------------------------]]
function PANEL:SortByMember( key, desc )

	desc = desc or true

	table.sort( self.Items, function( a, b ) 

								if ( desc ) then
								
									local ta = a
									local tb = b
									
									a = tb
									b = ta
								
								end
	
								if ( a[ key ] == nil ) then return false end
								if ( b[ key ] == nil ) then return true end
								
								return a[ key ] > b[ key ]
								
							end )

end

derma.DefineControl( "DPanelList", "A Panel that neatly organises other panels", PANEL, "DPanel" )
