//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include <mxtk/mx.h>
#include <stdio.h>
#include "resource.h"
#include "EventProperties.h"
#include "mdlviewer.h"
#include "choreoevent.h"
#include "choreoscene.h"
#include "mathlib/mathlib.h"
#include "choreochannel.h"
#include "choreoactor.h"
#include "filesystem.h"
#include "scriplib.h"

#include "eventproperties_expression.h"
#include "eventproperties_face.h"
#include "eventproperties_firetrigger.h"
#include "eventproperties_flexanimation.h"
#include "eventproperties_generic.h"
#include "eventproperties_gesture.h"
#include "eventproperties_interrupt.h"
#include "eventproperties_lookat.h"
#include "eventproperties_moveto.h"
#include "eventproperties_permitresponses.h"
#include "eventproperties_sequence.h"
#include "eventproperties_speak.h"
#include "eventproperties_subscene.h"

void CBaseEventPropertiesDialog::PopulateTagList( CEventParams *params )
{
	CChoreoScene *scene = params->m_pScene;
	if ( !scene )
		return;

	HWND control = GetControl( IDC_TAGS );
	if ( control )
	{
		SendMessage( control, CB_RESETCONTENT, 0, 0 ); 
		SendMessage( control, WM_SETTEXT , 0, (LPARAM)va( "\"%s\" \"%s\"", params->m_szTagName, params->m_szTagWav ) );

		for ( int i = 0; i < scene->GetNumActors(); i++ )
		{
			CChoreoActor *a = scene->GetActor( i );
			if ( !a )
				continue;

			for ( int j = 0; j < a->GetNumChannels(); j++ )
			{
				CChoreoChannel *c = a->GetChannel( j );
				if ( !c )
					continue;

				for ( int k = 0 ; k < c->GetNumEvents(); k++ )
				{
					CChoreoEvent *e = c->GetEvent( k );
					if ( !e )
						continue;

					if ( e->GetNumRelativeTags() <= 0 )
						continue;

					// add each tag to combo box
					for ( int t = 0; t < e->GetNumRelativeTags(); t++ )
					{
						CEventRelativeTag *tag = e->GetRelativeTag( t );
						if ( !tag )
							continue;

						SendMessage( control, CB_ADDSTRING, 0, (LPARAM)va( "\"%s\" \"%s\"", tag->GetName(), e->GetParameters() ) ); 
					}
				}
			}
		}
	}
}

#include "mapentities.h"
#include "utldict.h"

struct CMapEntityData
{
	CMapEntityData()
	{
		origin.Init();
		angles.Init();
	}

	Vector origin;
	QAngle angles;
};

class CMapEntities : public IMapEntities
{
public:
	CMapEntities();
	~CMapEntities();

	virtual void	CheckUpdateMap( char const *mapname );
	virtual bool	LookupOrigin( char const *name, Vector& origin, QAngle& angles )
	{
		int idx = FindNamedEntity( name );
		if ( idx == -1 )
		{
			origin.Init();
			angles.Init();
			return false;
		}

		CMapEntityData *e = &m_Entities[ idx ];
		Assert( e );
		origin = e->origin;
		angles = e->angles;
		return true;
	}

	virtual int		Count( void );
	virtual char const *GetName( int number );

	int	FindNamedEntity( char const *name );

private:
	char		m_szCurrentMap[ 1024 ];

	CUtlDict< CMapEntityData, int >	m_Entities;
};

static CMapEntities g_MapEntities;
// Expose to rest of tool
IMapEntities *mapentities = &g_MapEntities;

CMapEntities::CMapEntities()
{
	m_szCurrentMap[ 0 ] = 0;
}

CMapEntities::~CMapEntities()
{
	m_Entities.RemoveAll();
}

int CMapEntities::FindNamedEntity( char const *name )
{
	char lowername[ 128 ];
	strcpy( lowername, name );
	_strlwr( lowername );

	int index = m_Entities.Find( lowername );
	if ( index == m_Entities.InvalidIndex() )
		return -1;

	return index;
}

#include "bspfile.h"

