//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//
 //========= Copyright � 1996-2003, Valve LLC, All rights reserved. ============
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================


#include <stdio.h>
#define PROTECTED_THINGS_DISABLE

#include "utldict.h"

#include <vgui/KeyCode.h>
#include <vgui/Cursor.h>
#include <vgui/MouseCode.h>
#include <KeyValues.h>
#include <vgui/IInput.h>
#include <vgui/ISystem.h>
#include <vgui/IVGui.h>
#include <vgui/ISurface.h>

#include <vgui_controls/BuildGroup.h>
#include <vgui_controls/Panel.h>
#include <vgui_controls/PHandle.h>
#include <vgui_controls/Label.h>
#include <vgui_controls/EditablePanel.h>
#include <vgui_controls/MessageBox.h>
#include "filesystem.h"
#include "tier0/icommandline.h"
#include "const.h"
#include "vprof.h"

#if defined( _X360 )
#include "xbox/xbox_win32stubs.h"
#endif

// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>

ConVar vgui_cache_res_files( "vgui_cache_res_files", "1" );

using namespace vgui;

//-----------------------------------------------------------------------------
// Handle table
//-----------------------------------------------------------------------------
IMPLEMENT_HANDLES( BuildGroup, 20 )

CUtlDict< KeyValues* > BuildGroup::m_dictCachedResFiles;

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
BuildGroup::BuildGroup(Panel *parentPanel, Panel *contextPanel)
{
	CONSTRUCT_HANDLE( );

	_enabled=false;
	_snapX=1;
	_snapY=1;
	_cursor_sizenwse = dc_sizenwse;
	_cursor_sizenesw = dc_sizenesw;
	_cursor_sizewe = dc_sizewe;
	_cursor_sizens = dc_sizens;
	_cursor_sizeall = dc_sizeall;
	_currentPanel=null;
	_dragging=false;
	m_pResourceName=NULL;
	m_pResourcePathID = NULL;
	m_hBuildDialog=NULL;
	m_pParentPanel=parentPanel;
	for (int i=0; i<4; ++i)
		_rulerNumber[i] = NULL;
	SetContextPanel(contextPanel);
	_showRulers = false;

}

//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
BuildGroup::~BuildGroup()
{
	if (m_hBuildDialog)
		delete m_hBuildDialog.Get();
	m_hBuildDialog = NULL;

	delete [] m_pResourceName;
	delete [] m_pResourcePathID;

	for (int i=0; i <4; ++i)
	{
		if (_rulerNumber[i])
		{
			delete _rulerNumber[i];
			_rulerNumber[i]= NULL;
		}
	}
	
	DESTRUCT_HANDLE();
}

