//////////////////////////////////////////////////////////////////////////////
//
// AVP_Movement_System.cpp
//
// Copyright (c) 2007 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header.
#include "Moves/AVP_Movement_System.h"

// AVP
#include "Core/AVP_Game_Core.h"
#include "Core/AVP_Settings.h"
#include "Entity/AVP_Messages.h"
#include "Entity/PhysicalObject/Actor/AVP_ClientEntity_Actor.h"
#include "Entity/PlayerInfo/AVP_ClientEntity_PlayerInfo.h"
#include "Moves/AVP_Moves.h"

// C L A S S   I M P L E M E N T A T I O N S /////////////////////////////////

//
// AVP_Msg_MoveWithTargetEntity
//

void AVP_Msg_MoveWithTargetEntity::SetTargetGuid(Asura_Guid uTargetGuid)
{
	m_uTargetGuid = uTargetGuid;
	// Also update the project flags.
	m_usProjectFlags |= AVP_MSG_MOVE_FLAG_HASTARGETENTITY;
}

void AVP_Msg_MoveWithTargetEntity::SetTargetEntityIsPuppet(bool bIsPuppet)
{
	if (bIsPuppet)
	{
		m_usProjectFlags |= AVP_MSG_MOVE_FLAG_TARGETENTITYISPUPPET;
	}
	else
	{
		m_usProjectFlags &= ~AVP_MSG_MOVE_FLAG_TARGETENTITYISPUPPET;
	}
}

//
// AVP_Msg_Melee_Move
//

void AVP_Msg_Melee_Move::SetSuckPermitted(bool bPermitted)
{
	if (bPermitted)
	{
		m_usProjectFlags |= AVP_MSG_MELEE_MOVE_FLAG_SUCKPERMITTED;
	}
	else
	{
		m_usProjectFlags &= ~AVP_MSG_MELEE_MOVE_FLAG_SUCKPERMITTED;
	}
}

//
// AVP_Movement_System
//

AVP_Movement_System::AVP_Movement_System( AVP_ClientEntity_Actor& xOwner )
: Asura_Movement_System( xOwner )
, m_xOwner( xOwner )
{
    // Only send chain complete messages if we're the master system.
    if ( IsControlledByLocalPlayer( GetOwnerGuid() ) ||
        ( !IsControlledByAnyPlayer( GetOwnerGuid() ) && Asura_Network::IsServer() ) )
    {
        SetSendChainCompleteMessages( true );
    }
}


bool AVP_Movement_System::IsControlledByLocalPlayer( const Asura_Guid uGUID ) const
{
    return AVP_Game_Core::IsLocalPlayer(uGUID);
}