void CMapEntities::CheckUpdateMap( char const *mapname )
{
	if ( !mapname || !mapname[ 0 ] )
		return;

	if ( !stricmp( mapname, m_szCurrentMap ) )
		return;

	// Latch off the name of the map
	Q_strncpy( m_szCurrentMap, mapname, sizeof( m_szCurrentMap ) );

	// Load names from map
	m_Entities.RemoveAll();

	FileHandle_t hfile = filesystem->Open( mapname, "rb" );
	if ( hfile == FILESYSTEM_INVALID_HANDLE )
		return;

	dheader_t header;
	filesystem->Read( &header, sizeof( header ), hfile );

	// Check the header
	if ( header.ident != IDBSPHEADER ||
		 header.version < MINBSPVERSION || header.version > BSPVERSION )
	{
		Con_ErrorPrintf( "BSP file %s is wrong version (%i), expected (%i)\n",
			mapname,
			header.version, 
			BSPVERSION );

		filesystem->Close( hfile );
		return;
	}

	// Find the LUMP_PAKFILE offset
	lump_t *entlump = &header.lumps[ LUMP_ENTITIES ];
	if ( entlump->filelen <= 0 )
	{
		Con_ErrorPrintf( "BSP file %s is missing entity lump\n", mapname );

		// It's empty or only contains a file header ( so there are no entries ), so don't add to search paths
		filesystem->Close( hfile );
		return;
	}

	// Seek to correct position
	filesystem->Seek( hfile, entlump->fileofs, FILESYSTEM_SEEK_HEAD );

	char *buffer = new char[ entlump->filelen + 1 ];
	Assert( buffer );

	filesystem->Read( buffer, entlump->filelen, hfile );

	filesystem->Close( hfile );

	buffer[ entlump->filelen ] = 0;

	// Now we have entity buffer, now parse it
	ParseFromMemory( buffer, entlump->filelen );

	while ( 1 )
	{
		if (!GetToken (true))
			break;

		if (Q_stricmp (token, "{") )
			Error ("ParseEntity: { not found");
		
		char name[ 256 ];
		char origin[ 256 ];
		char angles[ 256 ];

		name[ 0 ] = 0;
		origin[ 0 ] = 0;
		angles[ 0 ] = 0;

		do
		{
			char key[ 256 ];
			char value[ 256 ];

			if (!GetToken (true))
			{
				Error ("ParseEntity: EOF without closing brace");
			}

			if (!Q_stricmp (token, "}") )
				break;

			Q_strncpy( key, token, sizeof( key ) );

			GetToken (false);

			Q_strncpy( value, token, sizeof( value ) );

			// Con_Printf( "Parsed %s -- %s\n", key, value );

			if ( !Q_stricmp( key, "name" ) )
			{
				Q_strncpy( name, value, sizeof( name ) );
			}
			if ( !Q_stricmp( key, "targetname" ) )
			{
				Q_strncpy( name, value, sizeof( name ) );
			}
			if ( !Q_stricmp( key, "origin" ) )
			{
				Q_strncpy( origin, value, sizeof( origin ) );
			}
			if ( !Q_stricmp( key, "angles" ) )
			{
				Q_strncpy( angles, value, sizeof( angles ) );
			}

		} while (1);

		if ( name[ 0 ] )
		{
			if ( FindNamedEntity( name ) == - 1 )
			{
				CMapEntityData ent;
				
				float org[3];
				if ( origin[ 0 ] )
				{
					if ( 3 == sscanf( origin, "%f %f %f", &org[ 0 ], &org[ 1 ], &org[ 2 ] ) )
					{
						ent.origin = Vector( org[ 0 ], org[ 1 ], org[ 2 ] );

						// Con_Printf( "read %f %f %f for entity %s\n", org[0], org[1], org[2], name );
					}
				}
				if ( angles[ 0 ] )
				{
					if ( 3 == sscanf( angles, "%f %f %f", &org[ 0 ], &org[ 1 ], &org[ 2 ] ) )
					{
						ent.angles = QAngle( org[ 0 ], org[ 1 ], org[ 2 ] );

						// Con_Printf( "read %f %f %f for entity %s\n", org[0], org[1], org[2], name );
					}
				}

				m_Entities.Insert( name, ent );
			}
		}
	}

	delete[] buffer;
}

int CMapEntities::Count( void )
{
	return m_Entities.Count();
}

char const *CMapEntities::GetName( int number )
{
	if ( number < 0 || number >= (int)m_Entities.Count() )
		return NULL;

	return m_Entities.GetElementName( number );
}

bool NameLessFunc( const char *const& name1, const char *const& name2 )
{
	if ( Q_stricmp( name1, name2 ) < 0 )
		return true;
	return false;
}

void CBaseEventPropertiesDialog::SetDialogTitle( CEventParams *params, char const *eventname, char const *desc )
{
	char sz[ 256 ];
	Q_snprintf( sz, sizeof( sz ), " : %s", eventname  );
	Q_strncat( params->m_szDialogTitle, sz, sizeof(  params->m_szDialogTitle ), COPY_ALL_CHARACTERS );
	Q_snprintf( sz, sizeof( sz ), "%s:", desc );

	// Set dialog title
	SetWindowText( m_hDialog, params->m_szDialogTitle );
	// Set type name field
	SetDlgItemText( m_hDialog, IDC_TYPENAME, sz );
	// Set event name
	SetDlgItemText( m_hDialog, IDC_EVENTNAME, params->m_szName );
}

