//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//
#include "hlfaceposer.h"
#include "faceposertoolwindow.h"
#include "utlvector.h"
#include "tier1/strtools.h"
#include "MDLViewer.h"
#include "choreowidgetdrawhelper.h"
#include "StudioModel.h"
#include "faceposer_models.h"

extern MDLViewer *g_MDLViewer;

static CUtlVector< IFacePoserToolWindow * > g_Tools;
IFacePoserToolWindow *IFacePoserToolWindow::s_pActiveTool = NULL;

bool	IFacePoserToolWindow::s_bToolsCanDraw;
static CUtlVector< IFacePoserToolWindow * > s_NeedRedraw;


IFacePoserToolWindow::IFacePoserToolWindow( char const *toolname, char const *displaynameroot )
{
	m_bAutoProcess = false;
	m_bUseForMainWindowTitle = false;
	SetToolName( toolname );
	m_szPrefix[0]=0;
	m_szSuffix[0]=0;

	SetDisplayNameRoot( displaynameroot );

	g_Tools.AddToTail( this );

	m_nToolFrameCount = 0;
}

mxWindow *IFacePoserToolWindow::GetMxWindow( void )
{
	return dynamic_cast< mxWindow * >( this );
}

IFacePoserToolWindow *IFacePoserToolWindow::GetActiveTool( void )
{
	if ( s_pActiveTool )
		return s_pActiveTool;

	if ( GetToolCount() > 0 )
		return GetTool( 0 );

	return NULL;
}

void IFacePoserToolWindow::SetActiveTool( IFacePoserToolWindow *tool )
{
	if ( tool != s_pActiveTool && s_pActiveTool )
	{
		InvalidateRect( (HWND)s_pActiveTool->GetMxWindow()->getHandle(), NULL, TRUE );
		InvalidateRect( (HWND)tool->GetMxWindow()->getHandle(), NULL, TRUE );
	}
	s_pActiveTool = tool;
}