//-----------------------------------------------------------------------------
// Purpose: Toggles build mode on/off
// Input  : state - new state
//-----------------------------------------------------------------------------
void BuildGroup::SetEnabled(bool state)
{
	if(_enabled != state)
	{
		_enabled = state;
		_currentPanel = NULL;

		if ( state )
		{
			ActivateBuildDialog();
		}
		else
		{
			// hide the build dialog
			if ( m_hBuildDialog )
			{
				m_hBuildDialog->OnCommand("Close");
			}

			// request focus for our main panel
			m_pParentPanel->RequestFocus();
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Check if buildgroup is enabled
//-----------------------------------------------------------------------------
bool BuildGroup::IsEnabled()
{
	return _enabled;
}

//-----------------------------------------------------------------------------
// Purpose: Get the list of panels that are currently selected
//-----------------------------------------------------------------------------
CUtlVector<PHandle> *BuildGroup::GetControlGroup()
{
   return &_controlGroup;
}

//-----------------------------------------------------------------------------
// Purpose:	Check if ruler display is activated
//-----------------------------------------------------------------------------
bool BuildGroup::HasRulersOn()
{
   return _showRulers;
}

//-----------------------------------------------------------------------------
// Purpose:	Toggle ruler display 
//-----------------------------------------------------------------------------
void BuildGroup::ToggleRulerDisplay()
{
	_showRulers = !_showRulers;

	if (_rulerNumber[0] == NULL) // rulers haven't been initialized
	{
		_rulerNumber[0] = new Label(m_pBuildContext, NULL, "");
		_rulerNumber[1] = new Label(m_pBuildContext, NULL, "");
		_rulerNumber[2] = new Label(m_pBuildContext, NULL, "");
		_rulerNumber[3] = new Label(m_pBuildContext, NULL, "");
	}
    SetRulerLabelsVisible(_showRulers);

   m_pBuildContext->Repaint();
}


//-----------------------------------------------------------------------------
// Purpose:	Tobble visibility of ruler number labels
//-----------------------------------------------------------------------------
void BuildGroup::SetRulerLabelsVisible(bool state)
{
	_rulerNumber[0]->SetVisible(state);
	_rulerNumber[1]->SetVisible(state);
	_rulerNumber[2]->SetVisible(state);
	_rulerNumber[3]->SetVisible(state);
}

void BuildGroup::ApplySchemeSettings( IScheme *pScheme )
{
	DrawRulers();
}

//-----------------------------------------------------------------------------
// Purpose:	Draw Rulers on screen if conditions are right
//-----------------------------------------------------------------------------
void BuildGroup::DrawRulers()
{		
	// don't draw if visibility is off
	if (!_showRulers)
	{
		return;
	}
	
	// no drawing if we selected the context panel
	if (m_pBuildContext == _currentPanel)
	{
		SetRulerLabelsVisible(false);
		return;
	}
	else
		SetRulerLabelsVisible(true);
	
	int x, y, wide, tall;
	// get base panel's postition
	m_pBuildContext->GetBounds(x, y, wide, tall);
	m_pBuildContext->ScreenToLocal(x,y);
	
	int cx, cy, cwide, ctall;
	_currentPanel->GetBounds (cx, cy, cwide, ctall);
	
	surface()->PushMakeCurrent(m_pBuildContext->GetVPanel(), false);	
	
	// draw rulers
	surface()->DrawSetColor(255, 255, 255, 255);	// white color
	
	surface()->DrawFilledRect(0, cy, cx, cy+1);           //top horiz left
	surface()->DrawFilledRect(cx+cwide, cy, wide, cy+1);  //top horiz right
	
	surface()->DrawFilledRect(0, cy+ctall-1, cx, cy+ctall);   //bottom horiz left
	surface()->DrawFilledRect(cx+cwide, cy+ctall-1, wide, cy+ctall);   //bottom	 horiz right
	
	surface()->DrawFilledRect(cx,0,cx+1,cy);         //top vert left
	surface()->DrawFilledRect(cx+cwide-1,0, cx+cwide, cy);  //top vert right
	
	surface()->DrawFilledRect(cx,cy+ctall, cx+1, tall); //bottom vert left
	surface()->DrawFilledRect(cx+cwide-1, cy+ctall, cx+cwide, tall); //bottom vert right   
	
	surface()->PopMakeCurrent(m_pBuildContext->GetVPanel());
	
	// now let's put numbers with the rulers
	char textstring[20];
	Q_snprintf (textstring, sizeof( textstring ), "%d", cx);
	_rulerNumber[0]->SetText(textstring);
	int twide, ttall;
	_rulerNumber[0]->GetContentSize(twide,ttall);
	_rulerNumber[0]->SetSize(twide,ttall);
	_rulerNumber[0]->SetPos(cx/2-twide/2, cy-ttall+3);
	
	Q_snprintf (textstring, sizeof( textstring ), "%d", cy);
	_rulerNumber[1]->SetText(textstring);
	_rulerNumber[1]->GetContentSize(twide,ttall);
	_rulerNumber[1]->SetSize(twide,ttall);
	_rulerNumber[1]->GetSize(twide,ttall);
	_rulerNumber[1]->SetPos(cx-twide + 3, cy/2-ttall/2);
	
	Q_snprintf (textstring, sizeof( textstring ), "%d", cy);
	_rulerNumber[2]->SetText(textstring);
	_rulerNumber[2]->GetContentSize(twide,ttall);
	_rulerNumber[2]->SetSize(twide,ttall);
	_rulerNumber[2]->SetPos(cx+cwide+(wide-cx-cwide)/2 - twide/2,  cy+ctall-3);
	
	Q_snprintf (textstring, sizeof( textstring ), "%d", cy);
	_rulerNumber[3]->SetText(textstring);
	_rulerNumber[3]->GetContentSize(twide,ttall);
	_rulerNumber[3]->SetSize(twide,ttall);
	_rulerNumber[3]->SetPos(cx+cwide, cy+ctall+(tall-cy-ctall)/2 - ttall/2);
	
}

//-----------------------------------------------------------------------------
// Purpose: respond to cursor movments
//-----------------------------------------------------------------------------
bool BuildGroup::CursorMoved(int x, int y, Panel *panel)
{
	Assert(panel);

	if ( !m_hBuildDialog.Get() )
	{
		if ( panel->GetParent() )
		{
			EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() );
			if ( ep )
			{
				BuildGroup *bg = ep->GetBuildGroup();
				if ( bg && bg != this )
				{
					bg->CursorMoved( x, y, panel );
				}
			}
		}
		return false;
	}

	// no moving uneditable panels
	// commented out because this has issues with panels moving 
	// to front and obscuring other panels
	//if (!panel->IsBuildModeEditable())
	//	return;

	if (_dragging)
	{
		input()->GetCursorPos(x, y);
		
		if (_dragMouseCode == MOUSE_RIGHT)
		{
			int newW = max( 1, _dragStartPanelSize[ 0 ] + x - _dragStartCursorPos[0] );
			int newH = max( 1, _dragStartPanelSize[ 1 ] + y - _dragStartCursorPos[1] );

			bool shift = ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) );
			bool ctrl = ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) );

			if ( shift )
			{
				newW = _dragStartPanelSize[ 0 ];
			}
			if ( ctrl )
			{
				newH = _dragStartPanelSize[ 1 ];
			}

			panel->SetSize( newW, newH );
			ApplySnap(panel);
		}
		else
		{
			for (int i=0; i < _controlGroup.Count(); ++i)
			{
				// now fix offset of member panels with respect to the one we are dragging
				Panel *groupMember = _controlGroup[i].Get();
			   	groupMember->SetPos(_dragStartPanelPos[0] + _groupDeltaX[i] +(x-_dragStartCursorPos[0]), _dragStartPanelPos[1] + _groupDeltaY[i] +(y-_dragStartCursorPos[1]));
				ApplySnap(groupMember);				
			}
		}

		// update the build dialog
		if (m_hBuildDialog)
		{
			KeyValues *keyval = new KeyValues("UpdateControlData");
			keyval->SetPtr("panel", GetCurrentPanel());
			ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL);

			keyval = new KeyValues("EnableSaveButton");	
			ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL);	
		}
		
		panel->Repaint();
		panel->CallParentFunction(new KeyValues("Repaint"));
	}

	return true;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool BuildGroup::MousePressed(MouseCode code, Panel *panel)
{
	Assert(panel);

	if ( !m_hBuildDialog.Get() )
	{
		if ( panel->GetParent() )
		{
			EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() );
			if ( ep )
			{
				BuildGroup *bg = ep->GetBuildGroup();
				if ( bg && bg != this )
				{
					bg->MousePressed( code, panel );
				}
			}
		}
		return false;
	}

	// if people click on the base build dialog panel.
	if (panel == m_hBuildDialog)
	{
		// hide the click menu if its up
		ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("HideNewControlMenu"), NULL);
		return true;
	}

	// don't select unnamed items
	if (strlen(panel->GetName()) < 1)
		return true;
	
	bool shift = ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) );	
	if (!shift)
	{
		_controlGroup.RemoveAll();	
	}

	// Show new ctrl menu if they click on the bg (not on a subcontrol)
	if ( code == MOUSE_RIGHT && panel == GetContextPanel())
	{		
		// trigger a drop down menu to create new controls
		ivgui()->PostMessage (m_hBuildDialog->GetVPanel(), new KeyValues("ShowNewControlMenu"), NULL);	
	}	
	else
	{	
		// don't respond if we click on ruler numbers
		if (_showRulers) // rulers are visible
		{
			for ( int i=0; i < 4; i++)
			{
				if ( panel == _rulerNumber[i])
					return true;
			}
		}

		_dragging = true;
		_dragMouseCode = code;
		ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("HideNewControlMenu"), NULL);
		
		int x, y;
		input()->GetCursorPos(x, y);
		
		_dragStartCursorPos[0] = x;
		_dragStartCursorPos[1] = y;
	
		
		input()->SetMouseCapture(panel->GetVPanel());
		
		_groupDeltaX.RemoveAll();
		_groupDeltaY.RemoveAll();

		// basepanel is the panel that all the deltas will be calculated from.
		// it is the last panel we clicked in because if we move the panels  as a group
		// it would be from that one
		Panel *basePanel = NULL;
		// find the panel we clicked in, that is the base panel
		// it might already be in the group
		for (int i=0; i< _controlGroup.Count(); ++i)	
		{
			if (panel == _controlGroup[i].Get())
			{
				basePanel = panel;
				break;
			}
		}

		// if its not in the group we just added this panel. get it in the group 
		if (basePanel == NULL)
		{
			PHandle temp;
			temp = panel;
			_controlGroup.AddToTail(temp);
			basePanel = panel;
		}
		
		basePanel->GetPos(x,y);
		_dragStartPanelPos[0]=x;
		_dragStartPanelPos[1]=y;

		basePanel->GetSize( _dragStartPanelSize[ 0 ], _dragStartPanelSize[ 1 ] );

		// figure out the deltas of the other panels from the base panel
		for (int i=0; i<_controlGroup.Count(); ++i)
		{
			int cx, cy;
			_controlGroup[i].Get()->GetPos(cx, cy);
			_groupDeltaX.AddToTail(cx - x);
			_groupDeltaY.AddToTail(cy - y);
		}
						
		// if this panel wasn't already selected update the buildmode dialog controls to show its info
		if(_currentPanel != panel)
		{			
			_currentPanel = panel;
			
			if ( m_hBuildDialog )
			{
				// think this is taken care of by SetActiveControl.
				//ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("ApplyDataToControls"), NULL);
				
				KeyValues *keyval = new KeyValues("SetActiveControl");
				keyval->SetPtr("PanelPtr", GetCurrentPanel());
				ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL);
			}		
		}		

		// store undo information upon panel selection.
		ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("StoreUndo"), NULL);

		panel->RequestFocus();
	}

	return true;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool BuildGroup::MouseReleased(MouseCode code, Panel *panel)
{
	if ( !m_hBuildDialog.Get() )
	{
		if ( panel->GetParent() )
		{
			EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() );
			if ( ep )
			{
				BuildGroup *bg = ep->GetBuildGroup();
				if ( bg && bg != this )
				{
					bg->MouseReleased( code, panel );
				}
			}
		}
		return false;
	}

	Assert(panel);

	_dragging=false;
	input()->SetMouseCapture(null);
	return true;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool BuildGroup::MouseDoublePressed(MouseCode code, Panel *panel)
{
	Assert(panel);
	return MousePressed( code, panel );
}