void CBaseEventPropertiesDialog::ShowControlsForEventType( CEventParams *params )
{
	// Special processing for various settings
	if ( !params->m_bHasEndTime )
	{
		ShowWindow( GetControl( IDC_ENDTIME ), SW_HIDE );
	}

	if ( params->m_bFixedLength )
	{
		ShowWindow( GetControl( IDC_ENDTIME ), SW_HIDE );
		ShowWindow( GetControl( IDC_CHECK_ENDTIME ), SW_HIDE );
	}
}

void CBaseEventPropertiesDialog::InitControlData( CEventParams *params )
{
	SetDlgItemText( m_hDialog, IDC_STARTTIME, va( "%f", params->m_flStartTime ) );
	SetDlgItemText( m_hDialog, IDC_ENDTIME, va( "%f", params->m_flEndTime ) );

	SendMessage( GetControl( IDC_CHECK_ENDTIME ), BM_SETCHECK, 
		( WPARAM ) params->m_bHasEndTime ? BST_CHECKED : BST_UNCHECKED,
		( LPARAM )0 );

	if ( GetControl( IDC_CHECK_RESUMECONDITION ) != (HWND)0 )
	{
		SendMessage( GetControl( IDC_CHECK_RESUMECONDITION ), BM_SETCHECK, 
			( WPARAM ) params->m_bResumeCondition ? BST_CHECKED : BST_UNCHECKED,
			( LPARAM )0 );
	}
	
	SendMessage( GetControl( IDC_CHECK_DISABLED ), BM_SETCHECK, 
		( WPARAM ) params->m_bDisabled ? BST_CHECKED : BST_UNCHECKED,
		( LPARAM )0 );

	PopulateTagList( params );
}

BOOL CBaseEventPropertiesDialog::InternalHandleMessage( CEventParams *params, HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam, bool& handled )
{
	handled = false;
	switch(uMsg)
	{
	default:
		break;
    case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		default:
			break;
		case IDC_CHECK_DISABLED:
			{
				params->m_bDisabled = SendMessage( GetControl( IDC_CHECK_DISABLED ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
				handled = true;
				return TRUE;
			}
			break;
		}
	}
	return FALSE;
}

void CBaseEventPropertiesDialog::PopulateNamedActorList( HWND wnd, CEventParams *params )
{
	int i;

	char const *mapname = NULL;
	if ( params->m_pScene )
	{
		mapname = params->m_pScene->GetMapname();
	}

	CUtlRBTree< char const *, int >		m_SortedNames( 0, 0, NameLessFunc );

	if ( mapname )
	{
		g_MapEntities.CheckUpdateMap( mapname );

		for ( i = 0; i < g_MapEntities.Count(); i++ )
		{
			char const *name = g_MapEntities.GetName( i );
			if ( name && name[ 0 ] )
			{
				m_SortedNames.Insert( name );
			}
		}
	}

	for ( i = 0 ; i < params->m_pScene->GetNumActors() ; i++ )
	{
		CChoreoActor *actor = params->m_pScene->GetActor( i );
		if ( actor && actor->GetName() && actor->GetName()[0] )
		{
			if ( m_SortedNames.Find( actor->GetName() ) == m_SortedNames.InvalidIndex() )
			{
				m_SortedNames.Insert( actor->GetName() );
			}
		}
	}

	i = m_SortedNames.FirstInorder();
	while ( i != m_SortedNames.InvalidIndex() )
	{
		char const *name = m_SortedNames[ i ];
		if ( name && name[ 0 ] )
		{
			SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)name ); 
		}

		i = m_SortedNames.NextInorder( i );
	}

	/*
	// Note have to do this here, after posting data to the control, since we are storing a raw string pointer in m_SortedNames!!!
	if ( allActors )
	{
		allActors->deleteThis();
	}
	*/

	// These events can also be directed at another player or named target, too
	SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!player" );
	SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!enemy" );
	SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!self" );
	SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!friend" );
	SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!speechtarget" );
	SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!target1" );
	SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!target2" );
	SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!target3" );
	SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!target4" );
	SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!target5" );
	SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!target6" );
	SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!target7" );
	SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!target8" );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : wnd - 
