/*****************************************************************************
 * $Id: eng_world.hh,v 1.107 2006/11/17 06:35:23 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	World, Quadrant, Locus classes.
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#ifndef ENG_WORLD_HH
#define ENG_WORLD_HH 1

class Quadrant;  // eng_object.hh has QuadrantPtr typedef
class Locus;
class QuadrantObject;
typedef void (*ForEachObjectCallback)( Object* obj );
typedef vector<Quadrant*>::iterator QuadrantItr;
typedef vector<Quadrant*>::const_iterator ConstQuadrantItr;

////////////////////////////////////////////////////////////////////////////////
/// @class Quadrant
/// @brief A quadrant is a grid of the simulated world containing a set of objects.
enum eQuadrantState { eQuadrantState_EMPTY, eQuadrantState_SHRUNK, eQuadrantState_POPULATED };
class Quadrant
{
PREVENT_COPYING(Quadrant)
public:
						Quadrant( const Array<3>& tuple,
                                  const fp edge,
                                  const uint seed,
                                  bool shadow = false );
						~Quadrant();
	// Quadrants states:
	// empty		Has zero objects.
	// populated	Has reproducible and non-reproducible objects
	// shrunk		Has non-reproducible objects
	eQuadrantState		GetState( void ) const	         { return mState; }
	void				SetState( eQuadrantState state ) { mState = state; }
	void				Populate( void );
	void				Shrink( void );
	void				Take( Object& object, QuadrantPtrConst giver );
	const Array<3>&		GetTuple( void ) const { return mTuple; }
	const Cube<fp>&		GetCube( void ) const { return mCube; }
	uint				GetSeed( void ) const { return mSeed; }
	QuadrantPtrConst   	GetShadow( void ) const { return mShadow; }
	uint				GetObjectCnt( void ) const;
	Position			GetPosition( void ) const { return mPosition; }
	Position			GetCenter( void ) const { return mCenter; }
	void				RandomPosition( Position& pos ) const;
	Dlink<Object>*		AddObject( Object* obj );           // not SafePtr<>
	void				RemoveObject( Dlink<Object>* dlink );
	Dlink<Detector>*	AddDetector( Detector* detector );  // not SafePtr<>
	void				RemoveDetector( Dlink<Detector>* dlink );
	Dlist<Object>&      GetObjectDlist( void )	 { return mObjectDlist; }
	Dlist<Detector>&	GetDetectorDlist( void ) { return mDetectorDlist; }
	void				DynaMoved( Dyna& dyna );
	void				DynaMoved( Dyna& dyna, const Position& pos );
						// Don't use IfInside(Posrad) for Fixed objects at ground-level
						// for (n,1,n) Worlds as the radius will extend below ground.
	bool				IfInside( const Position& pos ) const;
	bool				IfInside( const Posrad& posrad ) const;
	bool				IfInsideXZ( const Position& pos ) const;
	bool				IfInsideXZ( const Posrad& posrad ) const;
	bool				IfVisible( void ) const;

protected:
	eQuadrantState		mState;
	static fp			mEdge;
	Quadrant*			mShadow;		///< holds Object managed by ObjectGroup
	uint				mSeed;			///< random seed
	Array<3>			mTuple;			///< for mQuadrants[x,y,z]
	Cube<fp>			mCube;			///< defines the quadrant in world space
	Position			mPosition;		///< origin, equals Cube::x1,y1,z1
	Position			mCenter;		///< center of Quadrant
	uint				mObjectCnt;		///< amount of objects in this quadrant
	Dlist<Object>		mObjectDlist;	///< list of objects in the quadrant
    // Maintainting a Detector list is an optimization vs. calling every
    // Object's Collided() method as mosts Objects don't have Detectors
	Dlist<Detector>		mDetectorDlist;	///< Detector objects of Dynas migrate to other quadrants

private:
	SafePtr<QuadrantObject> mQuadrantObject; ///< to speed visibility test

public:
	DECL_TYPESIG( TYPESIG_QUADRANT )

friend class Locus;
friend ostream& operator<<( ostream& strm, const Quadrant& o );
};

////////////////////////////////////////////////////////////////////////////////
/// @class Locus
/// @brief The Locus is a subset of Quadrants that are being rendered.
/// @remarks Each dimension must be an odd value.  The reason is that
/// the center of the locus is computed by dividing by 2.  For a 3x3x3 Locus,
/// its center is 1x1x1.  A 4x4x4 Locus would be unbalanced with a center of 2x2x2.
class Locus
{
PREVENT_COPYING(Locus)
public:                 // Dimensions must have odd values.
                        Locus( void );
	void                Init( const Array<3>& dim );
                        ~Locus();
	void                Update( const Position& pos );
	void                Update( void );
	uint                GetQuadrantIdx( const Array<3>& tuple ) const;
	QuadrantPtrConst    GetCenterQuadrant( void ) const;
	uint                GetObjectCnt( void ) const;
	uint                GetQuadrantCnt( void ) const { ASSERT(mValid); return mQuadrants.size(); }
	fp                  GetEdge( uint dim ) const;

public:
    typedef vector<Quadrant*>           QuadrantContainer;
    typedef QuadrantContainer::iterator QuadrantIterator;
	QuadrantContainer   mQuadrants;
private:
	Array<3>			mDim;			// each coord must be odd
	Array<3>			mDimHalf;		// pre-computed by constructor
	Array<3>			mCenterTuple;	// pre-computed by constructor
    DEBUG_CODE( bool mValid; )

friend ostream& operator<<( ostream& strm, const Locus& o );
};

////////////////////////////////////////////////////////////////////////////////
/// @class World
/// @brief World class defines the simulated world.  It consists of quadrants.
/// @remarks The world's origin is always at (0,0,0) which simplifies code.
/// This base class doesn't know how to make the world nor populate it with Objects.
class World
{
PREVENT_COPYING(World)
public:
						World( void );		// substituted by Make()
	virtual				~World() { }		// substituted by Destroy()
	virtual void		Make( void ) = 0;
protected:
	void				Make1( bool flightSim, const Array<3>& dimQuadrant, const fp quadrantEdge, const fp margin, const Array<3>& dimLocus );
	void				Make2( void );
public:
	void				Destroy( void );
	bool				IfMade( void ) const { return mMade; }
	virtual void		PopulateQuadrant( QuadrantPtrConst q ) = 0;
	void				ForEachObject( ForEachObjectCallback callback ) const;
	bool        		Fence( Dyna& dyna, Position& pos /*IN/OUT*/ );
