//
// Asura_Zone.cpp
//
// Created by Richard Rice - 25/01/05
// 
// Copyright (C) 2005 Rebellion, All Rights Reserved.
//

//////////////////////////////////////////////////////////////////////////////
//
// includes
//
//////////////////////////////////////////////////////////////////////////////

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

// header
#include "Zone/Asura_Zone.h"

// asura
#include "ChunkSystem/Asura_Chunk_Macros.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Asura_OrientedVolume.h"

//////////////////////////////////////////////////////////////////////////////
//
// Asura_Zone
//
//////////////////////////////////////////////////////////////////////////////

u_int Asura_Zone::s_uNextZoneID = ASURA_INVALID_ZONE_ID + 1;

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

Asura_Zone::Asura_Zone( const bool bCreateFromLoad )
: m_uZoneID( bCreateFromLoad ? static_cast<u_int>(ASURA_INVALID_ZONE_ID) : s_uNextZoneID++ ),
  m_iNumberOfVolumes( 0 ),
  m_pxVolumes( NULL ),
  m_uFlags( 0 ),
  m_iPreviousVolumeIndex( -1 )
#ifdef ASURA_TOOLS
  ,
  m_xName( "" )
#endif
{
}

Asura_Zone::~Asura_Zone()
{
    delete [] m_pxVolumes;
}

bool Asura_Zone::Contains(const Asura_Vector_3& xPos) const
{
	if(!IsEnabled()) return false;

    if(m_iPreviousVolumeIndex>=0 && m_iPreviousVolumeIndex<m_iNumberOfVolumes)
    {
        //check the most recent volume
        if(m_pxVolumes[m_iPreviousVolumeIndex].Contains(xPos))
        {
            return true;
        }
    }

    //check the other volumes
    for(int i=0;i<m_iNumberOfVolumes;i++)
    {
        if(i==m_iPreviousVolumeIndex) continue;
        
        if(m_pxVolumes[i].Contains(xPos))
        {
            m_iPreviousVolumeIndex = i;
            return true;
        }
    }
    return false;   
}

bool Asura_Zone::Contains(const Asura_Vector_3& xPos, int iVolumeIndex) const
{
	if(!IsEnabled()) return false;

	Asura_Assert(iVolumeIndex>=0 && iVolumeIndex< m_iNumberOfVolumes,"");

        
    if(m_pxVolumes[iVolumeIndex].Contains(xPos))
    {
        m_iPreviousVolumeIndex = iVolumeIndex;
        return true;
    }
    return false;   
}


void Asura_Zone::InitialiseFromChunkBuffer(const char*& pcChunkBuffer)
{
	Asura_Chunk_ChunkBuffer_Stream xStream(pcChunkBuffer);
	Asura_Zone::ReadFromChunkStream(xStream);
}


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

    if ( iVersion == ChunkData::CURRENT_VERSION )
    {
        xStream >> m_uZoneID;
        xStream >> m_iNumberOfVolumes;
        xStream >> m_uFlags;

        if ( m_iNumberOfVolumes )
        {
            m_pxVolumes = new Asura_OrientedVolume[ m_iNumberOfVolumes ];
            for ( int i = 0; i < m_iNumberOfVolumes; ++i )
            {
                xStream >> m_pxVolumes[i].m_xBox;
                xStream >> m_pxVolumes[i].m_xOrient;
            }
        }

        #ifdef ASURA_TOOLS
		xStream >> m_xName;
        #else
		xStream.SkipString();
        #endif

        // make sure we can still create zones with valid zone IDs
        s_uNextZoneID = Asura_Maths::Max( s_uNextZoneID, m_uZoneID + 1 );
    }
    else
    {
		//(not bothering with version 0 for this function)
        Asura_Assertf( false, ( "Asura_Zone::ReadFromChunkStream - unhandled chunk version (%d)", iVersion ) );
    }
}

#ifdef ASURA_TOOLS

