//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Core implementation of vgui
//
// $NoKeywords: $
//===========================================================================//


#if defined( WIN32 ) && !defined( _X360 )
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif

#include "VGuiMatSurface/IMatSystemSurface.h"
#include <vgui/VGUI.h>
#include <vgui/Dar.h>
#include <vgui/IInputInternal.h>
#include <vgui/IPanel.h>
#include <vgui/ISystem.h>
#include <vgui/ISurface.h>
#include <vgui/IVGui.h>
#include <vgui/IClientPanel.h>
#include <vgui/IScheme.h>
#include <KeyValues.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <stdarg.h>
#ifdef OSX
#include <malloc/malloc.h>
#else
#include <malloc.h>
#endif
#include <tier0/dbg.h>
#include <tier1/utlhandletable.h>
#include "vgui_internal.h"
#include "VPanel.h"
#include "IMessageListener.h"
#include "tier3/tier3.h"
#include "utllinkedlist.h"
#include "utlpriorityqueue.h"
#include "utlvector.h"
#include "tier0/vprof.h"
#include "tier0/icommandline.h"

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

#undef GetCursorPos // protected_things.h defines this, and it makes it so we can't access g_pInput->GetCursorPos.

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


using namespace vgui;
static const int WARN_PANEL_NUMBER = 32768; // in DEBUG if more panels than this are created then throw an Assert, helps catch panel leaks


//-----------------------------------------------------------------------------
// Purpose: Single item in the message queue
//-----------------------------------------------------------------------------
struct MessageItem_t
{
	KeyValues *_params; // message data
						// _params->GetName() is the message name

	HPanel _messageTo;	// the panel this message is to be sent to
	HPanel _from;		// the panel this message is from (if any)
	float _arrivalTime;	// time at which the message should be passed on to the recipient

	int _messageID;		// incrementing message index
};


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool PriorityQueueComp(const MessageItem_t& x, const MessageItem_t& y) 
{
	if (x._arrivalTime > y._arrivalTime)
	{
		return true;
	}
	else if (x._arrivalTime < y._arrivalTime)
	{
		return false;
	}

	// compare messageID's to ensure we have the messages in the correct order
	return (x._messageID > y._messageID);
}



//-----------------------------------------------------------------------------
// Purpose: Implementation of core vgui functionality
//-----------------------------------------------------------------------------
class CVGui : public CTier3AppSystem< IVGui >
{
	typedef CTier3AppSystem< IVGui > BaseClass;

public:
	CVGui();
	~CVGui();

//-----------------------------------------------------------------------------
	// SRC specific stuff
	// Here's where the app systems get to learn about each other 
	virtual bool Connect( CreateInterfaceFn factory );
	virtual void Disconnect();

	// Here's where systems can access other interfaces implemented by this object
	// Returns NULL if it doesn't implement the requested interface
	virtual void *QueryInterface( const char *pInterfaceName );

	// Init, shutdown
	virtual InitReturnVal_t Init();
	virtual void Shutdown();
	// End of specific interface
//-----------------------------------------------------------------------------


	virtual void RunFrame();

	virtual void Start()
	{
		m_bRunning = true;
	}

	// signals vgui to Stop running
	virtual void Stop()
	{
		m_bRunning = false;
	}

	// returns true if vgui is current active
	virtual bool IsRunning()
	{
		return m_bRunning;
	}

	virtual void ShutdownMessage(unsigned int shutdownID);

	// safe-pointer handle methods
	virtual VPANEL AllocPanel();
	virtual void FreePanel(VPANEL ipanel);
	virtual HPanel PanelToHandle(VPANEL panel);
	virtual VPANEL HandleToPanel(HPanel index);
	virtual void MarkPanelForDeletion(VPANEL panel);

	virtual void AddTickSignal(VPANEL panel, int intervalMilliseconds = 0);
	virtual void AddTickSignalToHead( VPANEL panel, int intervalMilliseconds = 0 ) OVERRIDE;
	virtual void RemoveTickSignal(VPANEL panel );


	// message pump method
	virtual void PostMessage(VPANEL target, KeyValues *params, VPANEL from, float delaySeconds = 0.0f);

	virtual void SetSleep( bool state ) { m_bDoSleep = state; };
	virtual bool GetShouldVGuiControlSleep() { return m_bDoSleep; }