private:
	bool        		FenceSub( Dyna& dyna, Position& pos /*IN/OUT*/ );
public:
	const Array<3>&		GetDimQuadrant( void ) const { return mDimQuadrant; }
	uint				GetQuadrantCnt( void ) const { return mQuadrants.size(); }
	fp					GetQuadrantEdge( void ) const { return mQuadrantEdge; }
	uint				GetQuadrantIdx( const Array<3>& tuple ) const;
	uint				GetQuadrantIdx( const Position& pos ) const;
	QuadrantPtrConst	GetQuadrant( const Array<3>& tuple ) const { return mQuadrants[ GetQuadrantIdx(tuple) ]; }
	QuadrantPtrConst   	GetQuadrant( const Position& pos ) const { return mQuadrants[ GetQuadrantIdx(pos) ]; }
	const Array<3>&		GetQuadrantTuple( const Position& pos ) const;
	QuadrantPtrConst    GetCenterQuadrant( void ) const;
	Position            GetCenter( void ) const { ASSERT(mDimValid); return mCenter; }  // subtle: derived-World reassigns mCenter
	bool            	IfCenterQuadrant( QuadrantPtrConst q ) const;
	bool            	IfEdgeQuadrant( QuadrantPtrConst q ) const;
	uint				GetObjectCnt( void ) const { ASSERT(mObjectCnt >= 0); return mObjectCnt; }
	void				IncObjectCnt( void ) { ++mObjectCnt; ++mObjectCntMade; }
	void				DecObjectCnt( void ) { --mObjectCnt; ASSERT(mObjectCnt >= 0); }
	uint				GetObjectCntInCenterQuadrant( void ) const;
	virtual bool		IfSmallObject( const Object& obj ) const = 0;
	void				SetObjectSmallDist ( fp dist );
	void				SetObjectLargeDist( fp dist );
	fp					DistanceOutside( void ) const;
	uint				CacheSize( void );
	void				PreloadCaches( void );
	bool				IfBelowGround( const Position& pos ) const;
	bool				IfBelowGround( const MatrixExt& m ) const;
    virtual fp          GroundLevel( void ) const = 0;
	bool				IfInside( const Position& pos ) const;
						/// GetGroundY() returns the Y coordinate of the terrain at a 3D point.
	fp					GetGroundY( const Position& pos ) const { return WORLD(2.0); } // TODO consult terrain map
    fp                  GetFrustumFar( void ) const;

