//////////////////////////////////////////////////////////////////////////////
//
// AVP_Tactical_System.cpp
//
// Created by NJD - 07 December 2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

// core
#include "Core/Asura.h"

// header
#include "Tactical/AVP_Tactical_System.h"

// asura
#include "ChunkSystem/Asura_Chunk_Stream.h"

// axon
#include "Core/BrainTypes/Axon_Project_Brain.h"

// avp
#include "Entity/AVP_Entity_Classification.h"
#include "Entity/Locations/FormationPoint/AVP_ServerNode_FormationPoint.h"
#include "Entity/Locations/OrientedNode/CoverpointNode/AVP_ServerNode_CoverpointNode.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/FaceHugger/AVP_ServerActor_FaceHugger.h"
#include "Entity/PhysicalObject/Actor/Weapons/Sentrygun/AVP_ServerWeapon_Sentrygun.h"
#include "Entity/PlayerInfo/AVP_ClientEntity_PlayerInfo.h"

//////////////////////////////////////////////////////////////////////////////
//
// AVP_Tactical_System
//
//////////////////////////////////////////////////////////////////////////////

AVP_Tactical_System::AVP_Tactical_System( Axon_Project_Brain* pxBrain )
: m_pxBrain( pxBrain )
, m_uFlags( 0 )
, m_fAtDestinationRange( 0.75f )
, m_xDestination( Asura_Maths::ZeroVector3 )
, m_xTorsoLookAt( Asura_Maths::ZeroVector3 )
, m_xHeadLookAt( Asura_Maths::ZeroVector3 )
, m_pxCurrentGamePlayVolume( NULL )
, m_pxCoverPoint( NULL )
, m_uMovementSpeed( 0 )
, m_uCurrentObjectiveGuid( ASURA_GUID_UNREGISTERED )
, m_uCurrentObjectiveType( 0 )
, m_fAimLerpModifier( 1.0f )
{

}

AVP_Tactical_System::~AVP_Tactical_System()
{
	if (m_pxCurrentGamePlayVolume)
	{
		m_pxCurrentGamePlayVolume = NULL;
	}

	if (m_pxCoverPoint)
	{
		m_pxCoverPoint = NULL;
	}
}

void AVP_Tactical_System::ReadFromChunkStream( Asura_Chunk_Stream& xStream )
{
    int iVersion;
    xStream >> iVersion;

    Asura_Assertf( iVersion == CURRENT_CHUNK_VERSION, ( "AVP_Tactical_System::ReadFromChunkStream - unhandled chunk version (%d)", iVersion ) );
}

void AVP_Tactical_System::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
    xStream << CURRENT_CHUNK_VERSION;
}

void AVP_Tactical_System::SetDestination(const Asura_Vector_3& xDest)
{
	m_bNewDestinationSet = true;
	m_xDestination = xDest;
	m_bHasArrivedAtDestination = false;
}

void AVP_Tactical_System::SetLookAt(const Asura_Vector_3& xPos)
{
	m_bLookAtSet	= true;
	m_xHeadLookAt	= xPos;
	m_xTorsoLookAt	= xPos;
}

void AVP_Tactical_System::SetLookAt(const Asura_Vector_3& xHead, const Asura_Vector_3& xTorso, const Asura_Vector_3& xFeet)
{
	m_bLookAtSet	= true;
	m_xHeadLookAt	= xHead;
	m_xTorsoLookAt	= xTorso;
}