bool BuildGroup::KeyTyped( wchar_t unichar, Panel *panel )
{
	if ( !m_hBuildDialog.Get() )
	{
		if ( panel->GetParent() )
		{
			EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() );
			if ( ep )
			{
				BuildGroup *bg = ep->GetBuildGroup();
				if ( bg && bg != this )
				{
					bg->KeyTyped( unichar, panel );
				}
			}
		}
		return false;
	}

	return true;
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool BuildGroup::KeyCodeTyped(KeyCode code, Panel *panel)
{
	if ( !m_hBuildDialog.Get() )
	{
		if ( panel->GetParent() )
		{
			EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() );
			if ( ep )
			{
				BuildGroup *bg = ep->GetBuildGroup();
				if ( bg && bg != this )
				{
					bg->KeyCodeTyped( code, panel );
				}
			}
		}
		return false;
	}

	Assert(panel);

	int dx=0;
	int dy=0;

	bool shift = ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) );
	bool ctrl = ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) );
	bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT));

	
	if ( ctrl && shift && alt && code == KEY_B)
	{
		// enable build mode
		EditablePanel *ep = dynamic_cast< EditablePanel * >( panel );
		if ( ep )
		{
			ep->ActivateBuildMode();
		}
		return true;
	}

	switch (code)
	{
		case KEY_LEFT:
		{
			dx-=_snapX;
			break;
		}
		case KEY_RIGHT:
		{
			dx+=_snapX;
			break;
		}
		case KEY_UP:
		{
			dy-=_snapY;
			break;
		}
		case KEY_DOWN:
		{
			dy+=_snapY;
			break;
		}
		case KEY_DELETE:
		{
			// delete the panel we have selected 
			ivgui()->PostMessage (m_hBuildDialog->GetVPanel(), new KeyValues ("DeletePanel"), NULL);
			break;
		}

	}

	if (ctrl)
	{
		switch (code)
		{
		case KEY_Z:
			{
				ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("Undo"), NULL);
				break;
			}

		case KEY_C:
			{
				ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("Copy"), NULL);
				break;
			}
		case KEY_V:
			{
				ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("Paste"), NULL);
				break;
			}
		}
	}

	if(dx||dy)
	{
		//TODO: make this stuff actually snap

		int x,y,wide,tall;

		panel->GetBounds(x,y,wide,tall);

		if(shift)
		{
			panel->SetSize(wide+dx,tall+dy);
		}
		else
		{
			panel->SetPos(x+dx,y+dy);
		}

		ApplySnap(panel);

		panel->Repaint();
		if (panel->GetVParent() != 0)
		{
			panel->PostMessage(panel->GetVParent(), new KeyValues("Repaint"));
		}


		// update the build dialog
		if (m_hBuildDialog)
		{
			// post that it's active
			KeyValues *keyval = new KeyValues("SetActiveControl");
			keyval->SetPtr("PanelPtr", GetCurrentPanel());
			ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL);

			// post that it's been changed
			ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("PanelMoved"), NULL);
		}
	}

	// If holding key while dragging, simulate moving cursor so shift/ctrl key changes take effect
	if ( _dragging && panel != GetContextPanel() )
	{
		int x, y;
		input()->GetCursorPos( x, y );
		CursorMoved( x, y, panel );
	}

	return true;
}

