/**********************************************************************
 *<
	FILE: MNNormalSpec.h

	DESCRIPTION:  User-specifiable normals for MNMeshes - Luna task 747

	CREATED BY: Steve Anderson

	HISTORY: created January 2002

 *>	Copyright (c) 2002 Autodesk, Inc., All Rights Reserved.
 **********************************************************************/

// Necessary prior inclusions: max.h, mnmesh.h

#ifndef __MN_NORMALS_H_
#define __MN_NORMALS_H_

#include "maxheap.h"
#include "baseinterface.h"
#include "ipipelineclient.h"

#define MN_NORMAL_SPEC_INTERFACE Interface_ID(0x7b7c2c5f, 0xf94260f)

class MNNormalFace: public MaxHeapOperators {
	int mDegree;
	int *mpNormalID;
	BitArray mSpecified;

public:
	MNNormalFace () : mDegree(0), mpNormalID(NULL) { }
	DllExport MNNormalFace (int degree);
	~MNNormalFace () { Clear(); }
	DllExport void Init();
	DllExport void Clear();

	// Data accessors
	int GetDegree() { return mDegree; }
	DllExport void SetDegree (int degree);
	// Low-level - do not use mSpecified data!
	int GetNormalID(int corner) { return (mpNormalID && (corner<mDegree)) ? mpNormalID[corner] : -1; }
	void SetNormalID (int corner, int norm) { if (mpNormalID && (corner<mDegree)) mpNormalID[corner] = norm; }

	///  \brief  Return a pointer to the internal array of normal IDs.  This array has length == GetDegree().  
	int *GetNormalIDArray () { return mpNormalID; }

	bool GetSpecified (int corner) { return (mpNormalID && (corner<mDegree) && mSpecified[corner]) ? true : false; }
	void SetSpecified (int corner, bool value=true) { if (mpNormalID && (corner<mDegree)) mSpecified.Set (corner, value); }

	///  \brief  Set all normals on this face to be specified. 
	void SpecifyAll (bool value=true) { if (value) mSpecified.SetAll(); else mSpecified.ClearAll(); }
	DllExport void SpecifyNormalID (int corner, int norm);

	DllExport void MakePoly (int degree, int *pNormalID);
	DllExport void Insert (int pos, int num=1);
	DllExport void Delete (int pos, int num=1);
	DllExport void RotateStart (int newstart);
	DllExport void Flip ();	// Reverses order of verts.  0 remains start.

	DllExport MNNormalFace & operator= (const MNNormalFace & from);
	DllExport MNNormalFace & operator= (const MNFace & from);
	DllExport void ShallowTransfer (MNNormalFace & from);
	DllExport void MNDebugPrint (bool printAll=false);

	DllExport IOResult Save (ISave *isave);
	DllExport IOResult Load (ILoad *iload);
};

// Class MNNormalSpec flags:
#define MNNORMAL_NORMALS_BUILT 0x01
#define MNNORMAL_NORMALS_COMPUTED 0x02
#define MNNORMAL_DISPLAY_HANDLES 0x04
#define MNNORMAL_FACE_ANGLES 0x08	// Used face angles last time we computed normals

// Default length for normal display
#define MNNORMAL_LENGTH_DEFAULT 10.0f

class MNNormalSpec : public IPipelineClient, public FlagUser {
private:
	int mNumNormalAlloc, mNumFaceAlloc;
	int mNumNormals, mNumFaces;
	MNNormalFace *mpFace;
	Point3 *mpNormal;
	BitArray mNormalExplicit;	// Indicates whether mpNormal[i] is explicit or computed from face normals.

	// Display and selection data:
	BitArray mNormalSel;
	float mDisplayLength;

	// We also maintain a pointer to the parent MNMesh
	// (NOTE that the MNMesh MUST keep this pointer updated at all times!)
	MNMesh *mpParent;

public:
	MNNormalSpec () : mpFace(NULL), mpNormal(NULL), mNumNormalAlloc(0),
		mNumFaceAlloc(0), mNumNormals(0), mNumFaces(0), mpParent(NULL),
		mDisplayLength(MNNORMAL_LENGTH_DEFAULT) { }
	~MNNormalSpec () { ClearAndFree (); }

	// Initialization, allocation:
	DllExport void Initialize ();	// Initializes all data members - do not use if already allocated!
	DllExport bool NAlloc (int num, bool keep=TRUE);
	DllExport void NShrink ();	// shrinks allocation down to actual number of normals.
	DllExport bool FAlloc (int num, bool keep=TRUE);
	DllExport void FShrink ();
	DllExport void Clear ();	// Deletes everything.
	DllExport void ClearAndFree ();	// Deletes everything, frees all memory

	// Data access:
	// Lowest level:
	int GetNumFaces () const { return mNumFaces; }
	DllExport bool SetNumFaces (int numFaces);
	int GetNumNormals () const { return mNumNormals; }
	DllExport bool SetNumNormals (int numNormals);

