//////////////////////////////////////////////////////////////////////////////
//
// Axon_Sense_Vision.cpp
//
// Created by NJD - 29 August 2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

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

#ifdef ASURA_USE_AXON

// header
#include "Knowledge/SenseTypes/Axon_Sense_Vision.h"

// asura
#include "ChunkSystem/Asura_Chunk_Macros.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Asura_Maths.h"
#include "Network/Asura_Message.h"

// axon
#include "Knowledge/Axon_Knowledge.h"
#include "Knowledge/Axon_Knowledge_System.h"

//////////////////////////////////////////////////////////////////////////////
//
// Axon_Sense_Vision
//
//////////////////////////////////////////////////////////////////////////////

Axon_Sense_Vision::Axon_Sense_Vision( Axon_Knowledge_System< Axon_Knowledge >* pxKnowledgeSystem )
: Axon_Sense< Axon_Knowledge >( pxKnowledgeSystem )
, m_fRange( 0.0f )
, m_fTanFovX( Asura_Maths::Tangent( Asura_Maths::Pi * 0.25f ) )		//45 degrees
, m_fTanFovYUp( Asura_Maths::Tangent( Asura_Maths::Pi * 0.25f ) )
, m_fTanFovYDown( Asura_Maths::Tangent( Asura_Maths::Pi * 0.25f ) )
, m_fRangeModifier( 1.0f )
, m_xVisionList()
{
}

Axon_Sense_Vision::~Axon_Sense_Vision()
{
    DestroyAllKnowledge();
}

void Axon_Sense_Vision::ReadFromChunkStream( Asura_Chunk_Stream& xStream )
{
    int iChunkVersion;
    xStream >> iChunkVersion;

    // We would do our parent chunk now, if it existed.

    if ( iChunkVersion == 0 )
    {
        xStream >> m_fRangeModifier;
    }
    else
    {
        Asura_Assertf( false, ( "Axon_Sense_Vision::ReadFromChunkStream - unhandled chunk version (%d)", iChunkVersion ) );
    }
}

void Axon_Sense_Vision::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
    xStream << static_cast< int >( ChunkData::CURRENT_VERSION );

    // We would do our parent chunk now, if it existed.

    // Our data.
    xStream << m_fRangeModifier;
}

void Axon_Sense_Vision::Update( const float fTimeslice )
{
}

bool Axon_Sense_Vision::HandleMessage( const Asura_Message* pxMessage )
{
	return false;
}

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

bool Axon_Sense_Vision::ShouldCullOnRange( const float fRange, const Asura_Vector_3& xToEntity )
{
    return ( fRange > m_fRange * m_fRangeModifier );
}

bool Axon_Sense_Vision::ShouldCullOnFOV( const float fRange, const Asura_Vector_3& xToEntity )
{
    Asura_Matrix_3x3 xOrientMatrix;
    Asura_Maths::OrientationFromDirection( xOrientMatrix, m_xDirection );
    const Asura_Vector_3 xLocalSpacePoint = xOrientMatrix.ApplyTranspose( xToEntity );
    return !IsPointInFrustum( xLocalSpacePoint );
}

bool Axon_Sense_Vision::IsPointInFrustum( const Asura_Vector_3& xPoint )
{
    // adapted from Asura_Camera::IsSphereInFrustum

    // xPoint should already be in our local space

    // behind us
    if ( xPoint.z < 0.0f )
    {
        return false;
    }

    // left and right planes
    const float fZX = xPoint.z * m_fTanFovX;
    if ( ( xPoint.x - fZX ) > 0.0f || ( -xPoint.x - fZX ) > 0.0f )
    {
        return false;
    }

    // top and bottom planes
    if ( xPoint.y < 0.0f )
    {
        // top
        const float fZY = xPoint.z * m_fTanFovYUp;
        if ( -xPoint.y - fZY > 0.0f )
        {
            return false;
        }
    }
    else
    {
        // bottom
        const float fZY = xPoint.z * m_fTanFovYDown;
        if ( xPoint.y - fZY > 0.0f )
        {
            return false;
        }
    }

    return true;
}

void Axon_Sense_Vision::DestroyAllKnowledge()
{
    Asura_Hashed_List_Deleter_It< Axon_Knowledge > xVisionListIter( &m_xVisionList );
    while ( !xVisionListIter.Done() )
    {
        Axon_Knowledge* pxToDelete = xVisionListIter.GetCurrent();
        if ( pxToDelete )
        {
            xVisionListIter.DeleteCurrent();
            m_pxKnowledgeSystem->DestroyKnowledge( pxToDelete );
        }
    }
}

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

#endif
