/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
//																			//
//	plDrawableSpans Header													//
//																			//
//	The base plDrawable derivative for ice (triangle list)					//
//	drawables. Contains the basic structure for spans and the functions		//
//	to deal with them.														//
//																			//
//// Version History /////////////////////////////////////////////////////////
//																			//
//	4.3.2001 mcn - Created.													//
//	5.3.2001 mcn - Completely revamped. Now plDrawableSpans *IS* the group	//
//				   of spans, and each span is either an icicle or patch.	//
//				   This eliminates the need entirely for separate drawables,//
//				   at the cost of having to do a bit of extra work to		//
//				   maintain different types of spans in the same drawable.  //
//  4.20.2006 bob - Patches are long gone. Only icicle (or particle) spans  //
//					now.
//																			//
//////////////////////////////////////////////////////////////////////////////

#ifndef _plDrawableSpans_h
#define _plDrawableSpans_h


#include "hsBitVector.h"
#include "hsTemplates.h"
#include "plDrawable.h"
#include "hsBounds.h"
#include "hsMatrix44.h"
#include "plSpanTypes.h"

class plPipeline;
class plMessage;
class hsGMaterial;
class plGeometrySpan;
class plSpaceTree;
class plFogEnvironment;
class plLightInfo;
class plGBufferGroup;
class plParticleCore;
class plAccessSpan;
class plAccessVtxSpan;
class plAccessTriSpan;
class plVisMgr;
class plVisRegion;
class plClusterGroup;

//// Class Definition ////////////////////////////////////////////////////////

class plDISpanIndex
{
public:
	enum {
		kNone					= 0x0,
		kMatrixOnly				= 0x1,
		kDontTransformSpans		= 0x2		// Only used for particle systems right now
	};
	UInt8				fFlags;
	hsTArray<UInt32>	fIndices;

	hsBool		IsMatrixOnly() const { return 0 != (fFlags & kMatrixOnly); }
	hsBool		DontTransform() const { return 0 != ( fFlags & kDontTransformSpans ); }
	void		Append(UInt32 i) { fIndices.Append(i); }
	void		Reset() { fFlags = kNone; fIndices.Reset(); }
	void		SetCountAndZero(int c) { fIndices.SetCountAndZero(c); }
	UInt32		GetCount() const { return fIndices.GetCount(); }
	UInt32&		operator[](int i) const { return fIndices[i]; }
};

struct hsColorRGBA;

class plDrawableSpans : public plDrawable
{
	protected:

		static const UInt32		kSpanTypeMask;
		static const UInt32		kSpanIDMask;
		static const UInt32		kSpanTypeIcicle;
		static const UInt32		kSpanTypeParticleSpan;

		UInt32				fType;

		hsBool				fReadyToRender;

		hsBounds3Ext		fLocalBounds;
		hsBounds3Ext		fWorldBounds;
		hsBounds3Ext		fMaxWorldBounds;

		hsMatrix44			fLocalToWorld;
		hsMatrix44			fWorldToLocal;

		hsTArray<hsMatrix44>	fLocalToWorlds;
		hsTArray<hsMatrix44>	fWorldToLocals;

		hsTArray<hsMatrix44>	fLocalToBones;
		hsTArray<hsMatrix44>	fBoneToLocals;

		hsTArray<hsGMaterial *>	fMaterials;

		mutable plSpaceTree*	fSpaceTree;

		hsBitVector				fVisSet; // the or of all our spans visset's. Doesn't have to be exact, just conservative.
		hsBitVector				fVisNot; // same, but for visregions that exclude us.
		mutable hsBitVector		fLastVisSet; // Last vis set we were evaluated against.
		mutable hsBitVector		fLastVisNot; // Last exclusion set we were evaluated agains.
		hsBitVector				fVisCache; // the enabled section of the space tree

		hsTArray<plIcicle>			fIcicles;
		hsTArray<plParticleSpan>	fParticleSpans;

		hsTArray<plSpan *>			fSpans;				// Pointers into the above two arrays
		hsTArray<UInt32>			fSpanSourceIndices;	// For volatile drawables only
		hsTArray<plGBufferGroup *>	fGroups;
		hsTArray<plDISpanIndex*>	fDIIndices;

