/*==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==*/

#ifndef plDistributor_inc
#define plDistributor_inc

#include "../plMath/plRandom.h"
#include "plDistTree.h"

class INode;
class Mesh;
class TriObject;

class BitmapTex;
class plLayerTex;
class plExportProgressBar;
class plMaxNode;
class plDistTree;

// To Use:
//
// First you must set the interface
// Second, you must set the node(s) to be replicated.
//		If multiple replicants, they will be randomly selected at each plant site.
// Set the spacing. Default is 10 or so, but shouldn't be counted on.
// Set the spacing range. Set it to less than half the spacing to prevent
//		replicants piling up on each other.
// Rather than increasing the spacing range (which causes pileup), you can increase
//		the randomness of placement with the overall probability factor. Reducing it
//		means fewer grid points will be used, so placement seems more random
//
// Options:
//		PolarRange - defines the cone about the surface normal to randomly fill for up direction
//		PolarBunch - defines tendency to bunch around the center of PolarRange cone,
//			with zero being uniform distribution, and one being all on cone axis.
//		AlignmentVector - defines a world space preferred up orientation.
//		AlignmentWeight - blend factor between random normal (from polar stuff) and
//			above AlignmentVector.
//		ScaleRange - defines range of non-uniform scale to apply to each replicant.
//		ProbabilityTexmap - A BitmapTex which maps the probability of an instance
//			taking root at any point on the surface. Various interpretations of the
//			map available (see ProbabilityChan). If the surface mesh doesn't have
//			the appropriate uvw mapping for the map, the map will be ignored.
//		ProbabilityChan - defines interpretation of texel value from ProbTexmap
//			into a probability. ColorChan enum types are pretty self-explanatory.
//			In all cases, a higher texel channel value means more likely of a
//			replicant taking root there.
//
// Finally, call Distrubute() with a node containing a surface to be populated.
//
// Modifying. Were actually creating the new node instances, but then we just
// want to re-pack them into clusters anyway. So all we really need is to
// know which template (fRepNodes) and what transform to use for it. We can
// use that to make our clusters, without bogging Max down with adding and
// deleting a gazillion INodes.

class plDistribInstance
{
public:
	INode*			fNode;
	Matrix3			fNodeTM;
	Matrix3			fObjectTM;

	INode*			fBone;
	BOOL			fRigid;

	Box3			fFade;

	Point3			fFlex;

	Mesh*			fMesh;
};

class plDistribInstTab : public Tab<plDistribInstance>
{
};

class plMeshCache
{
public:
	Mesh*		fMesh;
	Point3		fFlex;

	plMeshCache() {}
};

class plMeshCacheTab : public Tab<plMeshCache>
{
};

class plDistributor
{
public:
	enum ColorChan
	{
		kRed						= 0x1,
		kGreen						= 0x2,
		kBlue						= 0x4,
		kAlpha						= 0x8,
		kAverageRedGreen			= kRed | kGreen,
		kAverageRedGreenTimesAlpha	= kRed | kGreen | kAlpha,
		kAverage					= kRed | kGreen | kBlue,
		kAverageTimesAlpha			= kAverage | kAlpha,
		kMax						= 0x100,
		kMaxColor					= kMax | kRed | kGreen | kBlue,
		kMaxColorTimesAlpha			= kMaxColor | kAlpha,
		kMaxRedGreen				= kMax | kRed | kGreen,
		kMaxRedGreenTimesAlpha		= kMaxRedGreen | kAlpha
	};
	enum 
	{
		kLockNone		= 0x0,
		kLockX			= 0x1,
		kLockY			= 0x2,
		kLockZ			= 0x4
	};
	enum
	{
		kWgtMapChan			= 66,
		kNormMapChan		= 67
	};
	enum IsoType
	{
		kIsoNone,
		kIsoLow,
		kIsoMedium,
		kIsoHigh,

		kIsoMax = kIsoHigh
	};
	enum ConformType
	{
		kConformNone,
		kConformAll,
		kConformHeight,
		kConformCheck,
		kConformBase
	};
protected:
	mutable INode*				fSurfNode;
	mutable Mesh*				fSurfMesh;
	mutable TriObject*			fSurfObjToDelete;

	mutable INodeTab			fRepNodes;

	mutable plDistTree*			fDistTree;
	mutable plDistTree			fMeshTree;

	Interface*					fInterface;

	IsoType				fIsolation;
	ConformType			fConformity;
	BOOL				fFaceNormals;
	
	float				fSpacing;
	float				fRndPosRadius;

	Point3				fAlignVec;
	float				fAlignWgt;

	float				fOffsetMin;
	float				fOffsetMax;

	Point3				fAngProbVec;
	float				fAngProbLo;
	float				fAngProbHi;
	float				fAngProbTrans;

	float				fAltProbLo;
	float				fAltProbHi;
	float				fAltProbTrans;

	float				fPolarRange;
	float				fTanPolarRange;
	float				fAzimuthRange;

	float				fOverallProb;

	float				fPolarBunch;

	ULONG				fScaleLock;				
	Point3				fScaleLo;
	Point3				fScaleHi;