bool BuildGroup::KeyCodeReleased(KeyCode code, Panel *panel )
{
	if ( !m_hBuildDialog.Get() )
	{
		if ( panel->GetParent() )
		{
			EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() );
			if ( ep )
			{
				BuildGroup *bg = ep->GetBuildGroup();
				if ( bg && bg != this )
				{
					bg->KeyCodeTyped( code, panel );
				}
			}
		}
		return false;
	}

	// If holding key while dragging, simulate moving cursor so shift/ctrl key changes take effect
	if ( _dragging && panel != GetContextPanel() )
	{
		int x, y;
		input()->GetCursorPos( x, y );
		CursorMoved( x, y, panel );
	}

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: Searches for a BuildModeDialog in the hierarchy
//-----------------------------------------------------------------------------
Panel *BuildGroup::CreateBuildDialog( void )
{
	// request the panel
	Panel *buildDialog = NULL;
	KeyValues *data = new KeyValues("BuildDialog");
	data->SetPtr("BuildGroupPtr", this);
	if (m_pBuildContext->RequestInfo(data))
	{
		buildDialog = (Panel *)data->GetPtr("PanelPtr");
	}

	// initialize the build dialog if found
	if ( buildDialog )
	{
		input()->ReleaseAppModalSurface();
	}

	return buildDialog;
}