	virtual void DPrintf(const char *format, ...);
	virtual void DPrintf2(const char *format, ...);
	virtual void SpewAllActivePanelNames();

	// Creates/ destroys vgui contexts, which contains information
	// about which controls have mouse + key focus, for example.
	virtual HContext CreateContext();
	virtual void DestroyContext( HContext context ); 

	// Associates a particular panel with a vgui context
	// Associating NULL is valid; it disconnects the panel from the context
	virtual void AssociatePanelWithContext( HContext context, VPANEL pRoot );

	// Activates a particular input context, use DEFAULT_VGUI_CONTEXT
	// to get the one normally used by VGUI
	virtual void ActivateContext( HContext context );

	// enables VR mode
	virtual void SetVRMode( bool bVRMode ) OVERRIDE
	{
		m_bVRMode = bVRMode;
	}
	virtual bool GetVRMode() OVERRIDE
	{
		return m_bVRMode;
	}

	bool IsDispatchingMessages( void )
	{
		return m_InDispatcher;
	}

private:
	// VGUI contexts
	struct Context_t
	{
		HInputContext m_hInputContext;
	};

	struct Tick_t
	{
		VPanel	*panel;
		int		interval;
		int		nexttick;
		bool	bMarkDeleted;
		// Debugging
		char	panelname[ 64 ];
	};

	Tick_t* CreateNewTick( VPANEL panel, int intervalMilliseconds );

	// Returns the current context
	Context_t *GetContext( HContext context );

	void PanelCreated(VPanel *panel);
	void PanelDeleted(VPanel *panel);
	bool DispatchMessages();
	void DestroyAllContexts( );
	void ClearMessageQueues();
	inline bool IsReentrant() const 
	{ 
		return m_nReentrancyCount > 0; 
	}

	// safe panel handle stuff
	CUtlHandleTable< VPanel, 20 > m_HandleTable;
	int m_iCurrentMessageID;

	bool m_bRunning : 1;
	bool m_bDoSleep : 1;
	bool m_InDispatcher : 1;
	bool m_bDebugMessages : 1;
	bool m_bVRMode : 1;
	bool m_bCanRemoveTickSignal : 1;
	int m_nReentrancyCount;

	CUtlVector< Tick_t * > m_TickSignalVec;
	CUtlLinkedList< Context_t >	m_Contexts;

	HContext m_hContext;
	Context_t m_DefaultContext;

#ifdef DEBUG
	int m_iDeleteCount, m_iDeletePanelCount;
#endif

	// message queue. holds all vgui messages generated by windows events
	CUtlLinkedList<MessageItem_t, ushort> m_MessageQueue;

	// secondary message queue, holds all vgui messages generated by vgui
	CUtlLinkedList<MessageItem_t, ushort> m_SecondaryQueue;

	// timing queue, holds all the messages that have to arrive at a specified time
	CUtlPriorityQueue<MessageItem_t> m_DelayedMessageQueue;
};

CVGui g_VGui;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CVGui, IVGui, VGUI_IVGUI_INTERFACE_VERSION, g_VGui);

bool IsDispatchingMessageQueue( void )
{
	return g_VGui.IsDispatchingMessages();
}

namespace vgui
{
IVGui *g_pIVgui = &g_VGui;
}

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CVGui::CVGui() : m_DelayedMessageQueue(0, 4, PriorityQueueComp)
{
	m_bRunning = false;
	m_InDispatcher = false;
	m_bDebugMessages = false;
	m_bDoSleep = true;
	m_bVRMode = false;
	m_bCanRemoveTickSignal = true;
	m_nReentrancyCount = 0;
	m_hContext = DEFAULT_VGUI_CONTEXT;
	m_DefaultContext.m_hInputContext = DEFAULT_INPUT_CONTEXT;
}

//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CVGui::~CVGui()
{
#ifdef _DEBUG
	int nCount = m_HandleTable.GetHandleCount();
	int nActualCount = 0;
	for ( int i = 0; i < nCount; ++i )
	{
		UtlHandle_t h = m_HandleTable.GetHandleFromIndex( i );
		if ( m_HandleTable.IsHandleValid( h ) )
		{
			++nActualCount;
		}
	}

	if ( nActualCount > 0 )
	{
		Msg("Memory leak: panels left in CVGui::m_PanelList: %d\n", nActualCount );
	}
#endif // _DEBUG
}