AVP_Allegiance AVP_Tactical_System::GetAllegiance(const Asura_Entity& xMe, const Asura_Entity& xThem)
{
	// TODO - if this is going to develop into the way we check for allies in all targeting circumstances we need some more multiplayer-specific handling and more client-side bits. The function should probably move to another file too.
	//		For the time being though, this works for all current purposes.

	// Multiplayer - based on teams.
	if (AVP_Settings::IsMultiPlayer())
	{
		// Only do special tests if these are two players, so get hold of the player info.
		const AVP_ClientEntity_Actor* pxActorMe = AVP_ClientEntity_Actor::FindEntity(xMe.GetGuid());
		const AVP_ClientEntity_Actor* pxActorThem = AVP_ClientEntity_Actor::FindEntity(xThem.GetGuid());
		if (pxActorMe && pxActorThem)
		{
			const AVP_ClientEntity_PlayerInfo* pxMyPlayerInfo = AVP_ClientEntity_PlayerInfo::FindEntity(pxActorMe->GetPlayerInfoGuid());
			const AVP_ClientEntity_PlayerInfo* pxTheirPlayerInfo = AVP_ClientEntity_PlayerInfo::FindEntity(pxActorThem->GetPlayerInfoGuid());
			if (pxMyPlayerInfo && pxTheirPlayerInfo)
			{
				const AVP_Settings::Team eMyTeam = pxMyPlayerInfo->GetMultiplayerTeam();
				const AVP_Settings::Team eTheirTeam = pxTheirPlayerInfo->GetMultiplayerTeam();
				// If either is team DM, we're enemies.
				if ((eMyTeam == AVP_Settings::AVP_TEAM_NONE) || (eTheirTeam == AVP_Settings::AVP_TEAM_NONE))
				{
					return AVP_ALLEGIANCE_ENEMY;
				}

				// Otherwise, see if we're on different teams.
				if (eMyTeam == eTheirTeam)
				{
					return AVP_ALLEGIANCE_FRIEND;
				}
				else
				{
					return AVP_ALLEGIANCE_ENEMY;
				}
			}
		}
	}

    switch ( xMe.GetClassification() )
    {
        case AVP_ENTITYCLASS_ALIEN:
        case AVP_ENTITYCLASS_ALIENCHESTBURSTER:
        case AVP_ENTITYCLASS_ALIENFACEHUGGER:
        case AVP_ENTITYCLASS_ALIENQUEEN:
        case AVP_ENTITYCLASS_ALIENTEAM:
        {
            switch ( xThem.GetClassification() )
            {
                case AVP_ENTITYCLASS_ALIEN:
                case AVP_ENTITYCLASS_ALIENCHESTBURSTER:
                case AVP_ENTITYCLASS_ALIENFACEHUGGER:
                case AVP_ENTITYCLASS_ALIENQUEEN:
                case AVP_ENTITYCLASS_ALIENTEAM:
                {
                    return AVP_ALLEGIANCE_FRIEND;
                }

				case AVP_ENTITYCLASS_HUMAN: // ...
                case AVP_ENTITYCLASS_MARINE:
                case AVP_ENTITYCLASS_PREDATOR:
				case AVP_ENTITYCLASS_ANDROID:
				case AVP_ENTITYCLASS_CIVILIAN:
				case AVP_ENTITYCLASS_PREDALIEN:
                {
                    return AVP_ALLEGIANCE_ENEMY;
                }

				case AVP_ENTITYCLASS_SENTRYGUN:
				{	
					const AVP_ServerWeapon_Sentrygun* pxThem = AVP_ServerWeapon_Sentrygun::FindEntity( xThem.GetGuid() );
					if ( pxThem )
					{
						return pxThem->IsEnemyTo( xMe ) ? AVP_ALLEGIANCE_ENEMY : AVP_ALLEGIANCE_FRIEND;
					}
					else
					{
						return AVP_ALLEGIANCE_NEUTRAL;
					}
				}

                default:
                {
                    return AVP_ALLEGIANCE_NEUTRAL;
                }
            }
        }

        case AVP_ENTITYCLASS_MARINE:
        {
            switch ( xThem.GetClassification() )
            {
				case AVP_ENTITYCLASS_HUMAN: // ...
				case AVP_ENTITYCLASS_CIVILIAN:
                case AVP_ENTITYCLASS_MARINE:
				{
                    return AVP_ALLEGIANCE_FRIEND;
                }

                case AVP_ENTITYCLASS_ALIEN:
                case AVP_ENTITYCLASS_ALIENCHESTBURSTER:
                case AVP_ENTITYCLASS_ALIENQUEEN:
                case AVP_ENTITYCLASS_ALIENTEAM:
                case AVP_ENTITYCLASS_PREDATOR:
				case AVP_ENTITYCLASS_PREDALIEN:
				case AVP_ENTITYCLASS_ANDROID:
                {
                    return AVP_ALLEGIANCE_ENEMY;
                }

				case AVP_ENTITYCLASS_ALIENFACEHUGGER:
				{
					const AVP_ServerActor_FaceHugger* pxThem = AVP_ServerActor_FaceHugger::FindEntity( xThem.GetGuid() );
					if ( pxThem )
					{
						return pxThem->IsHugging() ? AVP_ALLEGIANCE_FRIEND : AVP_ALLEGIANCE_ENEMY;
					}
					else
					{
						return AVP_ALLEGIANCE_NEUTRAL;
					}
				}


				case AVP_ENTITYCLASS_SENTRYGUN:
				{
					const AVP_ServerWeapon_Sentrygun* pxThem = AVP_ServerWeapon_Sentrygun::FindEntity( xThem.GetGuid() );
					if ( pxThem )
					{
						return pxThem->IsEnemyTo( xMe ) ? AVP_ALLEGIANCE_ENEMY : AVP_ALLEGIANCE_FRIEND;
					}
					else
					{
						return AVP_ALLEGIANCE_NEUTRAL;
					}
				}

                default:
                {
                    return AVP_ALLEGIANCE_NEUTRAL;
                }
            }
        }

        case AVP_ENTITYCLASS_PREDATOR:
        {
            switch ( xThem.GetClassification() )
            {
                case AVP_ENTITYCLASS_PREDATOR:
                {
                    return AVP_ALLEGIANCE_FRIEND;
                }

                case AVP_ENTITYCLASS_ALIEN:
                case AVP_ENTITYCLASS_ALIENCHESTBURSTER:
                case AVP_ENTITYCLASS_ALIENQUEEN:
                case AVP_ENTITYCLASS_ALIENTEAM:
                case AVP_ENTITYCLASS_MARINE:
				case AVP_ENTITYCLASS_ANDROID:
				case AVP_ENTITYCLASS_HUMAN:
				case AVP_ENTITYCLASS_PREDALIEN:
				{
                    return AVP_ALLEGIANCE_ENEMY;
                }

				case AVP_ENTITYCLASS_SENTRYGUN:
				{
					const AVP_ServerWeapon_Sentrygun* pxThem = AVP_ServerWeapon_Sentrygun::FindEntity( xThem.GetGuid() );
					if ( pxThem )
					{
						return pxThem->IsEnemyTo( xMe ) ? AVP_ALLEGIANCE_ENEMY : AVP_ALLEGIANCE_FRIEND;
					}
					else
					{
						return AVP_ALLEGIANCE_NEUTRAL;
					}
				}

				case AVP_ENTITYCLASS_ALIENFACEHUGGER:
				{
					const AVP_ServerActor_FaceHugger* pxThem = AVP_ServerActor_FaceHugger::FindEntity( xThem.GetGuid() );
					if ( pxThem )
					{
						return pxThem->IsHugging() ? AVP_ALLEGIANCE_FRIEND : AVP_ALLEGIANCE_ENEMY;
					}
					else
					{
						return AVP_ALLEGIANCE_NEUTRAL;
					}
				}

				case AVP_ENTITYCLASS_CIVILIAN:
                default:
                {
                    return AVP_ALLEGIANCE_NEUTRAL;
                }
            }
        }

		case AVP_ENTITYCLASS_PREDALIEN: // he hate everyone 
			{
				return AVP_ALLEGIANCE_ENEMY; 
			}

		case AVP_ENTITYCLASS_ANDROID:
		{
			switch (xThem.GetClassification())
			{
				case AVP_ENTITYCLASS_PREDATOR:
				case AVP_ENTITYCLASS_PREDALIEN:
				case AVP_ENTITYCLASS_ALIEN:
				case AVP_ENTITYCLASS_ALIENCHESTBURSTER:
				case AVP_ENTITYCLASS_ALIENQUEEN:
				case AVP_ENTITYCLASS_ALIENTEAM:
				case AVP_ENTITYCLASS_MARINE:
				case AVP_ENTITYCLASS_HUMAN:
				{
					return AVP_ALLEGIANCE_ENEMY;
				}

				case AVP_ENTITYCLASS_CIVILIAN:
				case AVP_ENTITYCLASS_ANDROID:
				{
					return AVP_ALLEGIANCE_FRIEND;
				}

				case AVP_ENTITYCLASS_ALIENFACEHUGGER:
				{
					const AVP_ServerActor_FaceHugger* pxThem = AVP_ServerActor_FaceHugger::FindEntity( xThem.GetGuid() );
					if ( pxThem )
					{
						return pxThem->IsHugging() ? AVP_ALLEGIANCE_FRIEND : AVP_ALLEGIANCE_ENEMY;
					}
					else
					{
						return AVP_ALLEGIANCE_NEUTRAL;
					}
				}

				case AVP_ENTITYCLASS_SENTRYGUN:
				{
					const AVP_ServerWeapon_Sentrygun* pxThem = AVP_ServerWeapon_Sentrygun::FindEntity( xThem.GetGuid() );
					if ( pxThem )
					{
						return pxThem->IsEnemyTo( xMe ) ? AVP_ALLEGIANCE_ENEMY : AVP_ALLEGIANCE_FRIEND;
					}
					else
					{
						return AVP_ALLEGIANCE_NEUTRAL;
					}
				}

				default:
				{
					return AVP_ALLEGIANCE_NEUTRAL;
				}
			}
		}

		case AVP_ENTITYCLASS_CIVILIAN:
		{
			switch (xThem.GetClassification())
			{
				case AVP_ENTITYCLASS_PREDATOR:
				case AVP_ENTITYCLASS_PREDALIEN:
				case AVP_ENTITYCLASS_ALIEN:
				case AVP_ENTITYCLASS_ALIENCHESTBURSTER:
				case AVP_ENTITYCLASS_ALIENQUEEN:
				case AVP_ENTITYCLASS_ALIENTEAM:
				{
					return AVP_ALLEGIANCE_ENEMY;
				}

				case AVP_ENTITYCLASS_MARINE:
				case AVP_ENTITYCLASS_CIVILIAN:
				case AVP_ENTITYCLASS_ANDROID:
				case AVP_ENTITYCLASS_HUMAN: // ...
				{
					return AVP_ALLEGIANCE_FRIEND;
				}

				case AVP_ENTITYCLASS_ALIENFACEHUGGER:
				{
					const AVP_ServerActor_FaceHugger* pxThem = AVP_ServerActor_FaceHugger::FindEntity( xThem.GetGuid() );
					if ( pxThem )
					{
						return pxThem->IsHugging() ? AVP_ALLEGIANCE_FRIEND : AVP_ALLEGIANCE_ENEMY;
					}
					else
					{
						return AVP_ALLEGIANCE_NEUTRAL;
					}
				}

				case AVP_ENTITYCLASS_SENTRYGUN:
				{
					const AVP_ServerWeapon_Sentrygun* pxThem = AVP_ServerWeapon_Sentrygun::FindEntity( xThem.GetGuid() );
					if ( pxThem )
					{
						return pxThem->IsEnemyTo( xMe ) ? AVP_ALLEGIANCE_ENEMY : AVP_ALLEGIANCE_FRIEND;
					}
					else
					{
						return AVP_ALLEGIANCE_NEUTRAL;
					}
				}

				default:
				{
					return AVP_ALLEGIANCE_NEUTRAL;
				}
				
			}
		}

		case AVP_ENTITYCLASS_SENTRYGUN:
		{
			const AVP_ServerWeapon_Sentrygun* pxMe = AVP_ServerWeapon_Sentrygun::FindEntity( xMe.GetGuid() );
			if ( pxMe )
			{
				return pxMe->IsEnemyTo( xThem ) ? AVP_ALLEGIANCE_ENEMY : AVP_ALLEGIANCE_FRIEND;
			}
			else
			{
				return AVP_ALLEGIANCE_NEUTRAL;
			}
		}

        default:
        {
            return AVP_ALLEGIANCE_NEUTRAL;
        }
    }
}

//////////////////////////////////////////////////////////////////////////////
//
// 
//
//////////////////////////////////////////////////////////////////////////////