bool AVP_Movement_System::HandleMessage( const Asura_Message* pxMessage )
{
	switch(pxMessage->GetMsgID())
	{
		case AVP_MSGID_GRAPPLE_ATTACKER_UPDATE:
		case AVP_MSGID_GRAPPLE_VICTIM_UPDATE:
		case AVP_MSGID_COUNTER_MELEE:
		case AVP_MSGID_MELEE_ATTACK_HIT:
		case AVP_MSGID_MELEE_ATTACK_PREPAREGRAPPLE:
		case AVP_MSGID_ACTIVEGRAPPLE_RESULT:
		{
			const Asura_Move* pxCurrentMove = GetCurrentMove();
			if(pxCurrentMove)
			{
				AVP_Move* pxMove = static_cast<AVP_Move*>(GetMove(pxCurrentMove->GetHash()));
				if(pxMove)
				{
					pxMove->HandleMessage(pxMessage);
				}
			}

			// Return true anyway.
			return true;
		}

		case AVP_MSGID_MOVESTART_SERVER:
			// Similar to the normal move start message, but with different rules for when to ignore it.
			// AJG - 27/02/08 - Not ignored on the server as it will get ignored further down, and we can use this message as a way of starting the puppet move without accidentally aborting it after it's already started when the puppeteer is using a chain of moves.

			// If we're in the middle of updating the current move, we can't process messages.
			// Store the message and handle it later.
			if( m_bUpdatingCurrentMove )
			{
				Asura_Assert( !m_pxPendingMoveMessage, "Already have a move message pending. Currently pending message will be lost." );
				if( m_pxPendingMoveMessage )
				{
					Asura_Network::DestroyMessage( m_pxPendingMoveMessage );
					m_pxPendingMoveMessage = NULL;
				}

				m_pxPendingMoveMessage = Asura_Network::DuplicateMessage( pxMessage, false );
				return true;
			}
			else
			{	
				// safe to handle message
				// Ignore requests to start moves if our owner actor is
				// currently dictating that we can't.
				if( !m_xOwner.CanCurrentlyUseMoves() ) { return true; }

				const Asura_Msg_Move* pxData = static_cast<const Asura_Msg_Move*>(pxMessage->GetData());
				const Asura_Hash_ID uMoveHash = pxData->GetMoveID();

				AVP_Move* pxCurrentMove = static_cast<AVP_Move*>(m_pxCurrentMove);

				// Server start messages only start a move if we're not already in that one - it's used to correct things up.
				if (pxCurrentMove && (pxCurrentMove->GetHash() == uMoveHash)) { return true; }

				Asura_Move* pxMove = GetMove(uMoveHash);

				Asura_Assertf(pxMove, ("Couldn't find move to start - ID 0x%08X.", uMoveHash));

				if(pxMove)
				{
					// if there's a move in progress, try to abort that one first 
					if( pxCurrentMove )
					{
						if( pxData->GetForceAbort() || pxCurrentMove->CanBeAborted() )
						{
							// Make sure we don't send any abort messages.
							bool bSuppressAbort = m_pxCurrentMove->GetSuppressAbortMessage();
							pxCurrentMove->SetSuppressAbortMessage(true);

							pxCurrentMove->Abort();

							pxCurrentMove->SetSuppressAbortMessage(bSuppressAbort);
						}
						else
						{
							// can't abort this move, ignore the message
							return true;
						}
					}	

					StartMove( *pxMove, pxData );
				}

				return true;
			}

		default:
		{
			break;
		}
	}
	return(PARENT::HandleMessage(pxMessage));
}

bool AVP_Movement_System::IsControlledByAnyPlayer( const Asura_Guid uGUID ) const
{
    for ( Asura_Hashed_List_ConstIt< AVP_ClientEntity_PlayerInfo > xPlayerInfoIter( AVP_ClientEntity_PlayerInfo::GetList() );
        !xPlayerInfoIter.Done();
        xPlayerInfoIter.Next() )
    {
        const AVP_ClientEntity_PlayerInfo* pxPlayerInfo = xPlayerInfoIter.GetCurrent();
        if ( pxPlayerInfo && ( pxPlayerInfo->GetGuid() == uGUID || pxPlayerInfo->GetPuppetGuid() == uGUID ) )
        {
            return true;
        }
    }

    return false;
}

void AVP_Movement_System::ExternalUpdateCurrentMove(const float fTimeStep)
{
	if (m_pxCurrentMove)
	{
		(static_cast<AVP_Move*>(m_pxCurrentMove))->ExternalUpdate(fTimeStep);
	}
}

void AVP_Movement_System::SendChainCompleteMessage( Asura_Hash_ID uFinalMoveID ) const
{
    // Send a message to the master state handler and always the server.
    Asura_Message* pxMessage = Asura_Network::CreateMessage(ASURA_MSG_MOVECHAINCOMPLETE, sizeof(Asura_Msg_MoveChainComplete));
    if(pxMessage != NULL)
    {
        Asura_Msg_MoveChainComplete* pxData = static_cast<Asura_Msg_MoveChainComplete*>(pxMessage->GetData());
        pxData->SetInitialMoveID(GetFirstMoveInChain());
        pxData->SetFinalMoveID(uFinalMoveID);

        pxMessage->SetToGuid(GetOwnerGuid());
        pxMessage->SetFromGuid(GetOwnerGuid());
        pxMessage->UseGuaranteedDelivery();

        Asura_Network::Send(pxMessage);
    }
}