//-----------------------------------------------------------------------------
// Purpose: Dumps out list of all active panels
//-----------------------------------------------------------------------------
void CVGui::SpewAllActivePanelNames()
{
	int nCount = m_HandleTable.GetHandleCount();
	for ( int i = 0; i < nCount; ++i )
	{
		UtlHandle_t h = m_HandleTable.GetHandleFromIndex( i );
		if ( m_HandleTable.IsHandleValid( h ) )
		{
			VPanel *pPanel = m_HandleTable.GetHandle( h );
			Msg("\tpanel '%s' of type '%s' leaked\n", g_pIPanel->GetName( (VPANEL)pPanel ), ((VPanel *)pPanel)->GetClassName());
		}
	}
}


//-----------------------------------------------------------------------------
// Creates/ destroys "input" contexts, which contains information
// about which controls have mouse + key focus, for example.
//-----------------------------------------------------------------------------
HContext CVGui::CreateContext()
{
	HContext i = m_Contexts.AddToTail();
	m_Contexts[i].m_hInputContext = g_pInput->CreateInputContext();
	return i;
}

void CVGui::DestroyContext( HContext context )
{
	Assert( context != DEFAULT_VGUI_CONTEXT );

	if ( m_hContext == context )
	{
		ActivateContext( DEFAULT_VGUI_CONTEXT );
	}

	g_pInput->DestroyInputContext( GetContext(context)->m_hInputContext );
	m_Contexts.Remove(context);
}

void CVGui::DestroyAllContexts( )
{
	HContext next;
	HContext i = m_Contexts.Head();
	while (i != m_Contexts.InvalidIndex())
	{
		next = m_Contexts.Next(i);
		DestroyContext( i );
		i = next;
	}
}


//-----------------------------------------------------------------------------
// Returns the current context
//-----------------------------------------------------------------------------
CVGui::Context_t *CVGui::GetContext( HContext context )
{
	if (context == DEFAULT_VGUI_CONTEXT)
		return &m_DefaultContext;
	return &m_Contexts[context];
}


//-----------------------------------------------------------------------------
// Associates a particular panel with a context
// Associating NULL is valid; it disconnects the panel from the context
//-----------------------------------------------------------------------------
void CVGui::AssociatePanelWithContext( HContext context, VPANEL pRoot )
{
	Assert( context != DEFAULT_VGUI_CONTEXT );
	g_pInput->AssociatePanelWithInputContext( GetContext(context)->m_hInputContext, pRoot );
}