//			*params - 
// Output : static void
//-----------------------------------------------------------------------------
void CBaseEventPropertiesDialog::ParseTags( CEventParams *params )
{
	strcpy( params->m_szTagName, "" );
	strcpy( params->m_szTagWav, "" );

	if ( params->m_bUsesTag )
	{
		// Parse out the two tokens
		char selectedText[ 512 ];
		selectedText[ 0 ] = 0;
		HWND control = GetControl( IDC_TAGS );
		if ( control )
		{
			SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( selectedText ), (LPARAM)selectedText );
		}

		ParseFromMemory( selectedText, strlen( selectedText ) );
		if ( TokenAvailable() )
		{
			GetToken( false );
			char tagname[ 256 ];
			strcpy( tagname, token );
			if ( TokenAvailable() )
			{
				GetToken( false );
				char wavename[ 256 ];
				strcpy( wavename, token );

				// Valid
				strcpy( params->m_szTagName, tagname );
				strcpy( params->m_szTagWav, wavename );

			}
			else
			{
				params->m_bUsesTag = false;
			}
		}
		else
		{
			params->m_bUsesTag = false;
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : wnd - 
//			*params - 
// Output : static void
//-----------------------------------------------------------------------------
void CBaseEventPropertiesDialog::UpdateTagRadioButtons( CEventParams *params )
{
	if ( params->m_bUsesTag )
	{
		SendMessage( GetControl( IDC_RELATIVESTART ), BM_SETCHECK, ( WPARAM )BST_CHECKED, (LPARAM)0 );
		SendMessage( GetControl( IDC_ABSOLUTESTART ), BM_SETCHECK, ( WPARAM )BST_UNCHECKED, (LPARAM)0 );
	}
	else
	{
		SendMessage( GetControl( IDC_ABSOLUTESTART ), BM_SETCHECK, ( WPARAM )BST_CHECKED, (LPARAM)0 );
		SendMessage( GetControl( IDC_RELATIVESTART ), BM_SETCHECK, ( WPARAM )BST_UNCHECKED, (LPARAM)0 );
	}
}

void CBaseEventPropertiesDialog::GetSplineRect( HWND placeholder, RECT& rcOut )
{
	GetWindowRect( placeholder, &rcOut );
	RECT rcDlg;
	GetWindowRect( m_hDialog, &rcDlg );

	OffsetRect( &rcOut, -rcDlg.left, -rcDlg.top );
}

void CBaseEventPropertiesDialog::DrawSpline( HDC hdc, HWND placeholder, CChoreoEvent *e )
{
	RECT rcOut;

	GetSplineRect( placeholder, rcOut );

	HBRUSH bg = CreateSolidBrush( GetSysColor( COLOR_BTNFACE ) );
	FillRect( hdc, &rcOut, bg );
	DeleteObject( bg );

	if ( !e )
		return;

	// Draw spline
	float range = ( float )( rcOut.right - rcOut.left );
	if ( range <= 1.0f )
		return;

	float height = ( float )( rcOut.bottom - rcOut.top );

	HPEN pen = CreatePen( PS_SOLID, 1, GetSysColor( COLOR_BTNTEXT ) );
	HPEN oldPen = (HPEN)SelectObject( hdc, pen );

	float duration = e->GetDuration();
	float starttime = e->GetStartTime();

	for ( int i = 0; i < (int)range; i++ )
	{
		float frac = ( float )i / ( range - 1 );

		float scale = 1.0f - e->GetIntensity( starttime + frac * duration );

		int h = ( int ) ( scale * ( height - 1 ) );

		if ( i == 0 )
		{
			MoveToEx( hdc, rcOut.left + i, rcOut.top + h, NULL );
		}
		else
		{
			LineTo( hdc, rcOut.left + i, rcOut.top + h );
		}
	}

	SelectObject( hdc, oldPen );

	HBRUSH frame = CreateSolidBrush( GetSysColor( COLOR_BTNSHADOW ) );
	InflateRect( &rcOut, 1, 1 );
	FrameRect( hdc, &rcOut, frame );
	DeleteObject( frame );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *view - 
//			*actor - 
// Output : int
//-----------------------------------------------------------------------------
int EventProperties( CEventParams *params )
{
	int iret = 1;
	switch ( params->m_nType )
	{
	default:
		break;
	case CChoreoEvent::EXPRESSION:
		return EventProperties_Expression( params );
	case CChoreoEvent::LOOKAT:
		return EventProperties_LookAt( params );
	case CChoreoEvent::MOVETO:
		return EventProperties_MoveTo( params );
	case CChoreoEvent::SPEAK:
		return EventProperties_Speak( params );
	case CChoreoEvent::GESTURE:
		return EventProperties_Gesture( params );
	case CChoreoEvent::SEQUENCE:
		return EventProperties_Sequence( params );
	case CChoreoEvent::FACE:
		return EventProperties_Face( params );
	case CChoreoEvent::FIRETRIGGER:
		return EventProperties_FireTrigger( params );
	case CChoreoEvent::FLEXANIMATION:
		return EventProperties_FlexAnimation( params );
	case CChoreoEvent::SUBSCENE:
		return EventProperties_SubScene( params );
	case CChoreoEvent::INTERRUPT:
		return EventProperties_Interrupt( params );
	case CChoreoEvent::PERMIT_RESPONSES:
		return EventProperties_PermitResponses( params );
	case CChoreoEvent::GENERIC:
		return EventProperties_Generic( params );
	}

	return iret;
}