		UInt32				fProps;
		UInt32				fCriteria;
		plRenderLevel		fRenderLevel;
		plLoadMask			fLoadMask;

		hsBool				fRegisteredForRecreate, fNeedCleanup;
		hsBool				fRegisteredForRender;

		hsBitVector			fParticleSpanVector;
		hsBitVector			fBlendingSpanVector;
		hsBitVector			fFakeBlendingSpanVector;

		plKey				fSceneNode;

		hsBool				fSettingMatIdxLock;

		UInt32				fSkinTime;

		/// Export-only members
		hsTArray<plGeometrySpan *>	fSourceSpans;
		hsBool						fOptimized;

		virtual void	IQuickSpaceTree( void ) const;

		// Temp placeholder function. See code for comments.
		void	IUpdateMatrixPaletteBoundsHack( );

		void	ICleanupMatrices();
		void	IRemoveGarbage( void );
		void	IAdjustSortData( plGBufferTriangle *triList, UInt32 count, UInt32 threshhold, Int32 delta );

		// The following two functions return true if they create a new span, false if it's just an instance
		hsBool	IConvertGeoSpanToVertexSpan( plGeometrySpan *geoSpan, plVertexSpan *span, int lod, plVertexSpan *instancedParent );
		hsBool	IConvertGeoSpanToIcicle( plGeometrySpan *geoSpan, plIcicle *icicle, int lod, plIcicle *instancedParent = nil );

		void	IUpdateIcicleFromGeoSpan( plGeometrySpan *geoSpan, plIcicle *icicle );
		void	IUpdateVertexSpanFromGeoSpan( plGeometrySpan *geoSpan, plVertexSpan *span );

		UInt32	IXlateSpanProps( UInt32 props, hsBool xlateToSpan );

		UInt32	IAddAMaterial( hsGMaterial *material );
		UInt32	IRefMaterial( UInt32 index );
		void	ICheckToRemoveMaterial( UInt32 materialIdx );

		// Annoying to need this, but necessary until materials can test for properties on any of their layers (might add in the future)
		hsBool	ITestMatForSpecularity( hsGMaterial *mat );

		void	IAssignMatIdxToSpan( plSpan *span, hsGMaterial *mtl );

		// Create the sorting data for a given span and flag it as sortable
		void			ICheckSpanForSortable( UInt32 idx ) { if( !(fSpans[idx]->fProps & plSpan::kPropFacesSortable) )IMakeSpanSortable(idx); }
		void			IMakeSpanSortable( UInt32 index );

		/// Bit vector build thingies
		virtual void			IBuildVectors( void );

		hsBool					IBoundsInvalid(const hsBounds3Ext& bnd) const;

		/// EXPORT-ONLY FUNCTIONS
		// Packs the span indices
		void	IPackSourceSpans( void );
		// Sort the spans
		void	ISortSourceSpans( void );
		// Compare two spans for sorting
		short	ICompareSpans( plGeometrySpan *span1, plGeometrySpan *span2 );
		// Find a buffer group of the given format (returns its index into fGroups)
		UInt8	IFindBufferGroup( UInt8 vtxFormat, UInt32 numVertsNeeded, int lod, hsBool vertVolatile, hsBool idxVolatile);
		// Write a span to a stream
		void	IWriteSpan( hsStream *s, plSpan *span );
		/// EXPORT-ONLY FUNCTIONS

		/// DYNAMIC FUNCTIONS
		plDISpanIndex	*IFindDIIndices( UInt32 &index );
		void			IRebuildSpanArray( void );
		plParticleSpan	*ICreateParticleIcicle( hsGMaterial *material, plParticleSet *set );

	    virtual void	SetKey(plKey k);

	public:
		plDrawableSpans();
		virtual ~plDrawableSpans();

		CLASSNAME_REGISTER( plDrawableSpans );
		GETINTERFACE_ANY( plDrawableSpans, plDrawable );

		virtual void SetNativeTransform(UInt32 idx, const hsMatrix44& l2w, const hsMatrix44& w2l);
		virtual plDrawable& SetTransform( UInt32 index, const hsMatrix44& l2w, const hsMatrix44& w2l);
		virtual const hsMatrix44& GetLocalToWorld( UInt32 index = (UInt32)-1 ) const;
		virtual const hsMatrix44& GetWorldToLocal( UInt32 index = (UInt32)-1 ) const;