	Point3 & Normal (int normID) const { return mpNormal[normID]; }
	Point3 * GetNormalArray () const { return mpNormal; }
	bool GetNormalExplicit (int normID) const { return mNormalExplicit[normID] ? true : false; }
	void SetNormalExplicit (int normID, bool value) { mNormalExplicit.Set (normID, value); }

	///  \brief  Set all normals in this MNNormalSpec to be explicit. 
	void SetAllExplicit (bool value=true) { if (value) mNormalExplicit.SetAll(); else mNormalExplicit.ClearAll (); }
	MNNormalFace & Face(int faceID) const { return mpFace[faceID]; }
	MNNormalFace * GetFaceArray () const { return mpFace; }

	void SetParent (MNMesh *pMesh) { mpParent = pMesh; }

	// Data access - higher level:
	DllExport Point3 & GetNormal (int face, int corner);
	DllExport void SetNormal (int face, int corner, Point3 & normal);
	DllExport int GetNormalIndex (int face, int corner);
	DllExport void SetNormalIndex (int face, int corner, int normalIndex);
	DllExport int NewNormal (Point3 & normal, bool explic=true);

	DllExport void SetSelection (BitArray & newSelection);
	BitArray & GetSelection() { return mNormalSel; }
	void SetDisplayLength (float displayLength) { mDisplayLength = displayLength; }
	float GetDisplayLength () { return mDisplayLength; }

	DllExport void CollapseDeadFaces ();	// Requires an accurate mpParent pointer.

	// Display and hit testing - note that these require an accurate mpParent pointer.
	DllExport void Display (GraphicsWindow *gw, bool showSel);
	DllExport bool HitTest (GraphicsWindow *gw, HitRegion *hr, DWORD flags, SubObjHitList& hitList);
	DllExport Box3 GetBoundingBox (Matrix3 *tm=NULL, bool selectedOnly=false);

	// This method dumps all unspecified normals.  Best to use only from within CheckNormals.
	DllExport void ClearNormals ();

	// Fills in the mpSpecNormal data by building all the unspecified normals,
	// and computing non-explicit ones.
	// Does nothing if normal faces not allocated yet!
	// Requires an accurate mpParent pointer.
	DllExport void BuildNormals ();

	// This method just recomputes the directions of non-explicit normals,
	// without rebuilding the normal list.
	// Requires an accurate mpParent pointer.
	DllExport void ComputeNormals ();

	// This checks our flags and calls BuildNormals, ComputeNormals as needed.
	// Requires an accurate mpParent pointer.
	DllExport void CheckNormals ();

	// operators and debug printing
	DllExport MNNormalSpec & operator= (const MNNormalSpec & from);
	DllExport void CopySpecified (const MNNormalSpec & from);	// Like operator=, but omits unspecified.
	DllExport MNNormalSpec & operator+= (const MNNormalSpec & from);
	DllExport void MNDebugPrint (bool printAll=false);
	DllExport bool CheckAllData (int numParentFaces);

	DllExport IOResult Save (ISave *isave);
	DllExport IOResult Load (ILoad *iload);

	// From BaseInterface:
	Interface_ID GetID() {return MN_NORMAL_SPEC_INTERFACE;}
	DllExport void DeleteInterface();
	DllExport BaseInterface* GetInterface(Interface_ID id);
	DllExport BaseInterface* CloneInterface(void* remapDir = NULL);

	// --- IPipelineClient methods
	DllExport void ShallowCopy( IPipelineClient* from, ChannelMask channels );
	DllExport void DeepCopy( IPipelineClient* from, ChannelMask channels );
	DllExport void NewAndCopyChannels( ChannelMask channels );
	DllExport void FreeChannels( ChannelMask channels, int zeroOthers = 1 );
	DllExport void ZeroChannels( ChannelMask channels );
	DllExport void AppendAllChannels( IPipelineClient* from );

	// Actual operations:
	DllExport bool Transform (Matrix3 & xfm, BOOL useSel=false, BitArray *normalSelection=NULL);
	DllExport bool Translate (Point3 & translate, BOOL useSel=true, BitArray *normalSelection=NULL);
	DllExport bool BreakNormals (BOOL useSel=true, BitArray *normalSelection=NULL, BOOL toAverage=false);
	// Requires an accurate mpParent pointer:
	DllExport bool UnifyNormals (BOOL useSel=true, BitArray *normalSelection=NULL, BOOL toAverage=false);
	DllExport bool AverageNormals (BOOL useThresh=false, float threshold=0.0f, BOOL useSel=true, BitArray *normalSelection=NULL);
	DllExport bool SpecifyNormals (BOOL useSel=true, BitArray *normalSelection=NULL);
	DllExport bool MakeNormalsExplicit (BOOL useSel=true, BitArray *normalSelection=NULL, bool value=true);
	DllExport bool ResetNormals (BOOL useSel=true, BitArray *normalSelection=NULL);
};

#endif //__MN_NORMALS_H_