//-----------------------------------------------------------------------------
// Purpose: Activates the build mode settings dialog
//-----------------------------------------------------------------------------
void BuildGroup::ActivateBuildDialog( void )
{
	// create the build mode dialog first time through
	if (!m_hBuildDialog.Get())
	{
		m_hBuildDialog = CreateBuildDialog();

		if (!m_hBuildDialog.Get())
			return;
	}

	m_hBuildDialog->SetVisible( true );

	// send a message to set the initial dialog controls info
	_currentPanel = m_pParentPanel;
	KeyValues *keyval = new KeyValues("SetActiveControl");
	keyval->SetPtr("PanelPtr", GetCurrentPanel());
	ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL);
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
HCursor BuildGroup::GetCursor(Panel *panel)
{
	Assert(panel);
	
	int x,y,wide,tall;
	input()->GetCursorPos(x,y);
	panel->ScreenToLocal(x,y);
	panel->GetSize(wide,tall);

	if(x < 2)
	{
		if(y < 4)
		{
			return _cursor_sizenwse;
		}
		else
		if(y<(tall-4))
		{
			return _cursor_sizewe;
		}
		else
		{
			return _cursor_sizenesw;
		}
	}

	return _cursor_sizeall;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void BuildGroup::ApplySnap(Panel *panel)
{
	Assert(panel);
	
	int x,y,wide,tall;
	panel->GetBounds(x,y,wide,tall);

	x=(x/_snapX)*_snapX;
	y=(y/_snapY)*_snapY;
	panel->SetPos(x,y);
	
	int xx,yy;
	xx=x+wide;
	yy=y+tall;
	
	xx=(xx/_snapX)*_snapX;
	yy=(yy/_snapY)*_snapY;
	panel->SetSize(xx-x,yy-y);
}

//-----------------------------------------------------------------------------
// Purpose:	Return the currently selected panel
//-----------------------------------------------------------------------------
Panel *BuildGroup::GetCurrentPanel()
{
	return _currentPanel;
}

//-----------------------------------------------------------------------------
// Purpose: Add panel the list of panels that are in the build group
//-----------------------------------------------------------------------------
void BuildGroup::PanelAdded(Panel *panel)
{
	Assert(panel);

	PHandle temp;
	temp = panel;
	int c = _panelDar.Count();
	for ( int i = 0; i < c; ++i )
	{
		if ( _panelDar[ i ] == temp )
		{
			return;
		}
	}
	_panelDar.AddToTail(temp);
}

//-----------------------------------------------------------------------------
// Purpose: loads the control settings from file
//-----------------------------------------------------------------------------
void BuildGroup::LoadControlSettings(const char *controlResourceName, const char *pathID, KeyValues *pPreloadedKeyValues, KeyValues *pConditions)
{
	// make sure the file is registered
	RegisterControlSettingsFile(controlResourceName, pathID);

	// Use the keyvalues they passed in or load them.
	KeyValues *rDat = pPreloadedKeyValues;

	bool bUsePrecaching = vgui_cache_res_files.GetBool();
	bool bUsingPrecachedSourceKeys = false;
	bool bShouldCacheKeys = true;
	bool bDeleteKeys = false;

	while ( !rDat )
	{
		if ( bUsePrecaching )
		{
			int nIndex = m_dictCachedResFiles.Find( controlResourceName );
			if ( nIndex != m_dictCachedResFiles.InvalidIndex() )
			{
				rDat = m_dictCachedResFiles[nIndex];
				bUsingPrecachedSourceKeys = true;
				bDeleteKeys = false;
				bShouldCacheKeys = false;
				break;
			}
		}

		// load the resource data from the file
		rDat = new KeyValues( controlResourceName );

		// check the skins directory first, if an explicit pathID hasn't been set
		bool bSuccess = false;
		if ( !pathID )
		{
			bSuccess = rDat->LoadFromFile( g_pFullFileSystem, controlResourceName, "SKIN" );
		}

		if ( !V_stricmp( CommandLine()->ParmValue( "-game", "hl2" ), "tf" ) )
		{
			if ( !bSuccess )
			{
				bSuccess = rDat->LoadFromFile( g_pFullFileSystem, controlResourceName, "custom_mod" );
			}
			if ( !bSuccess )
			{
				bSuccess = rDat->LoadFromFile( g_pFullFileSystem, controlResourceName, "vgui" );
			}
			if ( !bSuccess )
			{
				bSuccess = rDat->LoadFromFile( g_pFullFileSystem, controlResourceName, "BSP" );
			}
			// only allow to load loose files when using insecure mode
			if ( !bSuccess && CommandLine()->FindParm( "-insecure" ) )
			{
				bSuccess = rDat->LoadFromFile( g_pFullFileSystem, controlResourceName, pathID );
			}
		}
		else
		{
			if ( !bSuccess )
			{
				bSuccess = rDat->LoadFromFile( g_pFullFileSystem, controlResourceName, pathID );
			}
		}

		if ( bSuccess )
		{
			if ( IsX360() )
			{
				rDat->ProcessResolutionKeys( surface()->GetResolutionKey() );
			}
			if ( IsPC() )
			{
				ConVarRef cl_hud_minmode( "cl_hud_minmode", true );
				if ( cl_hud_minmode.IsValid() && cl_hud_minmode.GetBool() )
				{
					rDat->ProcessResolutionKeys( "_minmode" );
				}
			}
			bDeleteKeys = true;
			bShouldCacheKeys = true;
		}
		else
		{
			Warning( "Failed to load %s\n", controlResourceName );
		}
		break;
	}

	if ( pConditions && pConditions->GetFirstSubKey() )
	{
		if ( bUsingPrecachedSourceKeys )
		{
			// ProcessConditionalKeys modifies the KVs in place.  We dont want
			// that to happen to our cached keys
			rDat = rDat->MakeCopy();
			bDeleteKeys = true;
		}

		ProcessConditionalKeys(rDat, pConditions);
		bShouldCacheKeys = false;
	}

	// save off the resource name
	delete [] m_pResourceName;
	m_pResourceName = new char[strlen(controlResourceName) + 1];
	strcpy(m_pResourceName, controlResourceName);

	if (pathID)
	{
		delete [] m_pResourcePathID;
		m_pResourcePathID = new char[strlen(pathID) + 1];
		strcpy(m_pResourcePathID, pathID);
	}

	// delete any controls not in both files
	DeleteAllControlsCreatedByControlSettingsFile();

	// loop through the resource data sticking info into controls
	ApplySettings(rDat);

	if (m_pParentPanel)
	{
		m_pParentPanel->InvalidateLayout();
		m_pParentPanel->Repaint();
	}

	if ( bShouldCacheKeys && bUsePrecaching )
	{
		Assert( m_dictCachedResFiles.Find( controlResourceName ) == m_dictCachedResFiles.InvalidIndex() );
		m_dictCachedResFiles.Insert( controlResourceName, rDat );
	}
	else if ( bDeleteKeys )
	{
		Assert( m_dictCachedResFiles.Find( controlResourceName ) != m_dictCachedResFiles.InvalidIndex() || pConditions || pPreloadedKeyValues );
		rDat->deleteThis();
	}
}

void BuildGroup::ProcessConditionalKeys( KeyValues *pData, KeyValues *pConditions )
{
	// for each condition, look for it in keys
	// if its a positive condition, promote all of its children, replacing values

	if ( pData )
	{
		KeyValues *pSubKey = pData->GetFirstSubKey();
		if ( !pSubKey )
		{
			// not a block
			return;
		}

		for ( ; pSubKey != NULL; pSubKey = pSubKey->GetNextKey() )
		{
			// recursively descend each sub block
			ProcessConditionalKeys( pSubKey, pConditions );

			KeyValues *pCondition = pConditions->GetFirstSubKey();
			for ( ; pCondition != NULL; pCondition = pCondition->GetNextKey() )
			{
				// if we match any conditions in this sub block, copy up
				KeyValues *pConditionBlock = pSubKey->FindKey( pCondition->GetName() );
				if ( pConditionBlock )
				{
					KeyValues *pOverridingKey;
					for ( pOverridingKey = pConditionBlock->GetFirstSubKey(); pOverridingKey != NULL; pOverridingKey = pOverridingKey->GetNextKey() )
					{
						KeyValues *pExistingKey = pSubKey->FindKey( pOverridingKey->GetName() );
						if ( pExistingKey )
						{
							pExistingKey->SetStringValue( pOverridingKey->GetString() );
						}
						else
						{
							KeyValues *copy = pOverridingKey->MakeCopy();
							pSubKey->AddSubKey( copy );
						}
					}				
				}
			}			
		}		
	}
}

//-----------------------------------------------------------------------------
// Purpose: registers that a control settings file may be loaded
//			use when the dialog may have multiple states and the editor will need to be able to switch between them
//-----------------------------------------------------------------------------
void BuildGroup::RegisterControlSettingsFile(const char *controlResourceName, const char *pathID)
{
	// add the file into a list for build mode
	CUtlSymbol sym(controlResourceName);
	if (!m_RegisteredControlSettingsFiles.IsValidIndex(m_RegisteredControlSettingsFiles.Find(sym)))
	{
		m_RegisteredControlSettingsFiles.AddToTail(sym);
	}
}

//-----------------------------------------------------------------------------
// Purpose: data accessor / iterator
//-----------------------------------------------------------------------------
int BuildGroup::GetRegisteredControlSettingsFileCount()
{
	return m_RegisteredControlSettingsFiles.Count();
}

//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
const char *BuildGroup::GetRegisteredControlSettingsFileByIndex(int index)
{
	return m_RegisteredControlSettingsFiles[index].String();
}

//-----------------------------------------------------------------------------
// Purpose: reloads the control settings from file
//-----------------------------------------------------------------------------
void BuildGroup::ReloadControlSettings()
{
	delete m_hBuildDialog.Get(); 
	m_hBuildDialog = NULL;

	// loop though objects in the current control group and remove them all
	// the 0th panel is always the contextPanel which is not deletable 
	for( int i = 1; i < _panelDar.Count(); i++ )
	{	
		if (!_panelDar[i].Get()) // this can happen if we had two of the same handle in the list
		{
			_panelDar.Remove(i);
			--i;
			continue;
		}
		
		// only delete deletable panels, as the only deletable panels
		// are the ones created using the resource file
		if ( _panelDar[i].Get()->IsBuildModeDeletable())
		{
			delete _panelDar[i].Get();
			_panelDar.Remove(i);
			--i;
		}		
	}	

	if (m_pResourceName)
	{
		EditablePanel *edit = dynamic_cast<EditablePanel *>(m_pParentPanel);
		if (edit)
		{
			edit->LoadControlSettings(m_pResourceName, m_pResourcePathID);
		}
		else
		{
			LoadControlSettings(m_pResourceName, m_pResourcePathID);
		}
	}

	_controlGroup.RemoveAll();	

	ActivateBuildDialog();	
}

//-----------------------------------------------------------------------------
// Purpose: changes which control settings are currently loaded
//-----------------------------------------------------------------------------
void BuildGroup::ChangeControlSettingsFile(const char *controlResourceName)
{
	// clear any current state
	_controlGroup.RemoveAll();
	_currentPanel = m_pParentPanel;

	// load the new state, via the dialog if possible
	EditablePanel *edit = dynamic_cast<EditablePanel *>(m_pParentPanel);
	if (edit)
	{
		edit->LoadControlSettings(controlResourceName, m_pResourcePathID);
	}
	else
	{
		LoadControlSettings(controlResourceName, m_pResourcePathID);
	}

	// force it to update
	KeyValues *keyval = new KeyValues("SetActiveControl");
	keyval->SetPtr("PanelPtr", GetCurrentPanel());
	ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL);
}