void Asura_Zone::FillInChunkBuffer(char*& pcChunkBuffer)
{
	Asura_Chunk_ChunkBuffer_Stream xStream(pcChunkBuffer);
	Asura_Zone::WriteToChunkStream(xStream);
}

int Asura_Zone::GetChunkBufferSize()
{
	Asura_Chunk_Dummy_Stream xStream;
	Asura_Zone::WriteToChunkStream(xStream);

	return xStream.GetPosition();
}

void Asura_Zone::WriteToChunkStream( class Asura_Chunk_Stream& xStream)
{
    xStream << (int) ChunkData::CURRENT_VERSION;

	Asura_WriteToStream_Bitfield_Wrapper<u_int>	xFlagsBitfield (m_uFlags);
	xFlagsBitfield.Add(m_bDefaultZone, 1);
	xFlagsBitfield.Add(0, 31);

    xStream << m_uZoneID;
	xStream << m_iNumberOfVolumes;
	xStream << xFlagsBitfield;

	//RAR 08/03/2006 For recentreing during level exports :
	const Asura_Vector_3& xPositionOffset = Tools_Level_Export::GetExportCentre();

    for ( int i = 0; i < m_iNumberOfVolumes; ++i )
    {
		Asura_Bounding_Box xBB = m_pxVolumes[ i ].m_xBox;
		xBB.Displace(-xPositionOffset);

        xStream << xBB;
        xStream << m_pxVolumes[ i ].m_xOrient;
    }

	xStream << m_xName;
}

#endif //ASURA_TOOLS

#ifdef ASURA_TOOLS
void Asura_Zone::GetAABBForVolume(int iIndex, Asura_Bounding_Box& xBox) const
{
	Asura_Assert(iIndex>=0 && iIndex<m_iNumberOfVolumes,"");

	const Asura_OrientedVolume& xVolume = m_pxVolumes[iIndex];

	if(xVolume.m_xOrient.w == 1.0f)
	{
		xBox = xVolume.m_xBox;
	}
	else
	{
		//using orientation
		Asura_Matrix_3x3 xMat;
		xVolume.m_xOrient.ToMatrix(xMat);

	    const Asura_Vector_3 xCentre  = xVolume.m_xBox.CalculateCentre();
	    const Asura_Vector_3 xExtents = xVolume.m_xBox.CalculateExtents();

	    float fHalfAABBExtentX = 0.5f * (   Asura_Maths::Abs(xMat.e11 * xExtents.x)
	                                      + Asura_Maths::Abs(xMat.e12 * xExtents.y)
	                                      + Asura_Maths::Abs(xMat.e13 * xExtents.z) );

	    float fHalfAABBExtentY = 0.5f * (   Asura_Maths::Abs(xMat.e21 * xExtents.x)
	                                      + Asura_Maths::Abs(xMat.e22 * xExtents.y)
	                                      + Asura_Maths::Abs(xMat.e23 * xExtents.z) );

	    float fHalfAABBExtentZ = 0.5f * (   Asura_Maths::Abs(xMat.e31 * xExtents.x)
	                                      + Asura_Maths::Abs(xMat.e32 * xExtents.y)
	                                      + Asura_Maths::Abs(xMat.e33 * xExtents.z) );

	    xBox.MinX = -fHalfAABBExtentX;
	    xBox.MaxX =  fHalfAABBExtentX;
	    xBox.MinY = -fHalfAABBExtentY;
	    xBox.MaxY =  fHalfAABBExtentY;
	    xBox.MinZ = -fHalfAABBExtentZ;
	    xBox.MaxZ =  fHalfAABBExtentZ;

	    xBox.Displace( xCentre );
	}
	
}
#endif //ASURA_TOOLS

float Asura_Zone::GetVolumeSize(int iVolumeIndex) const
{
	Asura_Assert(iVolumeIndex>=0 && iVolumeIndex < m_iNumberOfVolumes,"");	

	return m_pxVolumes[iVolumeIndex].m_xBox.GetVolume();
}

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