		virtual plDrawable& SetProperty( UInt32 index, int prop, hsBool on );
		virtual hsBool GetProperty( UInt32 index, int prop ) const;

		virtual plDrawable& SetProperty( int prop, hsBool on );
		virtual hsBool GetProperty( int prop ) const;

		virtual plDrawable& SetNativeProperty( int prop, hsBool on ) { if( on ) fProps |= prop; else fProps &= ~prop; return *this; }
		virtual hsBool GetNativeProperty( int prop ) const { return ( fProps & prop ) ? true : false; }

		virtual plDrawable& SetNativeProperty( UInt32 index, int prop, hsBool on );
		virtual hsBool GetNativeProperty( UInt32 index, int prop ) const;

		virtual plDrawable& SetSubType( UInt32 index, plSubDrawableType t, hsBool on );
		virtual UInt32 GetSubType( UInt32 index ) const; // returns or of all spans with this index (index==-1 is all spans).

		virtual UInt32	GetType( void ) const { return fType; }
		virtual void	SetType( UInt32 type ) { fType = type; }

		virtual void SetRenderLevel(const plRenderLevel& l);
		virtual const plRenderLevel& GetRenderLevel() const;

		const hsBounds3Ext& GetLocalBounds( UInt32 index = (UInt32)-1 ) const;
		const hsBounds3Ext& GetWorldBounds( UInt32 index = (UInt32)-1 ) const;
		const hsBounds3Ext& GetMaxWorldBounds( UInt32 index = (UInt32)-1 ) const;

		virtual void Read(hsStream* s, hsResMgr* mgr);
		virtual void Write(hsStream* s, hsResMgr* mgr);

		virtual plSpaceTree*	GetSpaceTree() const { if(!fSpaceTree)IQuickSpaceTree(); return fSpaceTree; }
		virtual void			SetSpaceTree(plSpaceTree* st) const;
		virtual void			SetVisSet(plVisMgr* visMgr);
		virtual void			SetDISpanVisSet(UInt32 diIndex, hsKeyedObject* reg, hsBool on);

		virtual const plSpan				*GetSpan( UInt32 index ) const { return fSpans[ index ]; }
		virtual const plSpan				*GetSpan( UInt32 diIndex, UInt32 index ) const { return fSpans[ (*fDIIndices[ diIndex ])[ index ] ]; }
		virtual const UInt32				GetNumSpans( void ) const { return fSpans.GetCount(); }
		virtual const hsTArray<plSpan *>	&GetSpanArray( void ) const { return fSpans; }

		hsMatrix44* GetMatrixPalette(int baseMatrix) const { return &fLocalToWorlds[baseMatrix]; }
		const hsMatrix44& GetPaletteMatrix(int i) const { return fLocalToWorlds[i]; }
		void SetInitialBone(int i, const hsMatrix44& l2b, const hsMatrix44& b2l);

		// Get the vertex buffer ref of a given group
		hsGDeviceRef	*GetVertexRef( UInt32 group, UInt32 idx );
		// Get the index buffer ref of a given group
		hsGDeviceRef	*GetIndexRef( UInt32 group, UInt32 idx );

		// BufferGroups accessed only by Pipeline and it's close personal acquaintances.
		plGBufferGroup*							GetBufferGroup(UInt32 i) const { return fGroups[i]; }
		UInt32									GetNumBufferGroups() const { return fGroups.GetCount(); }
		const hsTArray<plGeometrySpan*>&		GetSourceSpans() const { return fSourceSpans; }

		void			DirtyVertexBuffer(UInt32 group, UInt32 idx);
		void			DirtyIndexBuffer(UInt32 group, UInt32 idx);

		// Prepare all internal data structures for rendering
		virtual void	PrepForRender( plPipeline *p );
		void			SetNotReadyToRender() { fReadyToRender = false; }

		virtual hsBool		MsgReceive( plMessage* msg );

		// These two should only be called by the SceneNode
		virtual plKey GetSceneNode() const { return fSceneNode; }
		virtual void SetSceneNode(plKey newNode);

		// Lookup a material in the material array
		hsGMaterial		*GetMaterial( UInt32 index ) const { return ( ( index == (UInt32)-1 ) ? nil : fMaterials[ index ] ); }
		UInt32			GetNumMaterials( void ) const { return fMaterials.GetCount(); }