void IFacePoserToolWindow::Think( float dt )
{
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *name - 
//-----------------------------------------------------------------------------
void IFacePoserToolWindow::SetToolName( char const *name )
{
	Q_strncpy( m_szToolName, name, sizeof( m_szToolName ) );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : char const
//-----------------------------------------------------------------------------
char const *IFacePoserToolWindow::GetToolName( void ) const
{
	return m_szToolName;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *name - 
//-----------------------------------------------------------------------------
void IFacePoserToolWindow::SetDisplayNameRoot( char const *name )
{
	Q_snprintf( m_szDisplayRoot, sizeof( m_szDisplayRoot ), "%s", name );
	ComputeNewTitle();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : char const
//-----------------------------------------------------------------------------
char const *IFacePoserToolWindow::GetDisplayNameRoot( void  ) const
{
	return m_szDisplayRoot;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *suffix - 
//-----------------------------------------------------------------------------
void IFacePoserToolWindow::SetSuffix( char const *suffix )
{
	Q_snprintf( m_szSuffix, sizeof( m_szSuffix ), "%s", suffix );
	ComputeNewTitle();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *prefix - 
//-----------------------------------------------------------------------------
void IFacePoserToolWindow::SetPrefix( char const *prefix )
{
	Q_snprintf( m_szPrefix, sizeof( m_szPrefix ), "%s", prefix );
	ComputeNewTitle();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : char const
//-----------------------------------------------------------------------------
char const *IFacePoserToolWindow::GetWindowTitle( void ) const
{
	return m_szWindowTitle;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : use - 
//-----------------------------------------------------------------------------
void IFacePoserToolWindow::SetUseForMainWindowTitle( bool use )
{
	m_bUseForMainWindowTitle = use;
	if ( use )
	{
		g_MDLViewer->setLabel( m_szWindowTitle );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void IFacePoserToolWindow::ComputeNewTitle( void )
{
	Q_snprintf( m_szWindowTitle, sizeof( m_szWindowTitle ), "%s%s%s", m_szPrefix, m_szDisplayRoot, m_szSuffix );
	if ( GetMxWindow() )
	{
		GetMxWindow()->setLabel( m_szWindowTitle );
	}
	if ( !m_bUseForMainWindowTitle )
		return;

	g_MDLViewer->setLabel( m_szWindowTitle );
}

IFacePoserToolWindow::~IFacePoserToolWindow( void )
{
	g_Tools.FindAndRemove( this );
}

struct ToolTranslate
{
	char const *toolname;
	float		xfrac;
	float		yfrac;
	float		wfrac;
	float		hfrac;
	bool		locked;
};

static ToolTranslate s_ToolTranslate[]=
{
	{ "3D View", 0.0, 0.0, 0.4, 0.5, false },
	{ "ControlPanel", 0.4, 0.0, 0.2, 0.25, false },
	{ "FlexPanel", 0.6, 0.0, 0.4, 0.25, false },
	{ "RampTool", 0.4, 0.25, 0.6, 0.25, false },
	{ "CChoreoView", 0.0, 0.5, 1.0, 0.45, false },
//	{ "Status Window", 0.0, 0.85, 1.0, 0.15, false },
};

static bool TranslateToolPos( char const *toolname, int workspacew, int workspaceh, int& x, int& y, int &w, int &h, bool& locked )
{
	int c = ARRAYSIZE( s_ToolTranslate );

	for ( int i = 0; i < c; ++i )
	{
		ToolTranslate& tt = s_ToolTranslate[ i ];

		if ( !Q_stricmp( toolname, tt.toolname ) )
		{
			x = (int)((float)workspacew * tt.xfrac + 0.5f );
			y = (int)((float)workspaceh * tt.yfrac + 0.5f );
			w = (int)((float)workspacew * tt.wfrac + 0.5f );
			h = (int)((float)workspaceh * tt.hfrac + 0.5f );
			locked = tt.locked;
			return true;
		}
	}

	return false;
}

static int s_nToolCount = 0;

void IFacePoserToolWindow::LoadPosition( void )
{
	bool visible;
	bool locked;
	bool zoomed;
	int x, y, w, h;

	FacePoser_LoadWindowPositions( GetToolName(), visible, x, y, w, h, locked, zoomed );

	if ( w == 0 || h == 0 )
	{
		int idx = g_Tools.Find( this );
		Assert( idx != g_Tools.InvalidIndex() );
		if ( idx == 0 )
		{
			s_nToolCount = 0;
		}

		zoomed = false;
		locked = false;
		visible = true;

		// Just do a simple tiling
		w = g_MDLViewer->w2() * 0.5;
		h = g_MDLViewer->h2() * 0.5;

		x = g_MDLViewer->w2() * 0.25f + s_nToolCount * 20;
		y = s_nToolCount * 20;

		bool translated = TranslateToolPos
		( 
			GetToolName(), 
			g_MDLViewer->w2(), 
			g_MDLViewer->h2(),
			x,
			y,
			w,
			h,
			locked
		);
		if ( !translated )
		{
			++s_nToolCount;
			visible = false;
		}
	}

	GetMxWindow()->setBounds( x, y, w, h );
	if ( locked ^ IsLocked() )
	{
		ToggleLockedState();
	}
	GetMxWindow()->setVisible( visible );
}

void IFacePoserToolWindow::SavePosition( void )
{
	bool visible;
	int xpos, ypos, width, height;

	visible = GetMxWindow()->isVisible();
	xpos = GetMxWindow()->x();
	ypos = GetMxWindow()->y();
	width = GetMxWindow()->w();
	height = GetMxWindow()->h();

	// xpos and ypos are screen space
	POINT pt;
	pt.x = xpos;
	pt.y = ypos;

	// Convert from screen space to relative to client area of parent window so
	//  the setBounds == MoveWindow call will offset to the same location
	if ( GetMxWindow()->getParent() )
	{
		ScreenToClient( (HWND)GetMxWindow()->getParent()->getHandle(), &pt );
		xpos = (short)pt.x;
		ypos = (short)pt.y;
	}

	FacePoser_SaveWindowPositions( GetToolName(), visible, xpos, ypos, width, height, IsLocked(), false );
}
	
int IFacePoserToolWindow::GetToolCount( void )
{
	return g_Tools.Count();
}

IFacePoserToolWindow *IFacePoserToolWindow::GetTool( int index )
{
	return g_Tools[ index ];
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void IFacePoserToolWindow::InitTools( void )
{
	int c = GetToolCount();
	int i;
	for ( i = 0; i < c ; i++ )
	{
		IFacePoserToolWindow *tool = GetTool( i );

		FacePoser_MakeToolWindow( tool->GetMxWindow(), true );
		tool->GetMxWindow()->setLabel( tool->GetWindowTitle() );
	}
}

void IFacePoserToolWindow::ShutdownTools( void )
{
	int c = GetToolCount();
	int i;
	for ( i = 0; i < c ; i++ )
	{
		IFacePoserToolWindow *tool = GetTool( i );
		tool->Shutdown();
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : dt - 
//-----------------------------------------------------------------------------
void IFacePoserToolWindow::ToolThink( float dt )
{
	int c = GetToolCount();
	int i;
	for ( i = 0; i < c ; i++ )
	{
		IFacePoserToolWindow *tool = GetTool( i );
		tool->Think( dt );
	}

	// Don't self animate, all animation driven by thinking of various tools now
	
	if ( !ShouldAutoProcess() )
	{
		c = models->Count();
		for ( i = 0; i < c; i++ )
		{
			StudioModel *m = models->GetStudioModel( i );
			if ( m )
			{
				m->AdvanceFrame ( dt );
			}
		}
	}
}

bool IFacePoserToolWindow::IsLocked( void )
{
	mxWindow *w = GetMxWindow();
	if ( !w )
		return false;

	return !FacePoser_HasWindowStyle( w, WS_SYSMENU );
}

void IFacePoserToolWindow::ToggleLockedState( void )
{
	mxWindow *w = GetMxWindow();
	if ( !w )
		return;

	bool visible = w->isVisible();

	bool islocked = IsLocked();
	if ( islocked )
	{
		FacePoser_MakeToolWindow( w, true );
	}
	else
	{
		FacePoser_RemoveWindowStyle( w, WS_OVERLAPPEDWINDOW ); 
		FacePoser_AddWindowExStyle( w, WS_EX_OVERLAPPEDWINDOW );
	}

	w->setVisible( false );

	// If visible, force it to redraw, etc.
	if ( visible )
	{
		w->setVisible( true );
	}
}

#define LOCK_INSET 2
#define LOCK_SIZE 8

void IFacePoserToolWindow::	GetLockRect( RECT& rc )
{
	mxWindow *w = GetMxWindow();
	Assert( w );
	if ( !w )
		return;

	GetCloseRect( rc );

	OffsetRect( &rc, - ( LOCK_SIZE + 2 * LOCK_INSET ), 0 );
}

void IFacePoserToolWindow::GetCloseRect( RECT& rc )
{
	mxWindow *w = GetMxWindow();
	Assert( w );
	if ( !w )
		return;

	rc.right = w->w2() - LOCK_INSET;
	rc.left = rc.right - LOCK_SIZE;
	rc.top = LOCK_INSET;
	rc.bottom = rc.top + LOCK_SIZE;
}

bool IFacePoserToolWindow::HandleToolEvent( mxEvent *event )
{
	bool handled = false;
	switch ( event->event )
	{
	default:
		break;
	case mxEvent::Close:
		{
			g_MDLViewer->UpdateWindowMenu();
			handled = true;
		}
		break;
	case mxEvent::ParentNotify:
		{
			mxWindow *w = GetMxWindow();
			if ( w )
			{
				HWND wnd = (HWND)w->getHandle();
				SetFocus( wnd );
				SetWindowPos( wnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
				SetActiveTool( this );
			}
			handled = true;
		}
		break;
	case mxEvent::PosChanged:
		{
			SetActiveTool( this );
			mxWindow *w = GetMxWindow();
			if ( w )
			{
				SetFocus( (HWND)w->getHandle() );
			}
			handled = true;
		}
		break;
	case mxEvent::MouseDown:
	case mxEvent::MouseUp:
		{
			bool isup = event->event == mxEvent::MouseUp;

			mxWindow *w = GetMxWindow();

			if ( !isup && w )
			{
				SetFocus( (HWND)w->getHandle() );
				SetWindowPos( (HWND)w->getHandle(), HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);

				SetActiveTool( this );
			}

			if ( w && IsLocked() )
			{
				RECT captionRect;
				captionRect.left = 0;
				captionRect.right = w->w2();
				captionRect.top = 0;
				captionRect.bottom = GetCaptionHeight();

				POINT pt;
				pt.x = (short)event->x;
				pt.y = (short)event->y;

				if ( PtInRect( &captionRect, pt ) )
				{
					handled = !isup;

					// Right button anywhere
					if ( event->buttons & mxEvent::MouseRightButton )
					{
						if ( isup )
						{
							ToggleLockedState();
						}
					}

					// Left button on lock icon
					RECT lockRect, closeRect;
					GetLockRect( lockRect );
					GetCloseRect( closeRect );

					if ( PtInRect( &lockRect, pt ) )
					{
						if ( isup )
						{
							ToggleLockedState();
						}
					}

					if ( PtInRect( &closeRect, pt ) )
					{
						if ( isup )
						{
							w->setVisible( !w->isVisible() );
							g_MDLViewer->UpdateWindowMenu();
						}
					}
				}
			}
		}
		break;
	case mxEvent::NCMouseUp:
		{
			if ( event->buttons & mxEvent::MouseRightButton )
			{	
				ToggleLockedState();
				handled = true;
			}
		}
		break;

	case mxEvent::NCMouseDown:
	case mxEvent::Focus:
		{
			SetActiveTool( this );
			// don't mark handled = true, do this passively
		}
		break;
	}

	return handled;
}

void IFacePoserToolWindow::HandleToolRedraw( CChoreoWidgetDrawHelper& helper )
{
	if ( !IsLocked() )
		return;

	mxWindow *w = GetMxWindow();
	if ( !w )
		return;

	++m_nToolFrameCount;

	RECT lockRect, closeRect;
	GetLockRect( lockRect );
	GetCloseRect( closeRect );

	RECT captionRect;
	helper.GetClientRect( captionRect );
	RECT rcClient = captionRect;
	captionRect.bottom = captionRect.top + LOCK_SIZE + 2 * LOCK_INSET;

	COLORREF textColor = GetSysColor( COLOR_MENUTEXT ); //GetSysColor( COLOR_INACTIVECAPTIONTEXT );

	if ( IsActiveTool() )
	{
		helper.DrawFilledRect( GetSysColor( COLOR_ACTIVECAPTION ), captionRect );
	}
	else
	{
		helper.DrawFilledRect( GetSysColor( COLOR_INACTIVECAPTION ), captionRect );
	}

	captionRect.top += 1;

	InflateRect( &captionRect, -LOCK_INSET, 0 );

	helper.DrawColoredText( "Small Fonts", 9, FW_NORMAL, textColor, captionRect,
		GetWindowTitle() );

	//RECT rcFrame = captionRect;
	//rcFrame.left = rcFrame.right - 50;
	//rcFrame.right = rcFrame.left + 30;
	// helper.DrawColoredText( "Small Fonts", 9, FW_NORMAL, textColor, rcFrame, va( "%i", m_nToolFrameCount ) );

	lockRect.bottom++;
	OffsetRect( &lockRect, 1, 1 );
	helper.DrawColoredTextCharset( "Marlett", 8, FW_NORMAL, SYMBOL_CHARSET, textColor, lockRect, "v" );

	closeRect.bottom++;
	helper.DrawOutlinedRect( textColor, PS_SOLID, 1, closeRect );
	OffsetRect( &closeRect, 1, 1 );
	helper.DrawColoredTextCharset( "Marlett", 8, FW_NORMAL, SYMBOL_CHARSET, textColor, closeRect, "r" );

	rcClient.top += captionRect.bottom;

	helper.StartClipping( rcClient );
}

int IFacePoserToolWindow::GetCaptionHeight( void )
{
	if ( !IsLocked() )
		return 0;

	return LOCK_SIZE + 2 * LOCK_INSET;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : autoprocess - 
//-----------------------------------------------------------------------------
void IFacePoserToolWindow::SetAutoProcess( bool autoprocess )
{
	m_bAutoProcess = autoprocess;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool IFacePoserToolWindow::GetAutoProcess( void ) const
{
	return m_bAutoProcess;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool IFacePoserToolWindow::IsActiveTool( void )
{
	if ( this == s_pActiveTool )
		return true;
	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool IFacePoserToolWindow::IsAnyToolScrubbing( void )
{
	int c = GetToolCount();
	int i;
	for ( i = 0; i < c ; i++ )
	{
		IFacePoserToolWindow *tool = GetTool( i );
		if ( tool->IsScrubbing() )
			return true;
	}

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool IFacePoserToolWindow::IsAnyToolProcessing( void )
{
	int c = GetToolCount();
	int i;
	for ( i = 0; i < c ; i++ )
	{
		IFacePoserToolWindow *tool = GetTool( i );
		if ( tool->IsProcessing() )
			return true;
	}

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool IFacePoserToolWindow::ShouldAutoProcess( void )
{
	IFacePoserToolWindow *tool = GetActiveTool();
	if ( !tool )
		return false;

	return tool->GetAutoProcess();
}

void IFacePoserToolWindow::EnableToolRedraw( bool enabled )
{
	MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );

	s_bToolsCanDraw = enabled;

	if ( s_bToolsCanDraw )
	{
		int c = s_NeedRedraw.Count();
		int i;
		for ( i = 0; i < c; i++ )
		{
			IFacePoserToolWindow *tool = s_NeedRedraw[ i ];
			tool->GetMxWindow()->redraw();
		}

		s_NeedRedraw.Purge();
	}
}

bool IFacePoserToolWindow::ToolCanDraw()
{
	if ( !s_bToolsCanDraw )
	{
		if ( s_NeedRedraw.Find( this ) == s_NeedRedraw.InvalidIndex() )
		{
			s_NeedRedraw.AddToTail( this );
		}

		return false;
	}

	return true;
}

void IFacePoserToolWindow::OnModelChanged()
{
}

void IFacePoserToolWindow::ModelChanged()
{
	int c = GetToolCount();
	int i;
	for ( i = 0; i < c ; i++ )
	{
		IFacePoserToolWindow *tool = GetTool( i );
		tool->OnModelChanged();
	}
}