//-----------------------------------------------------------------------------
// Purpose: saves control settings to file
//-----------------------------------------------------------------------------
bool BuildGroup::SaveControlSettings( void )
{
	bool bSuccess = false;
	if ( m_pResourceName )
	{
		KeyValues *rDat = new KeyValues( m_pResourceName );

		// get the data from our controls
		GetSettings( rDat );
		
		char fullpath[ 512 ];
		g_pFullFileSystem->RelativePathToFullPath( m_pResourceName, m_pResourcePathID, fullpath, sizeof( fullpath ) );

		// save the data out to a file
		bSuccess = rDat->SaveToFile( g_pFullFileSystem, fullpath, NULL );
		if (!bSuccess)
		{
			MessageBox *dlg = new MessageBox("BuildMode - Error saving file", "Error: Could not save changes.  File is most likely read only.");
			dlg->DoModal();
		}

		rDat->deleteThis();
	}

	return bSuccess;
}

//-----------------------------------------------------------------------------
// Purpose: Deletes all the controls not created by the code
//-----------------------------------------------------------------------------
void BuildGroup::DeleteAllControlsCreatedByControlSettingsFile()
{
	// loop though objects in the current control group and remove them all
	// the 0th panel is always the contextPanel which is not deletable 
	for ( int i = 1; i < _panelDar.Count(); i++ )
	{	
		if (!_panelDar[i].Get()) // this can happen if we had two of the same handle in the list
		{
			_panelDar.Remove(i);
			--i;
			continue;
		}
		
		// only delete deletable panels, as the only deletable panels
		// are the ones created using the resource file
		if ( _panelDar[i].Get()->IsBuildModeDeletable())
		{
			delete _panelDar[i].Get();
			_panelDar.Remove(i);
			--i;
		}		
	}

	_currentPanel = m_pBuildContext;
	_currentPanel->InvalidateLayout();
    m_pBuildContext->Repaint();
}