//-----------------------------------------------------------------------------
// Activates a particular context, use DEFAULT_VGUI_CONTEXT
// to get the one normally used by VGUI
//-----------------------------------------------------------------------------
void CVGui::ActivateContext( HContext context )
{
	Assert( (context == DEFAULT_VGUI_CONTEXT) || m_Contexts.IsValidIndex(context) );

	if ( m_hContext != context )
	{
		// Clear out any messages queues that may be full...
		if ( !IsReentrant() )
		{
			DispatchMessages();
		}

		m_hContext = context;
		g_pInput->ActivateInputContext( GetContext(m_hContext)->m_hInputContext ); 

		if ( context != DEFAULT_VGUI_CONTEXT && !IsReentrant() )
		{
			g_pInput->RunFrame( );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Runs a single vgui frame, pumping all message to panels
//-----------------------------------------------------------------------------
void CVGui::RunFrame() 
{
	// NOTE: This can happen when running in Maya waiting for modal dialogs
	bool bIsReentrant = m_InDispatcher;
	if ( bIsReentrant )
	{
		++m_nReentrancyCount;
	}

#ifdef DEBUG
//  memory allocation debug helper
//	DPrintf( "Delete Count:%i,%i\n", m_iDeleteCount, m_iDeletePanelCount );
//	m_iDeleteCount = 	m_iDeletePanelCount = 0;
#endif

	// this will generate all key and mouse events as well as make a real repaint
	{
		VPROF( "surface()->RunFrame()" );
		g_pSurface->RunFrame();
	}

	// give the system a chance to process
	{
		VPROF( "system()->RunFrame()" );
		g_pSystem->RunFrame();
	}

	// update cursor positions
	if ( IsPC() && !IsReentrant() )
	{
		VPROF( "update cursor positions" );
		int cursorX, cursorY;
		g_pInput->GetCursorPosition(cursorX, cursorY);

		// this does the actual work given a x,y and a surface
		g_pInput->UpdateMouseFocus(cursorX, cursorY);

	}

	if ( !bIsReentrant )
	{
		VPROF( "input()->RunFrame()" );
		g_pInput->RunFrame();
	}

	// messenging
	if ( !bIsReentrant )
	{
		VPROF( "messaging" );

		// send all the messages waiting in the queue
		DispatchMessages();

		// Do the OnTicks before purging messages, since in previous code they were posted after dispatch and wouldn't hit
		//  until next frame
		int time = g_pSystem->GetTimeMillis();

		m_bCanRemoveTickSignal = false;

		tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Ticks", __FUNCTION__ );
		// directly invoke tick all who asked to be ticked
		int count = m_TickSignalVec.Count();
		for (int i = count - 1; i >= 0; i-- )
		{
			Tick_t *t = m_TickSignalVec[i];
			if ( t->bMarkDeleted )
				continue;

			if ( t->interval != 0 )
			{
				if ( time < t->nexttick )
					continue;

				t->nexttick = time + t->interval;
			}
			
			t->panel->Client()->OnTick();
			tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Ticks: %s", __FUNCTION__, t->panel->Client()->GetName() );
		}

		m_bCanRemoveTickSignal = true;

		// get count again. panels could be added to tick vector in OnTick
		count = m_TickSignalVec.Count();

		// Remove all panels that tried to remove tick in OnTick
		for (int i = count - 1; i >= 0; i-- )
		{
			Tick_t *t = m_TickSignalVec[i];
			if ( t->bMarkDeleted )
			{
				m_TickSignalVec.Remove( i );
				delete t;
			}
		}
	}

	{
		VPROF( "SolveTraverse" );
		// make sure the hierarchy is up to date
		g_pSurface->SolveTraverse(g_pSurface->GetEmbeddedPanel());
		g_pSurface->ApplyChanges();
#ifdef WIN32
		Assert( IsX360() || ( IsPC() && _heapchk() == _HEAPOK ) );
#endif
	}

	if ( bIsReentrant )
	{
		--m_nReentrancyCount;
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
VPANEL CVGui::AllocPanel()
{
#ifdef DEBUG
	m_iDeleteCount++;
#endif

	VPanel *panel = new VPanel;
	PanelCreated(panel);
	return (VPANEL)panel;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CVGui::FreePanel(VPANEL ipanel)
{
	PanelDeleted((VPanel *)ipanel);
	delete (VPanel *)ipanel;
#ifdef DEBUG
	m_iDeleteCount--;
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Returns the safe index of the panel
//-----------------------------------------------------------------------------
HPanel CVGui::PanelToHandle(VPANEL panel)
{
	if (panel)
		return ((VPanel*)panel)->GetHPanel();
	return INVALID_PANEL;
}


//-----------------------------------------------------------------------------
// Purpose: Returns the panel at the specified index
//-----------------------------------------------------------------------------
VPANEL CVGui::HandleToPanel(HPanel index)
{
	if ( !m_HandleTable.IsHandleValid( index ) )
	{
		return NULL;
	}
	return (VPANEL)m_HandleTable.GetHandle( (UtlHandle_t)index );
}


//-----------------------------------------------------------------------------
// Purpose: Called whenever a panel is constructed
//-----------------------------------------------------------------------------
void CVGui::PanelCreated(VPanel *panel)
{
	UtlHandle_t h = m_HandleTable.AddHandle();
	m_HandleTable.SetHandle( h, panel );

#if DUMP_PANEL_LIST
	int nCount = m_HandleTable.GetHandleCount();
	int nActualCount = 0;
	for ( int i = 0; i < nCount; ++i )
	{
		UtlHandle_t h = m_HandleTable.GetHandleFromIndex( i );
		if ( m_HandleTable.IsHandleValid( h ) )
		{
			++nActualCount;
		}
	}

	if ( nActualCount >= WARN_PANEL_NUMBER )
	{
		FILE *file1 = fopen("panellist.txt", "w");
		if (file1 != NULL)
		{
			fprintf(file1, "Too many panels...listing them all.\n");
			int panelIndex;
			for (panelIndex = 0; panelIndex < nCount; ++panelIndex)
			{
				UtlHandle_t h = m_HandleTable.GetHandleFromIndex( i );
				VPanel *pPanel = m_HandleTable.GetHandle( h );
				IClientPanel *ipanel = ( pPanel ) ? pPanel->Client() : NULL;
				if ( ipanel )
					fprintf(file1, "panel %d: name: %s   classname: %s\n", panelIndex, ipanel->GetName(), ipanel->GetClassName());
				else
					fprintf(file1, "panel %d: can't get ipanel\n", panelIndex);
			}

			fclose(file1);
		}
	}

	Assert( nActualCount < WARN_PANEL_NUMBER );
#endif // DUMP_PANEL_LIST

	((VPanel *)panel)->SetHPanel( h );

	g_pSurface->AddPanel((VPANEL)panel);
}

//-----------------------------------------------------------------------------
// Purpose: instantly stops the app from pointing to the focus'd object
//			used when an object is being deleted
//-----------------------------------------------------------------------------
void CVGui::PanelDeleted(VPanel *focus)
{
	Assert( focus );
	g_pSurface->ReleasePanel((VPANEL)focus);
	g_pInput->PanelDeleted((VPANEL)focus);

	// remove from safe handle list
	UtlHandle_t h = ((VPanel *)focus)->GetHPanel();

	Assert( m_HandleTable.IsHandleValid(h) );
	if ( m_HandleTable.IsHandleValid(h) )
	{
		m_HandleTable.RemoveHandle( h );
	}

	((VPanel *)focus)->SetHPanel( INVALID_PANEL );

	// remove from tick signal dar
	RemoveTickSignal( (VPANEL)focus );
}

//-----------------------------------------------------------------------------
// Purpose: Creates or updates a tick signal for a panel.  Returns NULL if already ticking.
//-----------------------------------------------------------------------------
CVGui::Tick_t* CVGui::CreateNewTick( VPANEL panel, int intervalMilliseconds )
{
	Tick_t *t;
	// See if it's already in list
	int count = m_TickSignalVec.Count();
	for (int i = 0; i < count; i++ )
	{
		Tick_t *t = m_TickSignalVec[i];
		if ( t->panel == (VPanel *)panel )
		{
			// Go ahead and update intervals
			t->interval = intervalMilliseconds;
			t->nexttick = g_pSystem->GetTimeMillis() + t->interval;

			// Somebody added this panel back to the tick list, don't delete it
			t->bMarkDeleted = false;
			return NULL;
		}
	}

	// Add to list
	t = new Tick_t;

	t->panel = (VPanel *)panel;
	t->interval = intervalMilliseconds;
	t->nexttick = g_pSystem->GetTimeMillis() + t->interval;
	t->bMarkDeleted = false;

	if ( strlen( ((VPanel *)panel)->Client()->GetName() ) > 0 )
	{
		strncpy( t->panelname, ((VPanel *)panel)->Client()->GetName(), sizeof( t->panelname ) );
	}
	else
	{
		strncpy( t->panelname, ((VPanel *)panel)->Client()->GetClassName(), sizeof( t->panelname ) );
	}

	return t;
}

//-----------------------------------------------------------------------------
// Purpose: Adds the panel to the tail of a tick signal list, so the panel receives a message every frame
//-----------------------------------------------------------------------------
void CVGui::AddTickSignal(VPANEL panel, int intervalMilliseconds /*=0*/ )
{
	Tick_t* t = CreateNewTick( panel, intervalMilliseconds );

	if ( t )
	{
		// add the element to the end list 
		m_TickSignalVec.AddToTail( t );
		// panel is removed from list when deleted
	}
}

//-----------------------------------------------------------------------------
// Purpose: Adds the panel to the head of a tick signal list, so the panel receives a message every frame
//-----------------------------------------------------------------------------
void CVGui::AddTickSignalToHead(VPANEL panel, int intervalMilliseconds /*=0*/ )
{
	Tick_t* t = CreateNewTick( panel, intervalMilliseconds );

	if ( t )
	{
		// simply add the element to the head list 
		m_TickSignalVec.AddToHead( t );
		// panel is removed from list when deleted
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CVGui::RemoveTickSignal( VPANEL panel )
{
	VPanel *search = (VPanel *)panel;

	// remove from tick signal dar
	int count = m_TickSignalVec.Count();

	for (int i = 0; i < count; i++ )
	{
		Tick_t *tick = m_TickSignalVec[i];
		if ( tick->panel == search )
		{
			if ( m_bCanRemoveTickSignal )
			{
				m_TickSignalVec.Remove( i );
				delete tick;
			}
			else
			{
				tick->bMarkDeleted = true;
			}
			
			return;
		}
	}
}



//-----------------------------------------------------------------------------
// Purpose: message pump
//			loops through and sends all active messages
//			note that more messages may be posted during the process
//-----------------------------------------------------------------------------
bool CVGui::DispatchMessages()
{
	int time = g_pSystem->GetTimeMillis();

	m_InDispatcher = true;
	bool doneWork = (m_MessageQueue.Count() > 12);

	bool bUsingDelayedQueue = (m_DelayedMessageQueue.Count() > 0);

	// Need two passes because we send the mouse move message after all
	// other messages are done, but the mouse move message may itself generate
	// some more messages
	int nPassCount = 0;
	while ( nPassCount < 2 )
	{
		while (m_MessageQueue.Count() > 0 || (m_SecondaryQueue.Count() > 0) || bUsingDelayedQueue)
		{
			// get the first message
			MessageItem_t *messageItem = NULL;
			int messageIndex = 0;

			// use the secondary queue until it empties. empty it after each message in the
			// primary queue. this makes primary messages completely resolve 
			bool bUsingSecondaryQueue = (m_SecondaryQueue.Count() > 0);
			if (bUsingSecondaryQueue)
			{
				doneWork = true;
				messageIndex = m_SecondaryQueue.Head();
				messageItem = &m_SecondaryQueue[messageIndex];
			}
			else if (bUsingDelayedQueue)
			{
				if (m_DelayedMessageQueue.Count() >0)
				{
					messageItem = (MessageItem_t*)&m_DelayedMessageQueue.ElementAtHead();
				}
				if (!messageItem || messageItem->_arrivalTime > time)
				{
					// no more items in the delayed message queue, move to the system queue
					bUsingDelayedQueue = false;
					continue;
				}
			}
			else
			{
				messageIndex = m_MessageQueue.Head();
				messageItem = &m_MessageQueue[messageIndex];
			}

			// message debug code 

			if ( m_bDebugMessages )
			{
				const char *qname = bUsingSecondaryQueue ? "Secondary" : "Primary";

				if (strcmp(messageItem->_params->GetName(), "Tick")
					&& strcmp(messageItem->_params->GetName(), "MouseFocusTicked") 
					&& strcmp(messageItem->_params->GetName(), "KeyFocusTicked")
					&& strcmp(messageItem->_params->GetName(), "CursorMoved"))
				{
					if (!stricmp(messageItem->_params->GetName(), "command"))
					{
						g_pIVgui->DPrintf2( "%s Queue dispatching command( %s, %s -- %i )\n", qname, messageItem->_params->GetName(), messageItem->_params->GetString("command"), messageItem->_messageID );
					}
					else
					{
						g_pIVgui->DPrintf2( "%s Queue dispatching( %s -- %i )\n", qname ,messageItem->_params->GetName(), messageItem->_messageID );
					}
				}
			}

			// send it
			KeyValues *params = messageItem->_params;

			// Deal with special internal cursor movement messages
			if ( messageItem->_messageTo == 0xFFFFFFFF )
			{
				if ( !Q_stricmp( params->GetName(), "SetCursorPosInternal" ) )
				{
					int nXPos = params->GetInt( "xpos", 0 );
					int nYPos = params->GetInt( "ypos", 0 );
					g_pInput->UpdateCursorPosInternal( nXPos, nYPos );
				}
			}
#ifdef _X360
			else if ( messageItem->_messageTo == 0xFFFFFFFE ) // special tag to always give message to the active key focus
			{
				VPanel *vto = (VPanel *) g_pInput->GetCalculatedFocus();
				if (vto)
				{
					vto->SendMessage(params, g_pIVgui->HandleToPanel(messageItem->_from));
				}
			}
#endif
			else
			{
				VPanel *vto = (VPanel *)g_pIVgui->HandleToPanel(messageItem->_messageTo);
				if (vto)
				{
					//			Msg("Sending message: %s to %s\n", params ? params->GetName() : "\"\"", vto->GetName() ? vto->GetName() : "\"\"");
					vto->SendMessage(params, g_pIVgui->HandleToPanel(messageItem->_from));
				}
			}

			// free the keyvalues memory
			// we can't reference the messageItem pointer anymore since the queue might have moved in memory
			if (params)
			{
				params->deleteThis();
			}

			// remove it from the queue
			if (bUsingSecondaryQueue)
			{
				m_SecondaryQueue.Remove(messageIndex);
			}
			else if (bUsingDelayedQueue)
			{
				m_DelayedMessageQueue.RemoveAtHead();
			}
			else
			{
				m_MessageQueue.Remove(messageIndex);
			}
		}

		++nPassCount;
		if ( nPassCount == 1 )
		{
			// Specifically post the current mouse position as a message
			g_pInput->PostCursorMessage();
		}
	}

	// Make sure the windows cursor is in the right place after processing input 
	// Needs to be done here because a message provoked by the cursor moved
	// message may move the cursor also
	g_pInput->HandleExplicitSetCursor( );

	m_InDispatcher = false;
	return doneWork;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CVGui::MarkPanelForDeletion(VPANEL panel)
{
	PostMessage(panel, new KeyValues("Delete"), NULL);
}

//-----------------------------------------------------------------------------
// Purpose: Adds a message to the queue to be sent to a user
//-----------------------------------------------------------------------------
void CVGui::PostMessage(VPANEL target, KeyValues *params, VPANEL from, float delay)
{
	// Ignore all messages in re-entrant mode
	if ( IsReentrant() )
	{
		Assert( 0 );
		if (params)
		{
			params->deleteThis();
		}
		return;
	}

	if (!target)
	{
		if (params)
		{
			params->deleteThis();
		}
		return;
	}

	MessageItem_t messageItem;
	 
#ifdef _X360
	// Special coded target that will always send the message to the key focus
	// this is needed since we might send two messages on a tice, and the first
	// could change the focus.
	if( target == (VPANEL) MESSAGE_CURRENT_KEYFOCUS )
	{
		messageItem._messageTo = 0xFFFFFFFE;
	}
	else
#endif	
	{
		messageItem._messageTo = (target != (VPANEL) MESSAGE_CURSOR_POS ) ? g_pIVgui->PanelToHandle(target) : 0xFFFFFFFF;
	}
	messageItem._params = params;
	Assert(params->GetName());
	messageItem._from = g_pIVgui->PanelToHandle(from);
	messageItem._arrivalTime = 0;
	messageItem._messageID = m_iCurrentMessageID++;

	/* message debug code
	//if ( stricmp(messageItem._params->GetName(),"CursorMoved") && stricmp(messageItem._params->GetName(),"KeyFocusTicked"))
	{
		g_pIVgui->DPrintf2( "posting( %s -- %i )\n", messageItem._params->GetName(), messageItem._messageID );
	}
	*/
				
	// add the message to the correct message queue
	if (delay > 0.0f)
	{
		messageItem._arrivalTime = g_pSystem->GetTimeMillis() + (delay * 1000);
		m_DelayedMessageQueue.Insert(messageItem);
	}
	else if (m_InDispatcher)
	{
		m_SecondaryQueue.AddToTail(messageItem);
	}
	else
	{
		m_MessageQueue.AddToTail(messageItem);
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CVGui::ShutdownMessage(unsigned int shutdownID)
{
	// broadcast Shutdown to all the top level windows, and see if any take notice
	VPANEL panel = g_pSurface->GetEmbeddedPanel();
	for (int i = 0; i < ((VPanel *)panel)->GetChildCount(); i++)
	{
		g_pIVgui->PostMessage((VPANEL)((VPanel *)panel)->GetChild(i), new KeyValues("ShutdownRequest", "id", shutdownID), NULL);
	}

	// post to the top level window as well
	g_pIVgui->PostMessage(panel, new KeyValues("ShutdownRequest", "id", shutdownID), NULL);
}

//-----------------------------------------------------------------------------
// Purpose: Clears all the memory queues and free's their memory
//-----------------------------------------------------------------------------
void CVGui::ClearMessageQueues()
{
	Assert(!m_InDispatcher);

	{FOR_EACH_LL( m_MessageQueue, i )
	{
		if (m_MessageQueue[i]._params)
		{
			m_MessageQueue[i]._params->deleteThis();
		}
	}}
	m_MessageQueue.RemoveAll();

	// secondary message queue, holds all vgui messages generated by vgui
	{FOR_EACH_LL( m_SecondaryQueue, i )
	{
		if (m_SecondaryQueue[i]._params)
		{
			m_SecondaryQueue[i]._params->deleteThis();
		}
	}}
	m_SecondaryQueue.RemoveAll();

	// timing queue, holds all the messages that have to arrive at a specified time
	while (m_DelayedMessageQueue.Count() > 0)
	{
		if (m_DelayedMessageQueue.ElementAtHead()._params)
		{
			m_DelayedMessageQueue.ElementAtHead()._params->deleteThis();
		}
		m_DelayedMessageQueue.RemoveAtHead();
	}
}

/*
static void*(*staticMalloc)(size_t size)=malloc;
static void(*staticFree)(void* memblock)=free;

static int g_iMemoryBlocksAllocated = 0;

void *operator new(size_t size)
{
	g_iMemoryBlocksAllocated += 1;
	return staticMalloc(size);
}

void operator delete(void* memblock)
{
	if (!memblock)
		return;

	g_iMemoryBlocksAllocated -= 1;

	if (g_iMemoryBlocksAllocated < 0)
	{
		int x = 3;
	}

	staticFree(memblock);
}

void *operator new [] (size_t size)
{
	return staticMalloc(size);
}

void operator delete [] (void *pMem)
{
	staticFree(pMem);
}
*/

void CVGui::DPrintf(const char* format,...)
{
	char    buf[2048];
	va_list argList;

	va_start(argList,format);
	Q_vsnprintf(buf,sizeof( buf ), format,argList);
	va_end(argList);

#ifdef WIN32
	::OutputDebugString(buf);
#else
	Msg( "%s", buf );
#endif
}

void CVGui::DPrintf2(const char* format,...)
{
	char    buf[2048];
	va_list argList;
	static int ctr=0;

	Q_snprintf(buf,sizeof( buf ), "%d:",ctr++ );

	va_start(argList,format);
	Q_vsnprintf(buf+strlen(buf),sizeof( buf )-strlen(buf),format,argList);
	va_end(argList);

#ifdef WIN32
	::OutputDebugString(buf);
#else
	Msg( "%s", buf );
#endif
}

void vgui::vgui_strcpy(char* dst,int dstLen,const char* src)
{
	Assert(dst!=null);
	Assert(dstLen>=0);
	Assert(src!=null);

	int srcLen=strlen(src)+1;
	if(srcLen>dstLen)
	{
		srcLen=dstLen;
	}

	memcpy(dst,src,srcLen-1);
	dst[srcLen-1]=0;
}

//-----------------------------------------------------------------------------
	// HL2/TFC specific stuff
//-----------------------------------------------------------------------------
// Here's where the app systems get to learn about each other 
//-----------------------------------------------------------------------------
bool CVGui::Connect( CreateInterfaceFn factory )
{
	if ( !BaseClass::Connect( factory ) )
		return false;

	if ( !g_pFullFileSystem || !g_pVGuiLocalize )
	{
		Warning( "IVGui unable to connect to required interfaces!\n" );
		return false;
	}

	return VGui_InternalLoadInterfaces( &factory, 1 );
}

void CVGui::Disconnect()
{
	// FIXME: Blat out interface pointers
	BaseClass::Disconnect();
}


//-----------------------------------------------------------------------------
// Init, shutdown
//-----------------------------------------------------------------------------
InitReturnVal_t CVGui::Init()
{
	m_hContext = DEFAULT_VGUI_CONTEXT;
	m_bDebugMessages = CommandLine()->FindParm( "-vguimessages" ) ? true : false;

	InitReturnVal_t nRetVal = BaseClass::Init();
	if ( nRetVal != INIT_OK )
		return nRetVal;

	return INIT_OK;
}

void CVGui::Shutdown()
{
	g_pSystem->SaveUserConfigFile();

	DestroyAllContexts();
	ClearMessageQueues();

	g_pSystem->Shutdown();
	g_pScheme->Shutdown(true);

	if ( !g_pSurface->QueryInterface( MAT_SYSTEM_SURFACE_INTERFACE_VERSION ) )
	{
		g_pSurface->Shutdown();
	}

	BaseClass::Shutdown();
}

//-----------------------------------------------------------------------------
// Here's where systems can access other interfaces implemented by this object
// Returns NULL if it doesn't implement the requested interface
//-----------------------------------------------------------------------------
void *CVGui::QueryInterface( const char *pInterfaceName )
{
	// FIXME: Should this go here?
	// Access other global interfaces exposed by this system...
	CreateInterfaceFn vguiFactory = Sys_GetFactoryThis();
	return vguiFactory( pInterfaceName, NULL );
}
