#include "../idlib/precompiled.h"
#pragma hdrstop

#include "Game_local.h"

const idEventDef EV_OpenGate( "<openGate>" );
const idEventDef EV_CloseGate( "<closeGate>" );

//=======================================================
//
//	rvTramGate
//
//=======================================================
CLASS_DECLARATION( idAnimatedEntity, rvTramGate )
	EVENT( EV_Touch,			rvTramGate::Event_Touch )
	EVENT( EV_Activate,			rvTramGate::Event_Activate )
	EVENT( EV_OpenGate,			rvTramGate::Event_OpenGate )
	EVENT( EV_CloseGate,		rvTramGate::Event_CloseGate )
	EVENT( EV_Door_Lock,		rvTramGate::Event_Lock )
	EVENT( EV_Door_IsOpen,		rvTramGate::Event_IsOpen )
	EVENT( EV_Door_IsLocked,	rvTramGate::Event_IsLocked )
END_CLASS

/*
================
rvTramGate::Spawn
================
*/
void rvTramGate::Spawn() {
	SpawnDoors();

	AdjustFrameRate();
}

/*
================
rvTramGate::~rvTramGate
================
*/
rvTramGate::~rvTramGate() {
	doorList.RemoveContents( true );
}

/*
================
rvTramGate::SpawnDoors
================
*/
void rvTramGate::SpawnDoors() {
	idDict	args;
	idVec3	dir = spawnArgs.GetAngles("doorsAxisOffset").ToMat3() * GetPhysics()->GetAxis()[1];

	args.Set( "team", GetName() );
	args.SetMatrix( "rotation", dir.ToMat3() );
	args.SetVector( "origin", GetPhysics()->GetOrigin() );

	int len = strlen("door_");
	for( const idKeyValue* kv = spawnArgs.MatchPrefix("door"); kv; kv = spawnArgs.MatchPrefix("door", kv) ) {
		args.Set( kv->GetKey().Right(kv->GetKey().Length() - len), kv->GetValue() );
	}

	args.SetFloat( "movedir", (-dir).ToYaw() );
	idDoor* door = gameLocal.SpawnSafeEntityDef<idDoor>( spawnArgs.GetString("def_door1"), &args );
	if( door ) {
		doorList.Alloc() = door;
		door->SetDoorFrameController( this );
	}

	args.SetFloat( "movedir", dir.ToYaw() );
	door = gameLocal.SpawnSafeEntityDef<idDoor>( spawnArgs.GetString("def_door2"), &args );
	if( door ) {
		doorList.Alloc() = door;
		door->SetDoorFrameController( this );
	}

	//assert( GetDoorMaster() == door->GetMoveMaster() );
}

/*
================
rvTramGate::AdjustFrameRate
================
*/
void rvTramGate::AdjustFrameRate() {
	GetAnimator()->SetPlaybackRate( "open", spawnArgs.GetFloat("openFrameRateScale") );
	GetAnimator()->SetPlaybackRate( "close", spawnArgs.GetFloat("closeFrameRateScale") );
}

/*
================
rvTramGate::OpenGate
================
*/
void rvTramGate::OpenGate() {
	PlayAnim( ANIMCHANNEL_ALL, "open" );
}

/*
================
rvTramGate::CloseGate
================
*/
void rvTramGate::CloseGate() {
	PlayAnim( ANIMCHANNEL_ALL, "close" );
}

/*
================
rvTramGate::Save
================
*/
void rvTramGate::Save( idSaveGame *savefile ) const {
	savefile->WriteInt( doorList.Num() );
	for( int i = 0; i < doorList.Num(); i++ ) {
		doorList[i].Save( savefile );
	}
}

/*
================
rvTramGate::Restore
================
*/
void rvTramGate::Restore( idRestoreGame *savefile ) {
	int num = 0;
	idEntityPtr<idDoor> temp;
	savefile->ReadInt( num );
	if ( num < 0 || num > MAX_SAVEGAME_TRAM_GATE_DOORS ) {
		savefile->Error( "rvTramGate::Restore: invalid door count %d", num );
	}
	doorList.Clear();
	for( int i = 0; i < num; i++ ) {
		temp.Restore( savefile );
		doorList.Append( temp );
	}
}