//-----------------------------------------------------------------------------
// Purpose: serializes settings from a resource data container
//-----------------------------------------------------------------------------
void BuildGroup::ApplySettings( KeyValues *resourceData )
{
	tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );

	// loop through all the keys, applying them wherever
	for (KeyValues *controlKeys = resourceData->GetFirstSubKey(); controlKeys != NULL; controlKeys = controlKeys->GetNextKey())
	{
		bool bFound = false;

		// Skip keys that are atomic..
		if (controlKeys->GetDataType() != KeyValues::TYPE_NONE)
			continue;

		char const *keyName = controlKeys->GetName();

		// check to see if any buildgroup panels have this name
		for ( int i = 0; i < _panelDar.Count(); i++ )
		{
			Panel *panel = _panelDar[i].Get();

			if (!panel) // this can happen if we had two of the same handle in the list
			{
				_panelDar.Remove(i);
				--i;
				continue;
			}


			Assert (panel);

			// make the control name match CASE INSENSITIVE!
			char const *panelName = panel->GetName();

			if (!Q_stricmp(panelName, keyName))
			{
				// apply the settings
				panel->ApplySettings(controlKeys);
				bFound = true;
				break;
			}
		}

		if ( !bFound )
		{
			// the key was not found in the registered list, check to see if we should create it
			if ( keyName /*controlKeys->GetInt("AlwaysCreate", false)*/ )
			{
				// create the control even though it wasn't registered
				NewControl( controlKeys );
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Create a new control in the context panel
// Input:	name: class name of control to create
//			controlKeys: keyvalues of settings for the panel.
//			name OR controlKeys should be set, not both.  
//			x,y position relative to base panel
// Output: Panel *newPanel, NULL if failed to create new control.
//-----------------------------------------------------------------------------
Panel *BuildGroup::NewControl( const char *name, int x, int y)
{
	Assert (name);
	
	Panel *newPanel = NULL;
	// returns NULL on failure
	newPanel = static_cast<EditablePanel *>(m_pParentPanel)->CreateControlByName(name);
	
	if (newPanel)
	{
		// panel successfully created
		newPanel->SetParent(m_pParentPanel);	
		newPanel->SetBuildGroup(this);
		newPanel->SetPos(x, y);

		char newFieldName[255];
		GetNewFieldName(newFieldName, sizeof(newFieldName), newPanel);
		newPanel->SetName(newFieldName);
		
		newPanel->AddActionSignalTarget(m_pParentPanel);
		newPanel->SetBuildModeEditable(true);
		newPanel->SetBuildModeDeletable(true);	
		
		// make sure it gets freed
		newPanel->SetAutoDelete(true);
	}	

	return newPanel;
}

//-----------------------------------------------------------------------------
// Purpose: Create a new control in the context panel
// Input:	controlKeys: keyvalues of settings for the panel only works when applying initial settings.
// Output:	Panel *newPanel, NULL if failed to create new control.
//-----------------------------------------------------------------------------
Panel *BuildGroup::NewControl( KeyValues *controlKeys, int x, int y)
{
	Assert (controlKeys);
	
	Panel *newPanel = NULL;
	if (controlKeys)
	{
//		Warning( "Creating new control \"%s\" of type \"%s\"\n", controlKeys->GetString( "fieldName" ), controlKeys->GetString( "ControlName" ) );
		KeyValues *keyVal = new KeyValues("ControlFactory", "ControlName", controlKeys->GetString("ControlName"));
		m_pBuildContext->RequestInfo(keyVal);
		// returns NULL on failure
		newPanel = (Panel *)keyVal->GetPtr("PanelPtr");
		keyVal->deleteThis();
	}
	else
	{
		return NULL;
	}

	if (newPanel)
	{
		// panel successfully created
		newPanel->SetParent(m_pParentPanel);	
		newPanel->SetBuildGroup(this);
		newPanel->SetPos(x, y);

		newPanel->SetName(controlKeys->GetName()); // name before applysettings :)
		newPanel->ApplySettings(controlKeys);

		newPanel->AddActionSignalTarget(m_pParentPanel);
		newPanel->SetBuildModeEditable(true);
		newPanel->SetBuildModeDeletable(true);	
		
		// make sure it gets freed
		newPanel->SetAutoDelete(true);
	}	

	return newPanel;
}

//-----------------------------------------------------------------------------
// Purpose: Get a new unique fieldname for a new control
//-----------------------------------------------------------------------------
void BuildGroup::GetNewFieldName(char *newFieldName, int newFieldNameSize, Panel *newPanel)
{
	int fieldNameNumber=1;
	char defaultName[25];
	
	Q_strncpy( defaultName, newPanel->GetClassName(), sizeof( defaultName ) );

	while (1)
	{
		Q_snprintf (newFieldName, newFieldNameSize, "%s%d", defaultName, fieldNameNumber);
		if ( FieldNameTaken(newFieldName) == NULL)
			break;
		++fieldNameNumber;
	}	
}

//-----------------------------------------------------------------------------
// Purpose: check to see if any buildgroup panels have this fieldname
// Input  : fieldName, name to check
// Output : ptr to a panel that has the name if it is taken
//-----------------------------------------------------------------------------
Panel *BuildGroup::FieldNameTaken(const char *fieldName)
{	 	
	for ( int i = 0; i < _panelDar.Count(); i++ )
	{
		Panel *panel = _panelDar[i].Get();
		if ( !panel )
			continue;

		if (!stricmp(panel->GetName(), fieldName) )
		{
			return panel;
		}
	}
	return NULL;
}

//-----------------------------------------------------------------------------
// Purpose: serializes settings to a resource data container
//-----------------------------------------------------------------------------
void BuildGroup::GetSettings( KeyValues *resourceData )
{
	// loop through all the objects getting their settings
	for( int i = 0; i < _panelDar.Count(); i++ )
	{
		Panel *panel = _panelDar[i].Get();
		if (!panel)
			continue;

		bool isRuler = false;
		// do not get setting for ruler labels.
		if (_showRulers) // rulers are visible
		{
			for (int j = 0; j < 4; j++)
			{
				if (panel == _rulerNumber[j])
				{
					isRuler = true;
					break;
				}
			}
			if (isRuler)
			{
				isRuler = false;
				continue;
			}
		}

		// Don't save the setting of the buildmodedialog
		if (!stricmp(panel->GetName(), "BuildDialog"))
			continue;

		// get the keys section from the data file
		if (panel->GetName() && *panel->GetName())
		{
			KeyValues *datKey = resourceData->FindKey(panel->GetName(), true);

			// get the settings
			panel->GetSettings(datKey);
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: loop though objects in the current control group and remove them all
//-----------------------------------------------------------------------------
void BuildGroup::RemoveSettings()
{	
	// loop though objects in the current control group and remove them all
	int i;
	for( i = 0; i < _controlGroup.Count(); i++ )
	{		
		// only delete delatable panels
		if ( _controlGroup[i].Get()->IsBuildModeDeletable())
		{
			delete _controlGroup[i].Get();
			_controlGroup.Remove(i);
			--i;
		}		
	}
	
	// remove deleted panels from the handle list
	for( i = 0; i < _panelDar.Count(); i++ )
	{
		if ( !_panelDar[i].Get() )	
		{	
		  _panelDar.Remove(i);
		  --i;
		}
	}

	_currentPanel = m_pBuildContext;
	_currentPanel->InvalidateLayout();
    m_pBuildContext->Repaint();
}

//-----------------------------------------------------------------------------
// Purpose: sets the panel from which the build group gets all it's object creation info
//-----------------------------------------------------------------------------
void BuildGroup::SetContextPanel(Panel *contextPanel)
{
	m_pBuildContext = contextPanel;
}

//-----------------------------------------------------------------------------
// Purpose: gets the panel from which the build group gets all it's object creation info
//-----------------------------------------------------------------------------
Panel *BuildGroup::GetContextPanel() 
{
	return m_pBuildContext;
}

//-----------------------------------------------------------------------------
// Purpose: get the list of panels in the buildgroup
//-----------------------------------------------------------------------------
CUtlVector<PHandle> *BuildGroup::GetPanelList() 
{
	return &_panelDar;
}

//-----------------------------------------------------------------------------
// Purpose: dialog variables
//-----------------------------------------------------------------------------
KeyValues *BuildGroup::GetDialogVariables()
{
	EditablePanel *edit = dynamic_cast<EditablePanel *>(m_pParentPanel);
	if (edit)
	{
		return edit->GetDialogVariables();
	}

	return NULL;
}

bool BuildGroup::PrecacheResFile( const char* pszResFileName )
{
	KeyValues *pkvResFile = new KeyValues( pszResFileName );
	if ( pkvResFile->LoadFromFile( g_pFullFileSystem, pszResFileName, "GAME" ) )
	{
		Assert( m_dictCachedResFiles.Find( pszResFileName ) == m_dictCachedResFiles.InvalidIndex() );
		m_dictCachedResFiles.Insert( pszResFileName, pkvResFile );
		return true;
	}

	return false;
}

void BuildGroup::ClearResFileCache()
{
	int nIndex = m_dictCachedResFiles.First();
	while( m_dictCachedResFiles.IsValidIndex( nIndex ) )
	{
		m_dictCachedResFiles[ nIndex ]->deleteThis();
		nIndex = m_dictCachedResFiles.Next( nIndex );
	}
	m_dictCachedResFiles.Purge();
}