		// Convert intermediate data into export/run-time-ready data
		virtual void	Optimize( void );
		// Called by the sceneNode to determine if we match the criteria
		virtual hsBool	DoIMatch( const plDrawableCriteria& crit );
		// To set the criteria that this ice fits
		void			SetCriteria( const plDrawableCriteria& crit );

		// Get a bitVector of the spans that are particle spans
		virtual hsBitVector	const	&GetParticleSpanVector( void ) const;

		// Get a bitVector of the spans that are blending (i.e. numMatrices > 0)
		virtual hsBitVector	const	&GetBlendingSpanVector( void ) const;

		// Set a single bit in the bitVector of spans that are blending
		virtual void	SetBlendingSpanVectorBit( UInt32 bitNumber, hsBool on );

		// Taking span index. DI Index doesn't make sense here, because one object's DI can dereference into many materials etc.
		virtual hsGMaterial*	GetSubMaterial(int index) const;
		virtual hsBool			GetSubVisDists(int index, hsScalar& minDist, hsScalar& maxDist) const; // return true if span invisible before minDist and/or after maxDist

		// Used by the pipeline to keep from reskinning on multiple renders per frame.
		UInt32 GetSkinTime() const { return fSkinTime; }
		void SetSkinTime(UInt32 t) { fSkinTime = t; }

		void			UnPackCluster(plClusterGroup* cluster);

		/// EXPORT-ONLY FUNCTIONS
		virtual UInt32	AddDISpans( hsTArray<plGeometrySpan *> &spans, UInt32 index = (UInt32)-1);
		virtual plDISpanIndex&	GetDISpans( UInt32 index ) const;

		// Data Access functions
		// Runtime
		hsPoint3& GetPosition(int spanIdx, int vtxIdx);
		hsVector3& GetNormal(int spanIdx, int vtxIdx);

		UInt32 GetNumTris(int spanIdx);
		UInt16* GetIndexList(int spanIdx);

		// Conversion (before geometryspans get tossed (at write)).
		UInt32		CvtGetNumVerts(int spanIdx) const;
		hsPoint3&	CvtGetPosition(int spanIdx, int vtxIdx);
		hsVector3&	CvtGetNormal(int spanIdx, int vtxIdx);

		UInt32		CvtGetNumTris(int spanIdx);
		UInt16*		CvtGetIndexList(int spanIdx);

		plGeometrySpan*		GetGeometrySpan(int spanIdx);

		/// DYNAMIC FUNCTIONS
		virtual void	RemoveDISpans( UInt32 index );
		virtual UInt32	AppendDISpans( hsTArray<plGeometrySpan *> &spans, UInt32 index = (UInt32)-1, hsBool clearSpansAfterAdd = true, hsBool doNotAddToSource = false, hsBool addToFront = false, int lod = 0 );
		virtual UInt32	RefreshDISpans( UInt32 diIndex );
		virtual UInt32	RefreshSpan( UInt32 srcSpanIndex );
		virtual void	RemoveDIMatrixSpans(UInt32 index);
		virtual UInt32	AppendDIMatrixSpans(int n);
		virtual UInt32	FindBoneBaseMatrix(const hsTArray<hsMatrix44>& initL2B, hsBool searchAll) const;
		virtual UInt32	NewDIMatrixIndex();
		void			SortSpan( UInt32 index, plPipeline *pipe );
		void			SortVisibleSpans(const hsTArray<Int16>& visList, plPipeline* pipe);
		void			SortVisibleSpansPartial(const hsTArray<Int16>& visList, plPipeline* pipe);
		void			CleanUpGarbage( void ) { IRemoveGarbage(); }

		/// Funky particle system functions
		virtual UInt32	CreateParticleSystem( UInt32 maxNumEmitters, UInt32 maxNumParticles, hsGMaterial *material );
		virtual void	ResetParticleSystem( UInt32 index );
		virtual void	AssignEmitterToParticleSystem( UInt32 index, plParticleEmitter *emitter );
		
		/// SceneViewer only!
		void			GetOrigGeometrySpans( UInt32 diIndex, hsTArray<plGeometrySpan *> &arrayToFill );
		void			ClearAndSetMaterialCount(UInt32 count);
};


#endif // _plDrawableSpans_h