/*
================
rvTramGate::PlayAnim
================
*/
int rvTramGate::PlayAnim( int channel, const char* animName, int blendFrames ) {
	int animHandle = GetAnimator()->GetAnim( animName );

	if( !animHandle ) {
		ClearAllAnims( blendFrames );
		return 0;
	}

	GetAnimator()->PlayAnim( channel, animHandle, gameLocal.GetTime(), FRAME2MS(blendFrames) );
	return GetAnimator()->CurrentAnim(channel)->PlayLength();
}

/*
================
rvTramGate::CycleAnim
================
*/
void rvTramGate::CycleAnim( int channel, const char* animName, int blendFrames ) {
	int animHandle = GetAnimator()->GetAnim( animName );

	if( !animHandle ) {
		ClearAllAnims( blendFrames );
		return;
	}

	GetAnimator()->CycleAnim( channel, animHandle, gameLocal.GetTime(), FRAME2MS(blendFrames) );
}

/*
================
rvTramGate::Event_Touch
================
*/
void rvTramGate::ClearAllAnims( int blendFrames ) {
	GetAnimator()->ClearAllAnims( gameLocal.GetTime(), FRAME2MS(blendFrames) );
}

/*
================
rvTramGate::Event_Touch
================
*/
void rvTramGate::ClearAnim( int channel, int blendFrames ) {
	GetAnimator()->Clear( channel, gameLocal.GetTime(), FRAME2MS(blendFrames) ); 
}

/*
================
rvTramGate::Event_Touch
================
*/
bool rvTramGate::AnimIsPlaying( int channel, int blendFrames ) {
	return GetAnimator()->CurrentAnim(channel)->GetEndTime() - FRAME2MS(blendFrames) >= gameLocal.GetTime();
}

/*
================
rvTramGate::IsOpen
================
*/
bool rvTramGate::IsOpen() const {
	return (IsDoorMasterValid()) ? GetDoorMaster()->IsOpen() : false;
}

/*
================
rvTramGate::IsClosed
================
*/
bool rvTramGate::IsClosed() const {
	return (IsDoorMasterValid()) ? GetDoorMaster()->IsClosed() : false;
}

/*
================
rvTramGate::GetDoorMaster
================
*/
idDoor* rvTramGate::GetDoorMaster() const {
	return doorList.Num() ? doorList[0] : NULL;
}

/*
================
rvTramGate::IsDoorMasterValid
================
*/
bool rvTramGate::IsDoorMasterValid() const {
	return GetDoorMaster() != NULL;
}

/*
================
rvTramGate::Event_Touch
================
*/
void rvTramGate::Event_Touch( idEntity* other, trace_t* trace ) {
	if( !IsDoorMasterValid() ) {
		return;
	}

	if( IsClosed() ) {
		OpenGate();
		GetDoorMaster()->ProcessEvent( &EV_Touch, this, trace );	
	} else if( IsOpen() ) {
		GetDoorMaster()->ProcessEvent( &EV_Touch, this, trace );	
	}
}

/*
================
rvTramGate::Event_Activate
================
*/
void rvTramGate::Event_Activate( idEntity* activator ) {
	if( !IsDoorMasterValid() ) {
		return;
	}
	
	// FIXME: may need some better logic than this.
	const char* animName = (IsClosed()) ? "open" : (IsOpen()) ? "close" : NULL;
	if( animName ) {
		PlayAnim( ANIMCHANNEL_ALL, animName );
		GetDoorMaster()->ProcessEvent( &EV_Activate, this );
	}
}

/*
================
rvTramGate::Event_OpenGate
================
*/
void rvTramGate::Event_OpenGate() {
	OpenGate();
}

/*
================
rvTramGate::Event_CloseGate
================
*/
void rvTramGate::Event_CloseGate() {
	CloseGate();
}

/*
================
rvTramGate::Event_IsOpen
================
*/
void rvTramGate::Event_IsOpen( void ) {
	idThread::ReturnInt( IsOpen() );
}

/*
================
rvTramGate::Event_IsLocked
================
*/
void rvTramGate::Event_IsLocked( void ) {
	idThread::ReturnInt( (IsDoorMasterValid()) ? GetDoorMaster()->IsLocked() : 0 );
}

/*
================
rvTramGate::Event_Lock
================
*/
void rvTramGate::Event_Lock( int f ) {
	if( IsDoorMasterValid() ) {
		GetDoorMaster()->Lock( f );
	}
}