	BitmapTex*			fProbBitmapTex;
	plLayerTex*			fProbLayerTex;
	ColorChan			fProbColorChan;

	float				fProbRemapFromLo;
	float				fProbRemapFromHi;
	float				fProbRemapToLo;
	float				fProbRemapToHi;

	float				fMaxConform; // in feet

	Box3				fFade;
	INode*				fBone;
	BOOL				fRigid;

	// Temps used during processing.
	mutable Matrix3		fSurfToWorld;
	mutable Matrix3		fWorldToSurf;
	mutable Matrix3		fSurfToWorldVec;
	mutable Matrix3		fWorldToSurfVec;
	mutable	Point3		fSurfAlignVec;
	mutable Point3		fSurfAngProbVec;
	mutable plRandom	fRand;
	mutable float		fCosAngProbHi;
	mutable float		fCosAngProbHiTrans;
	mutable float		fCosAngProbLo;
	mutable float		fCosAngProbLoTrans;

	void				ISetAngProbCosines() const;
	BOOL				ISetSurfaceNode(INode* node) const;
	BOOL				IGetMesh(INode* node, TriObject*& objToDelete, Mesh*& retMesh) const;

	BOOL				INeedMeshTree() const;
	void				IMakeMeshTree() const;
	void				IFindFaceSet(const Box3& box, Tab<Int32>& faces) const;
	BOOL				IProjectVertex(const Point3& pt, const Point3& dir, float maxDist, Tab<Int32>&faces, Point3& projPt) const;
	BOOL				IConform(Matrix3& l2w, int iRepNode, plMeshCacheTab& cache, int& iCache) const;
	BOOL				IConformHeight(Matrix3& l2w, int iRepNode, plMeshCacheTab& cache, int& iCache) const;
	BOOL				IConformAll(Matrix3& l2w, int iRepNode, plMeshCacheTab& cache, int& iCache) const;
	BOOL				IConformCheck(Matrix3& l2w, int iRepNode, plMeshCacheTab& cache, int& iCache) const;
	BOOL				IConformBase(Matrix3& l2w, int iRepNode, plMeshCacheTab& cache, int& iCache) const;

	Matrix3				IOTM(int iRepNode) const;
	Matrix3				IInvOTM(int iRepNode) const;

	Matrix3				IGenerateTransform(int iRepNode, int iFace, const Point3& pt, const Point3& bary) const;
	hsBool				IProbablyDoIt(int iFace, Point3& del, const Point3& bary) const;
	hsBool				IFailsAltProb(int iFace, const Point3& bary) const;
	hsBool				IFailsAngProb(int iFace, const Point3& bary) const;
	hsBool				IFailsProbBitmap(int iFace, const Point3& bary) const;
	Box3				ISetupGrid(const Point3& p0, const Point3& p1, const Point3& p2) const;
	Point3&				IPerturbPoint(Point3& pt) const;
	int 				ISelectRepNode() const;
	Point3				IPerpAxis(const Point3& p) const;
	Point3				IGetSurfaceNormal(int iFace, const Point3& bary) const;

	BOOL				ISpaceClear(int iRepNode, const Matrix3& l2w, Box3& clearBox, plMeshCacheTab& cache) const;
	void				IReserveSpace(const Box3& clearBox) const;

	void				IReplicate(Matrix3& l2w, int iRep, plDistribInstTab& reps, plMeshCache& mCache) const;
	void				IDistributeOverFace(int iFace, plDistribInstTab& reps, plMeshCacheTab& cache) const;
	BOOL				IDistributeOverMesh(plDistribInstTab& reps, plMeshCacheTab& cache, plExportProgressBar& bar) const;

	void				IClear();
	BOOL				IValidateSettings(INode* surfNode, plMeshCacheTab& cache) const;

	BOOL				ISetupNormals(plMaxNode* node, Mesh* mesh, BOOL radiateNorm) const;
	BOOL				IDuplicate2Sided(plMaxNode* node, Mesh* mesh) const;
	BOOL				ISetupSkinWeights(plMaxNode* node, Mesh* mesh, const Point3& flex) const;
	BOOL				IReadyRepNodes(plMeshCacheTab& cache) const;
public:

	plDistributor();
	virtual ~plDistributor();

	void				SetTheInterface(Interface* i) { fInterface = i; }
	Interface*			GetTheInterface() const { return fInterface; }

	BOOL				Distribute(INode* surfNode, plDistribInstTab& replicants, plMeshCacheTab& cache, plExportProgressBar& bar) const;

	void				Reset();
	UInt32				GetRandSeed() const;
	void				SetRandSeed(int seed);

	void				ClearReplicateNodes();
	void				AddReplicateNode(INode* node);
	int					GetNumReplicateNodes() const { return fRepNodes.Count(); }
	INode*				GetReplicateNode(int i) const { return fRepNodes[i]; }

	INode*				GetSurfaceNode() const { return fSurfNode; }

	void				SetSpacing(float f) { fSpacing = f; }
	float				GetSpacing() const { return fSpacing; }

	void				SetSpacingRange(float f) { fRndPosRadius = f; }
	float				GetSpacingRange() const { return fRndPosRadius; }