protected:
	Array<3>			WrapQuadrant( const Array<3>& tuple ) const;

	bool				mMade;
    bool                mFlightSim;         ///< affects interpretation of altitutde, edge/centera quadrants
	uint				mObjectCnt;			///< decremented when an Object is deleted
	uint				mObjectCntMade;		///< never decremented

	// Define the world dimensions:
	fp					mQuadrantEdge;		///< width of quadrant edge (all are equal)
	fp					mMargin;			///< for when Fence() wraps a Dyna
	Array<3>			mDimQuadrant;		///< dimensions of the world (in quadrants)
	Array<3,fp>			mDimWorld;			///< dimensions of the world (in world coords)
	Array<3>			mDimLocus;
	Position			mCenter;			///< center of the center quadrant
	bool				mDimValid;			///< also applies to mQuadrantEdge, mCenter

public:
	vector<Quadrant*>	mQuadrants;			///< Quadrants are divisions of the World
	Locus				mLocus;				///< locus of Quadrants being rendered

	fp					mObjectSmallDist;	///< used by Object::Visible()
	fp					mObjectLargeDist;	///< max distances small/large objects are visible
	fp					mObjectSmallDist2;	///< squared
	fp					mObjectLargeDist2;	///< squared
};

////////////////////////////////////////////////////////////////////////////////
/// @brief QuadrantObject is used to test visibility of entire quadrant.
class QuadrantObject : public FixedMesh
{
PREVENT_COPYING(QuadrantObject)
private:
	typedef FixedMesh Parent;
	enum { eQobj_POLYGON_CNT = 4,	// rectangles
           eQobj_VERTEX_CNT  = eQobj_POLYGON_CNT * 4 };
public:
	explicit			QuadrantObject( QuadrantPtrConst q );
						~QuadrantObject();
	void				FinalConstruction( void ) { }	// constructor sets position/radius
	int					Visible( void );
	bool				Reproducible( void ) { return false; }
	uint				GetVertexCnt( void )   { return eQobj_VERTEX_CNT; }
	uint				GetPolygonCnt( void )  { return eQobj_POLYGON_CNT; }
	const WorldVertex*	GetWorldVertexs( void ) { return mWorldVertexs; }
	const string		GetName( void ) { return string("QuadrantObject"); }

	void Draw( void ) { }							// OBJECT NOP
	void SetVertexCnt( uint vcnt ) { }				// OBJECT NOP
	void SetWorldVertexs( WorldVertex* wv ) { }		// OBJECT NOP

private:
	WorldVertex			mWorldVertexs[eQobj_VERTEX_CNT];
};

//------------------------------------------------------------------------------

// For World class code, pedantic assertions for Quadrants.
// GET_PTR() is compatible with Quadrant* or QuadrantPtr.
#if DEBUG == 2
#define ASSERT_WORLD_QUADRANT( QUADRANT ) \
{{ \
	ASSERT( (QUADRANT) != NULL ); \
    Quadrant* q_ = GET_PTR( (QUADRANT) ); \
	ASSERT( std::find( mQuadrants.begin(), mQuadrants.end(), q_ ) != mQuadrants.end() ); \
	CHECK_TYPESIG( q_, TYPESIG_QUADRANT ); \
}}
#else
#define ASSERT_WORLD_QUADRANT( QUADRANT ) \
{{ \
    CHECK_TYPESIG( (QUADRANT), TYPESIG_QUADRANT ); \
}}
#endif

//------------------------------------------------------------------------------

#endif // ENG_WORLD_HH
