//
// Tools_ViewCell.h
//
// Created by KJFL - 01 June 2007
//
// Copyright (C) 2007 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
#pragma once
#endif

#ifndef __TOOLS_VIEWCELL_H__
#define __TOOLS_VIEWCELL_H__

#ifdef ASURA_USE_NEW_VISIBILITY_SYSTEM

#include "Core/Asura_RingBuffer.h"

class Asura_Bounding_Box;
template< typename T > class Asura_LinkList;
class Asura_Vector_3;
class Tools_VisibilitySegment;

const float fTOOLS_VIEWCELL_MIN_EXTENT = 1.0f;
const float fTOOLS_VIEWCELL_MIN_VOLUME = fTOOLS_VIEWCELL_MIN_EXTENT * fTOOLS_VIEWCELL_MIN_EXTENT * fTOOLS_VIEWCELL_MIN_EXTENT;

class Tools_VisibilitySegment
{
	public:

		static Tools_VisibilitySegment* Create( bool bFromRefinement, const Asura_Vector_3& xP0, const Asura_Vector_3& xP1, u_int uVisibleFeatureID0, u_int uVisibleFeatureID1 );
        static void DestroyAll( void );
        static const Asura_RingBuffer<Tools_VisibilitySegment>& GetRefinementSegments( void );

		inline const Asura_Vector_3& GetPoint( u_int uIndex ) const
		{
			return m_xPoint[ uIndex ];
		}

		inline u_int GetVisibleFeatureID( u_int uIndex ) const
		{
			return m_uVisibleFeatureID[ uIndex ];
		}

		inline float GetMin( u_int uAxis ) const
		{
			return Asura_Maths::Min( m_xPoint[0].GetElement(uAxis), m_xPoint[1].GetElement(uAxis) );
		}
		inline float GetMax( u_int uAxis ) const
		{
			return Asura_Maths::Max( m_xPoint[0].GetElement(uAxis), m_xPoint[1].GetElement(uAxis) );
		}


	private:

        friend class Asura_RingBuffer<Tools_VisibilitySegment>;

		Tools_VisibilitySegment() {}
		~Tools_VisibilitySegment() {}
		
		// KJFL - 11 December 2007 - I used to store Min and Max directly here, but in the end
		// the speed up was minimal, so now the methods above calculate it directly when required.
		// Being only 32 bytes per segment has its own advantages.
		Asura_Vector_3	m_xPoint[2];
		u_int			m_uVisibleFeatureID[2];
};


class Tools_Visibility_SplitHintQuad
{
public:
    u_int m_uAxis;
    float m_fAxisValue;
    Asura_Bounding_Box m_xAABB;
};


class Tools_ViewCell
{
	public:

        typedef bool(*FIND_SPLIT_PLANE_CALLBACK)( const Tools_ViewCell* pxViewCell, u_int& uBestAxis, float& fBestPlane, bool& bForceSplit );
        static void SetFindSplitPlaneCallback( FIND_SPLIT_PLANE_CALLBACK pfnFindSplitPlaneCallback );

        static void SetSplitHintQuadList( const Asura_LinkList<Tools_Visibility_SplitHintQuad>* pxList );

		static Tools_ViewCell* Create( const Asura_Bounding_Box& xAABB,  const Tools_VisibilitySegment** ppxCandidateList, u_int uNumberOfCandidates, u_int uDepth );

		static void Destroy( Tools_ViewCell* pxCell );

		bool AABBContainsVisibilitySegment( const Tools_VisibilitySegment* pxSegment );

		bool RaySegmentIntersectsViewCell( const Asura_Vector_3& xStart, const Asura_Vector_3& xEnd ) const;

		void AddVisibilitySegment( const Tools_VisibilitySegment* pxSegment );

		void SetVisibleFeaturesArray( void );

		void CreateLeftChild( const Asura_Bounding_Box& xAABB );

		void CreateRightChild( const Asura_Bounding_Box& xAABB );

		Tools_ViewCell* GetLeftChild( void ) const { return m_pxLeftChild; }

		Tools_ViewCell* GetRightChild( void ) const { return m_pxRightChild; }

		const Asura_Bounding_Box& GetAABB( void ) const { return m_xAABB; }

		bool HasInternalLinks( void ) const { return m_bHasInternalLinks; }

		u_int GetDepth( void ) const { return m_uDepth; }

		u_int GetNumberOfSegments( void ) const { return m_uNumberOfSegments; }

		void DebugRender( void );

		void DebugLogFile( void );

		bool FindBestSplittingPlane( u_int& uBestAxis, float& fBestPlane, bool& bForceSplit );
        bool CheckSplitHintQuadList( u_int& uBestAxis, float& fBestPlane, bool& bForceSplit );

		void SplitBoundingBox( u_int uAxis, float fPlane, Asura_Bounding_Box& xLeft, Asura_Bounding_Box& xRight );

		void GetVisibleFeatures( u_char* pucVisibleFeaturesArray, u_char ucBitPattern ) const;

		void RefineVisibility( u_int uFeatureID0, u_int uFeatureID1 );

		u_int GetLeafIndex( void ) const { return m_uLeafIndex; }

		void SetLeafIndex( u_int uLeafIndex );

		void SetLeavesAsVisible( Tools_ViewCell** ppxLeafCells, u_int uNumberOfLeaves, u_int uTotalNumberOfLeaves  );

		const bool* GetVisibleFeaturesArray( void ) const { return m_pbVisibleFeatures; }
	
		const bool* GetVisibleLeavesArray( void ) const { return m_pbVisibleViewCells; }
	
	private:

		Asura_Bounding_Box					m_xAABB;

		Tools_ViewCell*						m_pxLeftChild;
		Tools_ViewCell*						m_pxRightChild;

		u_int								m_uDepth;

		u_int								m_uNumberOfSegments;
		const Tools_VisibilitySegment**		m_ppxSegmentList;

		u_int								m_uLeafIndex;

		bool*								m_pbVisibleFeatures;

		bool*								m_pbVisibleViewCells;

		u_int								m_bHasInternalLinks :1;

};

#endif// ASURA_USE_NEW_VISIBILITY_SYSTEM

#endif//__TOOLS_VIEWCELL_H__