	void				SetAlignmentVec(const Point3& v) { fAlignVec = v; }
	Point3				GetAlignmentVec() const { return fAlignVec; }

	void				SetAlignmentWeight(float w) { fAlignWgt = w / 100.f; }
	float				GetAlignmentWeight() const { return fAlignWgt * 100.f; }

	void				SetPolarRange(float deg);
	float				GetPolarRange() const { return hsScalarRadToDeg(fPolarRange); }

	void				SetAzimuthRange(float deg) { fAzimuthRange = hsScalarDegToRad(deg); }
	float				GetAzimuthRange() const { return hsScalarRadToDeg(fAzimuthRange); }

	void				SetOverallProb(float percent) { fOverallProb = percent/100.f; }
	float				GetOverallProb() const { return fOverallProb * 100.f; }

	void				SetAngleProbVec(const Point3& v) { fAngProbVec = v; }
	Point3				GetAngleProbVec() const { return fAngProbVec; }

	void				SetAngleProbHi(float deg) { fAngProbHi = deg; }
	float				GetAngleProbHi() const { return fAngProbHi; }

	void				SetAngleProbLo(float deg) { fAngProbLo = deg; }
	float				GetAngleProbLo() const { return fAngProbLo; }

	void				SetAngleProbTransition(float deg) { fAngProbTrans = deg; }
	float				GetAngleProbTransition() const { return fAngProbTrans; }

	void				SetMinAltitude(float feet) { fAltProbLo = feet; }
	float				GetMinAltitude() const { return fAltProbLo; }

	void				SetMaxAltitude(float feet) { fAltProbHi = feet; }
	float				GetMaxAltitude() const { return fAltProbHi; }

	void				SetAltitudeTransition(float feet) { fAltProbTrans = feet; }
	float				GetAltitudeTransition() const { return fAltProbTrans; }

	void				SetPolarBunch(float b) { fPolarBunch = b/100.f; }
	float				GetPolarBunch() const { return fPolarBunch * 100.f; }

	void				SetScaleRange(const Point3& lo, const Point3& hi) { fScaleLo = lo; fScaleHi = hi; }
	Point3				GetScaleRangeMin() const { return fScaleLo; }
	Point3				GetScaleRangeMax() const { return fScaleHi; }

	void				SetProbabilityBitmapTex(BitmapTex* t);
	BitmapTex*			GetProbabilityBitmapTex() const { return fProbBitmapTex; }

	void				SetProbabilityLayerTex(plLayerTex* t);
	plLayerTex*			GetProbabilityLayerTex() const { return fProbLayerTex; }

	void				SetProbabilityChan(ColorChan c) { fProbColorChan = c; }
	ColorChan			GetProbabilityChan() const { return fProbColorChan; }

	void				SetProbabilityRemapFromLo(float f) { fProbRemapFromLo = f / 255.f; }
	float				GetProbabilityRemapFromLo() const { return fProbRemapFromLo * 255.f; }

	void				SetProbabilityRemapFromHi(float f) { fProbRemapFromHi = f / 255.f; }
	float				GetProbabilityRemapFromHi() const { return fProbRemapFromHi * 255.f; }

	void				SetProbabilityRemapToLo(float f) { fProbRemapToLo = f / 255.f; }
	float				GetProbabilityRemapToLo() const { return fProbRemapToLo * 255.f; }

	void				SetProbabilityRemapToHi(float f) { fProbRemapToHi = f / 255.f; }
	float				GetProbabilityRemapToHi() const { return fProbRemapToHi * 255.f; }

	// We don't really know what fades are, they're just something we're handed that
	// we stamp on every distribInstance we generate. See plDistribComponent.h.
	void				SetFade(const Box3& fade) { fFade = fade; }
	Box3				GetFade() const { return fFade; }

	void				SetBone(INode* b) { fBone = b; }
	INode*				GetBone() const { return fBone; }

	void				SetRigid(BOOL b) { fRigid = b; }
	BOOL				GetRigid() const { return fRigid; }

	void				SetScaleLock(ULONG f) { fScaleLock = f; }
	ULONG				GetScaleLock() const { return fScaleLock; }

	void				SetDistTree(plDistTree* dt) { fDistTree = dt; }
	plDistTree*			GetDistTree() const { return fDistTree; }

	void				SetIsolation(IsoType t) { fIsolation = t; }
	IsoType				GetIsolation() const { return fIsolation; }

	void				SetConformity(ConformType t) { fConformity = t; }
	ConformType			GetConformity() const { return fConformity; }

	void				SetMaxConform(float feet) { fMaxConform = feet; }
	float				GetMaxConform() const { return fMaxConform; }

	void				SetMinOffset(float feet) { fOffsetMin = feet; }
	float				GetMinOffset() const { return fOffsetMin; }

	void				SetMaxOffset(float feet) { fOffsetMax = feet; }
	float				GetMaxOffset() const { return fOffsetMax; }

	void				SetFaceNormals(BOOL on=true) { fFaceNormals = on; }
	BOOL				GetFaceNormals() const { return fFaceNormals; }
};

#endif // plDistributor_inc