/**********************************************************************
 *<
	FILE: editmesh.h

	DESCRIPTION:  Edit Mesh OSM

	CREATED BY: Dan Silva & Rolf Berteig

	HISTORY: created 18 March, 1995

 *>	Copyright (c) 1994, All Rights Reserved.
 **********************************************************************/


#ifndef __EDITMESH_H__
#define __EDITMESH_H__

#include "namesel.h"

// These are values for selLevel.
#define EM_OBJECT	0
#define EM_VERTEX	1
#define EM_FACE		2
#define EM_EDGE		3

#define SELTYPE_SINGLE	1
#define SELTYPE_POLY	2
#define SELTYPE_ELEMENT	3

#define EDITMESH_CHANNELS (PART_GEOM|SELECT_CHANNEL|PART_SUBSEL_TYPE|PART_DISPLAY|PART_TOPO|TEXMAP_CHANNEL|PART_VERTCOLOR)
#define CID_EXTRUDE		CID_USER + 972
#define CID_CREATEVERT	CID_USER + 973
#define CID_OBJATTACH	CID_USER + 974
#define CID_BUILDFACE	CID_USER + 975
#define CID_DIVIDEEDGE	CID_USER + 976
#define CID_TURNEDGE	CID_USER + 977
#define CID_WELDVERT	CID_USER + 978

#define MAX_MATID	0xffff

#define UNDEFINED	0xffffffff

class EditMeshMod : public Modifier {
	friend BOOL CALLBACK ObjectParamDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );
	friend BOOL CALLBACK VertexParamDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );
	friend BOOL CALLBACK FaceParamDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );
	friend BOOL CALLBACK EdgeParamDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );
	friend BOOL CALLBACK PolyParamDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );
	friend BOOL CALLBACK ElementParamDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );
	friend BOOL CALLBACK AffectRegionDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
	friend BOOL CALLBACK SurfaceParamDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

	friend class VertexEditRestore;
	friend class VertexSelRestore;
	friend class VertexHideRestore;
	friend class EdgeSelRestore;
	friend class FaceSelRestore;
	friend class ExtrudeRestore;	
	friend class FaceAttribRestore;
	friend class ExtrudeCMode;
	friend class CreateVertCMode;
	friend class WeldVertCMode;
	friend class CreateVertMouseProc;
	friend class DivideEdgeProc;
	friend class TurnEdgeProc;
	friend class DivideEdgeCMode;
	friend class TurnEdgeCMode;
	friend class ObjAttachCMode;
	friend class ObjAttachMouseProc;
	friend class ExtrudeMouseProc;
	friend class BuildFaceCMode;
	friend class BuildFaceMouseProc;
	friend class EMTempData;
	friend class EditMeshData;
	friend class XFormProc;
	friend class EditMeshClassDesc;
	friend class AttachPickMode;

	private:
				
		static HWND hEditMeshParams, hParam2;
		static IObjParam *iObjParams;		
		
		static MoveModBoxCMode *moveMode;
		static RotateModBoxCMode *rotMode;
		static UScaleModBoxCMode *uscaleMode;
		static NUScaleModBoxCMode *nuscaleMode;
		static SquashModBoxCMode *squashMode;
		static SelectModBoxCMode *selectMode;
		static ExtrudeCMode *extrudeMode;
		static CreateVertCMode *createVertMode;
		static WeldVertCMode *weldVertMode;
		static ObjAttachCMode* attachObjMode;
		static AttachPickMode* attachPickMode;
		static BuildFaceCMode* buildFaceMode;
		static TurnEdgeCMode* turnEdgeMode;
		static DivideEdgeCMode* divideEdgeMode;
		static ISpinnerControl *amountSpin;
		static ISpinnerControl *tensSpin;
		static ISpinnerControl *planarSpin;
		static ICustButton *iSingle;
		static ICustButton *iPoly;
		static ICustButton *iElement;

		static BOOL inNormalMove;
		static BOOL inBuildFace;
		static BOOL faceUIValid;
		static float normScale;
		static int selType;
		static BOOL selByVert;

		static float falloff, pinch, bubble;

		RefResult NotifyRefChanged( Interval changeInt,RefTargetHandle hTarget, 
		   PartID& partID, RefMessage message ) { return REF_SUCCEED; }
				
		int selLevel;
		Tab<TSTR*> namedSel[3];
		Tab<DWORD> ids[3];
		int FindSet(TSTR &setName,int level);
		DWORD AddSet(TSTR &setName,int level);
		void RemoveSet(TSTR &setName,int level);
		void ClearSetNames();

	public:
		EditMeshMod();
		~EditMeshMod();

		Interval LocalValidity(TimeValue t);
		ChannelMask ChannelsUsed()  { return EDITMESH_CHANNELS; }
		ChannelMask ChannelsChanged() { return EDITMESH_CHANNELS; }
		void ModifyObject(TimeValue t, ModContext &mc, ObjectState *os, INode *node);
		void NotifyInputChanged(Interval changeInt, PartID partID, RefMessage message, ModContext *mc);
		Class_ID InputType() { return triObjectClassID; }
		
		int CompMatrix(TimeValue t, ModContext& mc, Matrix3& tm, Interval& valid);
		
		// From Animatable
		void DeleteThis() { delete this; }
		void GetClassName(TSTR& s) { s= GetString(IDS_RB_EDITMESHMOD); }
		Class_ID ClassID() { return Class_ID(EDITMESH_CLASS_ID,0);}
		void RescaleWorldUnits(float f);

		// From BaseObject
		int HitTest(TimeValue t, INode* inode, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt, ModContext* mc);
		int Display(TimeValue t, INode* inode, ViewExp *vpt, int flagst, ModContext *mc);
		void GetWorldBoundBox(TimeValue t,INode* inode, ViewExp *vpt, Box3& box, ModContext *mc);

		void GetSubObjectCenters(SubObjAxisCallback *cb,TimeValue t,INode *node,ModContext *mc);
		void GetSubObjectTMs(SubObjAxisCallback *cb,TimeValue t,INode *node,ModContext *mc);
		int SubObjectIndex(HitRecord *hitRec);
		void CloneSelSubComponents(TimeValue t);
		void AcceptCloneSelSubComponents(TimeValue t);

		BOOL SupportsNamedSubSels() {return TRUE;}
		void ActivateSubSelSet(TSTR &setName);
		void NewSetFromCurSel(TSTR &setName);
		void RemoveSubSelSet(TSTR &setName);

		BOOL DependOnTopology(ModContext &mc);

		// Generic xform procedure.
		void XFormVerts( XFormProc *xproc, TimeValue t, Matrix3& partm, Matrix3& tmAxis  );

		// For affect region
		void InitCalcRegion(Point3 center,Point3 delta);
		Point3 CalcRegionAffect(Point3 v,Point3 center,Point3 delta);

		// Affine transform methods		
		void Move( TimeValue t, Matrix3& partm, Matrix3& tmAxis, Point3& val, BOOL localOrigin=FALSE );
		void Rotate( TimeValue t, Matrix3& partm, Matrix3& tmAxis, Quat& val, BOOL localOrigin=FALSE );
		void Scale( TimeValue t, Matrix3& partm, Matrix3& tmAxis, Point3& val, BOOL localOrigin=FALSE );
		
		void TransformStart(TimeValue t);
		void TransformFinish(TimeValue t);
		void TransformCancel(TimeValue t);

		void BeginNormalMove(TimeValue t);
		void NormalMove( TimeValue t, float amount );
		void EndNormalMove(TimeValue t,BOOL accept=TRUE);

		void StartExtrudeMode();
		void DoExtrude();

		// Called when in sub-object selection and the user hits the delete key.
		void DeleteKey();
		
		// Called when the weld button is pressed
		BOOL WeldVerts(float thresh,int type,Point3 weldPoint=Point3(0,0,0));

		// Called when the user click in a viewport to add a vertex.
		void AddNewVertex(Point3 pt);

		// Creates a new object from selected faces.
		void DetachVertFaces(TSTR &name,BOOL doFaces,BOOL del=TRUE);

		// Attaches the given object to the first ModContext
		void AttachObject(TriObject *obj,INode *node);

		// Builds a face in the given meshData given 3 vert indices.
		void BuildNewFace(EditMeshData *meshData,int *v);

		// Sets the visibility of selected edges
		void SetSelEdgeVis(BOOL vis);
		void AutoEdge(float thresh);
		void DivideSelEdges();
		void TurnSelEdges();		
		void CollapseSelEdges();
		void CollapseSelFaces();
		void MakePlanar();
		void HideSelFaces();
		void UnhideAllFaces();	
		void HideSelVerts();
		void UnhideAllVerts();
		void ExplodeFaces(float thresh,BOOL objs,TSTR &name);
		void TesselateFaces(float tens,BOOL edge);

		void SetSelType(int type);

		// Causes the smooth group and mat index UI to be updated at the
		// next available time.
		void InvalidateSurfaceUI();

		// Get the commonality of smooth groups for the selection
		DWORD GetSelSmoothBits(DWORD &invalid);
		DWORD GetUsedSmoothBits();
		void SetSelSmoothBits(DWORD bits,DWORD which); // 'which' is a mask. Only bits that are 1 in 'which' are changed to the value given by 'bits'
		void AutoSmooth(float thresh);
		void SelectBySmoothGroup(DWORD bits,BOOL clear);		

		// Get the commonality of material index for the selection (-1 indicates no commonality)
		int GetSelMatIndex();
		void SetSelMatIndex(int index);
		void SelectByMat(int index,BOOL clear);

		// Sets the normal display state based on the 'showNorms' class variable
		void ShowNormals();

		void FlipSelNormals();
		void UnifySelNormals();


		void ClearMeshDataFlag(ModContextList& mcList,DWORD f);
		void DeleteMeshDataTempData();		
		void CreateMeshDataTempData();

		int NumRefs() { return 0; }
		RefTargetHandle GetReference(int i) { return NULL; }
		void SetReference(int i, RefTargetHandle rtarg) {}

		// IO
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		IOResult SaveLocalData(ISave *isave, LocalModData *ld);
		IOResult LoadLocalData(ILoad *iload, LocalModData **pld);
		IOResult LoadNamedSelChunk(ILoad *iload,int level);

		CreateMouseCallBack* GetCreateMouseCallBack() { return NULL; } 
		void BeginEditParams( IObjParam  *ip, ULONG flags,Animatable *prev);
		void EndEditParams( IObjParam *ip, ULONG flags,Animatable *next);
		RefTargetHandle Clone(RemapDir& remap = NoRemap());
		TCHAR *GetObjectName() { return GetString(IDS_RB_EDITMESH); }
		void ActivateSubobjSel(int level, XFormModes& modes );
		int NeedUseSubselButton() { return 0; }
		HitRecord *VertHitsToFaceHits(HitRecord *hitRec,BOOL all);
		void SelectSubComponent(HitRecord *hitRec, BOOL selected, BOOL all, BOOL invert);
		void ClearSelection(int selLevel);
		void SelectAll(int selLevel);
		void InvertSelection(int selLevel);

		void SetRollupPage(IObjParam *ip);
		void RemoveRollupPage(IObjParam *ip);
	};


// Table to convert selLevel values to mesh selLevel flags.
const int meshLevel[] = {MESH_OBJECT,MESH_VERTEX,MESH_FACE,MESH_EDGE,0,0};

// Get display flags based on selLevel.
const DWORD levelDispFlags[] = {0,DISP_VERTTICKS|DISP_SELVERTS,DISP_SELFACES,DISP_SELEDGES,0,0};

// For hit testing...
const int hitLevel[] = {0,SUBHIT_VERTS,SUBHIT_FACES,SUBHIT_EDGES,0,0};

const int meshDlg[] = {IDD_EMPARAM_OBJECT,IDD_EMPARAM_VERTEX,IDD_EMPARAM_FACE,IDD_EMPARAM_EDGE};
const int meshDlgString[] = {IDS_RB_EDITOBJECT,IDS_RB_EDITVERTEX,IDS_RB_EDITFACE,IDS_RB_EDITEDGE};
const DLGPROC meshDlgProc[] = {ObjectParamDlgProc,VertexParamDlgProc,FaceParamDlgProc,EdgeParamDlgProc};


class EditMeshClassDesc:public ClassDesc {
	public:
	int 			IsPublic() { return GetSystemSetting(SYSSET_ENABLE_EDITMESHMOD); }
	void *			Create(BOOL loading = FALSE ) { return new EditMeshMod; }
	const TCHAR *	ClassName() { return GetString(IDS_RB_EDITMESH_CLASS); }
	SClass_ID		SuperClassID() { return OSM_CLASS_ID; }
	Class_ID		ClassID() { return Class_ID(EDITMESH_CLASS_ID,0); }
	const TCHAR* 	Category() { return GetString(IDS_RB_DEFEDIT);}
	void			ResetClassParams(BOOL fileReset);
	};

void ResetEditMeshUI();

class AnimPoint3 {
	public:	
		Point3 p;
		
		// Unions wouldn't work because Point3 has a copy constructor.
		Control *GetControl() {return *((Control**)&p);}
		void SetControl(Control *c) {*((Control**)&p) = c;}
		AnimPoint3(Point3 pt) : p(pt) {}
		AnimPoint3();
	};

typedef Tab<Point3> Point3Tab;
typedef Tab<AnimPoint3> AnimPoint3Tab;
typedef Tab<Face> FaceTab;

class XFormProc {
	public:
		virtual Point3 proc(Point3& p, Matrix3 &mat, Matrix3 &imat)=0;
		virtual void SetMat( Matrix3& mat ) {}
	};

class MoveXForm : public XFormProc {
	private:
		Point3 delta, tdelta;		
	public:
		Point3 proc(Point3& p, Matrix3 &mat, Matrix3 &imat) 
			{ return p + tdelta; }
		void SetMat( Matrix3& mat ) 
			{ tdelta = VectorTransform(Inverse(mat),delta); }
		MoveXForm(Point3 d) { delta = d; }
	};

class RotateXForm : public XFormProc {
	private:
		Matrix3 rot, trot;
	public:
		Point3 proc(Point3& p, Matrix3 &mat, Matrix3 &imat) 
			{ return (trot*p)*imat; }
		void SetMat( Matrix3& mat ) 
			{ trot = mat * rot; }
		RotateXForm(Quat q) { q.MakeMatrix(rot); }
	};

class ScaleXForm : public XFormProc {
	private:
		Matrix3 scale, tscale;
	public:
		Point3 proc(Point3& p, Matrix3 &mat, Matrix3 &imat) 
			{ return (p*tscale)*imat; }
		void SetMat( Matrix3& mat ) 
			{ tscale = mat*scale; }
		ScaleXForm(Point3 s) { scale = ScaleMatrix(s); }
	};


class VertexDelta {
	public:
		Point3Tab	deltas;  //DS-- I needed this public		
		BitArray	hide;

		void SetSize(int size, BOOL clear=TRUE);
		Point3 operator[](int i);
		void Set(TimeValue t,int i, Point3& p);
		void Move(TimeValue t,int i, const Point3& p);
		void Apply(TimeValue t,Mesh& mesh);
		void Scale(float f);
		VertexDelta& operator=(VertexDelta& from);

		~VertexDelta();
		VertexDelta();
	};


class TopoOp {
	public:
		virtual void Apply(Mesh& mesh)=0;
		virtual TopoOp *Clone()=0;
		virtual ~TopoOp() {}
	};

typedef Tab<TopoOp*> TopoOpTab;

class FaceMap {
	public:
		DWORD v[3];
		FaceMap(DWORD fv[3]) {v[0]=fv[0];v[1]=fv[1];v[2]=fv[2];}
		FaceMap(DWORD a,DWORD b,DWORD c) {v[0]=a;v[1]=b;v[2]=c;}
		FaceMap() {v[0]=v[1]=v[2]=UNDEFINED;}
	};
typedef Tab<FaceMap> FaceMapTab;


// Bits for attribs:
// First 3 bits are edge visibility
// 4th bit is face visibility
// 29,30 and 31 indicate which if any should be applied
#define ATTRIB_APPLY_EDGE		(1<<31)
#define ATTRIB_APPLY_FACE		(1<<30)
#define ATTRIB_APPLY_MATID		(1<<29)
#define ATTRIB_APPLY_SMGROUP	(1<<28)

// Mat ID takes bit 5-21
#define ATTRIB_MATID_SHIFT	5
#define ATTRIB_MATID_MASK	0xffff


class TopoDelta {
	public:				
		DWORDTab cverts;			// Clone verts
		DWORDTab dverts;			// Delete verts		
		Point3Tab nverts;			// Add verts
		Point3Tab nTverts;			// Add texture verts
		DWORDTab dfaces;			// Delete faces
		Tab<Face> nfaces;			// New faces
		Tab<TVFace> nTVfaces;		// New texture vert faces
		FaceMapTab map;				// Remap faces
		FaceMapTab tmap;			// TVFace remap
		DWORDTab attribs;			// Changes attributes of a face.
		DWORDTab smgroups;			// Changes smooth groups for a face.
		DWORD inputFaces; 			// Number of faces on input mesh
		DWORD inputVerts;			// Number of vertices input mesh has
			
		void Apply(Mesh& mesh);
		TopoDelta& operator=(TopoDelta& td);
		~TopoDelta() {}

		// This builds a list that is used to register a clone record.
		// If you want to clone vertex #10, you need to find the index
		// of vertex #10 for the input mesh. If vertex #0 and #1 have
		// been deleted then vertex #10 is really vertex #12 on the input mesh.
		// This table would give: invVert[10] == 12
		// 
		void BuildInvCloneTable(DWORDTab &invVert);

		// Either removes the faces from the list of new faces or records a remove face record
		void RemoveFace(DWORD index);
		
		// Either removes the vertex from the list of new vertices or records a remove vertex record
		void RemoveVertex(DWORD index);
		
		// Remaps a face
		void ModifyFace(DWORD index,Face& face);
		void ModifyFace(DWORD index,DWORD* v);
		
		// Remaps a tvface
		void ModifyTVFace(DWORD index,TVFace& face);
		void ModifyTVFace(DWORD index,DWORD* v);

		// Changes the edge visibility
		void SetEdgeVisibility(DWORD index,DWORD vis);

		// Changes the visibility of a face
		void SetFaceVisibility(DWORD index,BOOL vis);

		// Access the face map for a given face
		FaceMap GetFaceMap(DWORD index);

		void SetSmoothGroup(DWORD index,DWORD sg,DWORD mask);
		void SetMatID(DWORD index,int id);
		
		// Sets the size of the input object
		void SetSize(int nface, int nvert, BOOL uvs, BOOL clear=TRUE);
	};


class AdjEdgeList;
class AdjFaceList;
class EMTempData;


// EditmeshData flags
#define EMD_BEENDONE			(1<<0)
#define EMD_UPDATING_CACHE		(1<<1)
#define EMD_HASDATA				(1<<2)
#define EMD_HELD				(1<<3) // equivalent to A_HELD

// Types of welds
#define WELD_COLLAPSE	1
#define WELD_THRESHOLD	2
#define WELD_TOVERT		3


// This is the data that each mod app will have.
class EditMeshData : public LocalModData {
	public:
		DWORD flags;
		
		// These record the changes to the incomming object.
		VertexDelta	vdelta;
		TopoDelta	tdelta;

		// The selection sets.
		BitArray vsel;
		BitArray fsel;		
		BitArray esel;

		// Lists of named selection sets
		NamedSelSetList vselSet;
		NamedSelSetList fselSet;
		NamedSelSetList eselSet;

		// While an object is being edited, this exists.
		EMTempData *tempData;

		EditMeshData();
		EditMeshData(EditMeshData& emc);
		~EditMeshData() {}
		
		// Applies modifications to a triOb
		void Apply(TimeValue t,TriObject *triOb,int selLevel,int selType, BOOL inBuildFace, float normScale);

		// Invalidates any caches affected by the change.
		void Invalidate(PartID part,BOOL meshValid=TRUE);
		
		// If this is the first edit, then the delta arrays will be allocated
		void BeginEdit(TimeValue t);

		LocalModData *Clone() { return new EditMeshData(*this); }
		
		void SetFlag(DWORD f,BOOL on) 
			{ 
			if ( on ) {
				flags|=f;
			} else {
				flags&=~f; 
				}
			}
		DWORD GetFlag(DWORD f) { return flags&f; }

		EMTempData *TempData(EditMeshMod *mod);

		// Deletes selected vertices and faces attached to them.
		void DeleteVertSet(Mesh *mesh,BitArray &set);
		void DeleteFaceSet(Mesh *mesh,BitArray &set,BitArray *isoVert=NULL);
		void WeldVertSet(Mesh *mesh,BitArray &set,Point3 *weldPoint=NULL);
		BOOL WeldVerts(Mesh *mesh,float thresh,int type,Point3 weldPoint);
		void DivideEdges(Mesh *mesh,AdjEdgeList *ae,BitArray &sel);
		void CollapseEdges(Mesh *mesh,AdjEdgeList *ae);
		void TurnEdges(Mesh *mesh,AdjEdgeList *ae,BitArray &sel);
		void CloneSelVerts(Mesh *mesh);
		void CloneSelFaces(Mesh *mesh);
		void MakeSelPlanar(Mesh *mesh);
		void FaceCenterTessellate(Mesh *mesh);
		void EdgeTessellate(Mesh *mesh, AdjEdgeList *ae, AdjFaceList *af, float tens);
		void ElemExplodeFaces(Mesh *mesh,float thresh,AdjFaceList *af);
		void ObjExplodeFaces(INode *node,TSTR &name,IObjParam *ip,
			Mesh *mesh,float thresh,AdjFaceList *af);
		void AutoSmooth(Mesh *mesh,AdjFaceList &af,AdjEdgeList &ae,float angle);
		void UnifyNormals(Mesh *mesh,AdjFaceList &af);
		void FlipNormal(Mesh *mesh,DWORD face);
		void PropogateFacing(Mesh &mesh,int face,AdjFaceList &af,BitArray &done,BOOL bias=1);
	};

class VertexEditRestore : public RestoreObj {
	public:
		VertexDelta	deltas;
		Point3Tab verts;
		VertexDelta	rdeltas;		
		TimeValue t;

		EditMeshData *meshData;
		EditMeshMod	 *mod;

		virtual ~VertexEditRestore() {};
		VertexEditRestore(EditMeshData* md, EditMeshMod* mod);
		void Restore(int isUndo);
		void Redo();
		int Size() { return 1; }
		void EndHold() {meshData->SetFlag(EMD_HELD,FALSE);}
				
		TSTR Description() { return TSTR(_T("Move Vertices")); }
	};

class TopoEditRestore : public VertexEditRestore {
	public:
		TopoDelta	tdelta;
		BitArray 	vsel;
		BitArray 	fsel;
		BitArray	esel;		
		Tab<Face> 	faces;
		
		TopoDelta	rtdelta;
		BitArray 	rvsel;
		BitArray 	rfsel;		
		BitArray	resel;		

		~TopoEditRestore() {};
		TopoEditRestore(EditMeshData* md, EditMeshMod* mod);
		void Restore(int isUndo);
		void Redo();		
		void EndHold() {meshData->SetFlag(EMD_HELD,FALSE);}				
		TSTR Description() { return TSTR(_T("Delete Vertices")); }
	};

class VertexSelRestore : public RestoreObj {
	public:
		BitArray sel;
		BitArray rsel;
		EditMeshData *meshData;
		EditMeshMod	 *mod;
		TimeValue t;

		~VertexSelRestore() {};
		VertexSelRestore(EditMeshData* md, EditMeshMod* mod);
		void Restore(int isUndo);
		void Redo();
		int Size() { return 1; }

		TSTR Description() { return TSTR(_T("Select Vertices")); }
	};

class VertexHideRestore : public RestoreObj {
	public:
		BitArray hide;
		BitArray rhide;
		EditMeshData *meshData;
		EditMeshMod	 *mod;
		TimeValue t;

		~VertexHideRestore() {};
		VertexHideRestore(EditMeshData* md, EditMeshMod* mod);
		void Restore(int isUndo);
		void Redo();
		int Size() { return 1; }

		TSTR Description() { return TSTR(_T("Hide Vertices")); }
	};

class FaceSelRestore : public RestoreObj {
	public:
		BitArray sel;
		BitArray rsel;
		EditMeshData *meshData;
		EditMeshMod	 *mod;
		TimeValue t;

		~FaceSelRestore() {};
		FaceSelRestore(EditMeshData* md, EditMeshMod* mod);
		void Restore(int isUndo);
		void Redo();
		int Size() { return 1; }

		TSTR Description() { return TSTR(_T("Select Faces")); }
	};

class EdgeSelRestore : public RestoreObj {
	public:
		BitArray sel;
		BitArray rsel;
		EditMeshData *meshData;
		EditMeshMod	 *mod;
		TimeValue t;

		~EdgeSelRestore() {};
		EdgeSelRestore(EditMeshData* md, EditMeshMod* mod);
		void Restore(int isUndo);
		void Redo();
		int Size() { return 1; }

		TSTR Description() { return TSTR(_T("Select Edges")); }
	};


class FaceAttribRestore : public RestoreObj {
	public:
		DWORDTab attribs;
		DWORDTab rattribs;

		EditMeshData *meshData;
		EditMeshMod	 *mod;		

		~FaceAttribRestore() {};
		FaceAttribRestore(EditMeshData* md, EditMeshMod* mod);
		void Restore(int isUndo);
		void Redo();
		int Size() {return 1;}

		TSTR Description() { return TSTR(_T("Face attributes")); }
	};

class FaceSmoothGroupRestore : public RestoreObj {
	public:
		DWORDTab smgroups;
		DWORDTab rsmgroups;

		EditMeshData *meshData;
		EditMeshMod	 *mod;		

		~FaceSmoothGroupRestore() {};
		FaceSmoothGroupRestore(EditMeshData* md, EditMeshMod* mod);
		void Restore(int isUndo);
		void Redo();
		int Size() {return 1;}
		void EndHold() {meshData->SetFlag(EMD_HELD,FALSE);}

		TSTR Description() { return TSTR(_T("Smooth groups")); }
	};


class ExtrudeRestore : public RestoreObj {
	public:
		DWORDTab	vmap;
		DWORDTab	ivmap;		
				
		DWORDTab	cverts;
		Tab<Face> 	nfaces;
		int nv, nf;

		EditMeshData *meshData;
		EditMeshMod	 *mod;
		
		ExtrudeRestore(EditMeshData* md, EditMeshMod* mod);
		~ExtrudeRestore() {};
		void Restore(int isUndo);
		void Redo();
		int Size() { return 1; }
	};

class ExtrudeMouseProc : public MouseCallBack {
	private:
		MoveTransformer moveTrans;
		EditMeshMod *em;
		IObjParam *ip;
		IPoint2 om;

	public:
		ExtrudeMouseProc(EditMeshMod* mod, IObjParam *i)
			: moveTrans(i) {em=mod;ip=i;}
		int proc( 
			HWND hwnd, 
			int msg, 
			int point, 
			int flags, 
			IPoint2 m );
	};

class ExtrudeSelectionProcessor : public GenModSelectionProcessor {
	protected:
		HCURSOR GetTransformCursor();
		
	public:
		ExtrudeSelectionProcessor(ExtrudeMouseProc *mc, Modifier *m, IObjParam *i) 
			: GenModSelectionProcessor(mc,m,i) {}
	};

class ExtrudeCMode : public CommandMode {
	private:
		ChangeFGObject fgProc;
		ExtrudeSelectionProcessor mouseProc;
		ExtrudeMouseProc eproc;
		EditMeshMod* em;

	public:
		ExtrudeCMode(EditMeshMod* mod, IObjParam *i) :
			fgProc(mod), mouseProc(&eproc,mod,i), eproc(mod,i) {em=mod;}

		int Class() { return MODIFY_COMMAND; }
		int ID() { return CID_EXTRUDE; }
		MouseCallBack *MouseProc(int *numPoints) { *numPoints=2; return &mouseProc; }
		ChangeForegroundCallback *ChangeFGProc() { return &fgProc; }
		BOOL ChangeFG( CommandMode *oldMode ) { return oldMode->ChangeFGProc() != &fgProc; }
		void EnterMode();
		void ExitMode();
	};


// Weld vert

class WeldVertMouseProc : public MoveModBox {
	private:		
		EditMeshMod *em;
		IObjParam *ip;		

	public:
		WeldVertMouseProc(EditMeshMod* mod, IObjParam *i)
			: MoveModBox(mod,i) {em=mod;ip=i;}		
		BOOL HitTestVerts(IPoint2 &m, ViewExp *vpt,int &v);
		int proc( 
			HWND hwnd, 
			int msg, 
			int point, 
			int flags, 
			IPoint2 m );
	};

class WeldVertSelectionProcessor : public GenModSelectionProcessor {
	protected:
		HCURSOR GetTransformCursor();
		
	public:
		WeldVertSelectionProcessor(WeldVertMouseProc *mc, Modifier *m, IObjParam *i) 
			: GenModSelectionProcessor(mc,m,i) {}
	};

class WeldVertCMode : public CommandMode {
	private:
		ChangeFGObject fgProc;
		WeldVertSelectionProcessor mouseProc;
		WeldVertMouseProc eproc;
		EditMeshMod* em;

	public:
		WeldVertCMode(EditMeshMod* mod, IObjParam *i) :
			fgProc(mod), mouseProc(&eproc,mod,i), eproc(mod,i) {em=mod;}

		int Class() { return MODIFY_COMMAND; }
		int ID() { return CID_WELDVERT; }
		MouseCallBack *MouseProc(int *numPoints) { *numPoints=2; return &mouseProc; }
		ChangeForegroundCallback *ChangeFGProc() { return &fgProc; }
		BOOL ChangeFG( CommandMode *oldMode ) { return oldMode->ChangeFGProc() != &fgProc; }
		void EnterMode();
		void ExitMode();
	};


class CreateVertMouseProc : public MouseCallBack {
	private:		
		EditMeshMod *em;
		IObjParam *ip;		

	public:
		CreateVertMouseProc(EditMeshMod* mod, IObjParam *i)
			{em=mod;ip=i;}
		int proc( 
			HWND hwnd, 
			int msg, 
			int point, 
			int flags, 
			IPoint2 m );
	};

class CreateVertCMode : public CommandMode {
	private:
		ChangeFGObject fgProc;		
		CreateVertMouseProc proc;
		EditMeshMod* em;

	public:
		CreateVertCMode(EditMeshMod* mod, IObjParam *i) 
			: fgProc(mod), proc(mod,i) {em=mod;}

		int Class() { return MODIFY_COMMAND; }
		int ID() { return CID_CREATEVERT; }
		MouseCallBack *MouseProc(int *numPoints) {*numPoints=1; return &proc;}
		ChangeForegroundCallback *ChangeFGProc() {return &fgProc;}
		BOOL ChangeFG(CommandMode *oldMode) {return oldMode->ChangeFGProc()!= &fgProc;}
		void EnterMode();
		void ExitMode();
	};



class ObjAttachMouseProc : public MouseCallBack {
	private:		
		EditMeshMod *em;
		IObjParam *ip;		

	public:
		ObjAttachMouseProc(EditMeshMod* mod, IObjParam *i)
			{em=mod;ip=i;}
		int proc( 
			HWND hwnd, 
			int msg, 
			int point, 
			int flags, 
			IPoint2 m );
	};

class ObjAttachCMode : public CommandMode {
	private:
		ChangeFGObject fgProc;		
		ObjAttachMouseProc proc;
		EditMeshMod* em;

	public:
		ObjAttachCMode(EditMeshMod* mod, IObjParam *i) 
			: fgProc(mod), proc(mod,i) {em=mod;}

		int Class() { return MODIFY_COMMAND; }
		int ID() { return CID_OBJATTACH; }
		MouseCallBack *MouseProc(int *numPoints) {*numPoints=1; return &proc;}
		ChangeForegroundCallback *ChangeFGProc() {return &fgProc;}
		BOOL ChangeFG(CommandMode *oldMode) {return oldMode->ChangeFGProc()!= &fgProc;}
		void EnterMode();
		void ExitMode();
	};


class AttachPickMode : 
		public PickModeCallback,
		public PickNodeCallback {
	public:		
		EditMeshMod* em;
		IObjParam *ip;

		AttachPickMode(EditMeshMod* mod, IObjParam *i)
			{em=mod;ip=i;}
		BOOL HitTest(IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags);				
		BOOL Pick(IObjParam *ip,ViewExp *vpt);
		void EnterMode(IObjParam *ip);
		void ExitMode(IObjParam *ip);		
		
		BOOL Filter(INode *node);
		BOOL RightClick(IObjParam *ip,ViewExp *vpt) {return TRUE;}
		PickNodeCallback *GetFilter() {return this;}
	};


class BuildFaceMouseProc : public MouseCallBack {
	private:		
		EditMeshMod *em;
		IObjParam *ip;		
		EditMeshData *meshData;
		int vts[3];
		IPoint3 mpts[3];
		int pt;
		IPoint2 lm;

	public:
		BuildFaceMouseProc(EditMeshMod* mod, IObjParam *i);
		void DrawFace(HWND hWnd,IPoint2 &m);
		BOOL HitTestVerts(IPoint2 &m, ViewExp *vpt,int &v);
		int proc( 
			HWND hwnd, 
			int msg, 
			int point, 
			int flags, 
			IPoint2 m );
	};

class BuildFaceCMode : public CommandMode {
	private:
		ChangeFGObject fgProc;		
		BuildFaceMouseProc proc;
		EditMeshMod* em;

	public:
		BuildFaceCMode(EditMeshMod* mod, IObjParam *i) 
			: fgProc(mod), proc(mod,i) {em=mod;}

		int Class() { return MODIFY_COMMAND; }
		int ID() { return CID_BUILDFACE; }
		MouseCallBack *MouseProc(int *numPoints) {*numPoints=999999; return &proc;}
		ChangeForegroundCallback *ChangeFGProc() {return &fgProc;}
		BOOL ChangeFG(CommandMode *oldMode) {return oldMode->ChangeFGProc()!= &fgProc;}
		void EnterMode();
		void ExitMode();
	};



class PickEdgeMouseProc : public MouseCallBack {
	public:
		EditMeshMod *em;
		IObjParam *ip;

		PickEdgeMouseProc(EditMeshMod* mod, IObjParam *i)
			{em=mod;ip=i;}
		HitRecord *HitTestEdges(IPoint2 &m, ViewExp *vpt);
		int proc( 
			HWND hwnd, 
			int msg, 
			int point, 
			int flags, 
			IPoint2 m );
		virtual void EdgePick(EditMeshData *meshData,DWORD edge)=0;
	};

class DivideEdgeProc : public PickEdgeMouseProc {
	public:
		DivideEdgeProc(EditMeshMod* mod, IObjParam *i) : PickEdgeMouseProc(mod,i) {}
		void EdgePick(EditMeshData *meshData,DWORD edge);
	};

class TurnEdgeProc : public PickEdgeMouseProc {
	public:
		TurnEdgeProc(EditMeshMod* mod, IObjParam *i) : PickEdgeMouseProc(mod,i) {}
		void EdgePick(EditMeshData *meshData,DWORD edge);
	};

class DivideEdgeCMode : public CommandMode {
	private:
		ChangeFGObject fgProc;		
		DivideEdgeProc proc;
		EditMeshMod* em;

	public:
		DivideEdgeCMode(EditMeshMod* mod, IObjParam *i) 
			: fgProc(mod), proc(mod,i) {em=mod;}

		int Class() { return MODIFY_COMMAND; }
		int ID() { return CID_DIVIDEEDGE; }
		MouseCallBack *MouseProc(int *numPoints) {*numPoints=1; return &proc;}
		ChangeForegroundCallback *ChangeFGProc() {return &fgProc;}
		BOOL ChangeFG(CommandMode *oldMode) {return oldMode->ChangeFGProc()!= &fgProc;}
		void EnterMode();
		void ExitMode();
	};

class TurnEdgeCMode : public CommandMode {
	private:
		ChangeFGObject fgProc;		
		TurnEdgeProc proc;
		EditMeshMod* em;

	public:
		TurnEdgeCMode(EditMeshMod* mod, IObjParam *i) 
			: fgProc(mod), proc(mod,i) {em=mod;}

		int Class() { return MODIFY_COMMAND; }
		int ID() { return CID_TURNEDGE; }
		MouseCallBack *MouseProc(int *numPoints) {*numPoints=1; return &proc;}
		ChangeForegroundCallback *ChangeFGProc() {return &fgProc;}
		BOOL ChangeFG(CommandMode *oldMode) {return oldMode->ChangeFGProc()!= &fgProc;}
		void EnterMode();
		void ExitMode();
	};

//-----------------------------------------------------------------
//
//
#include "edmdata.h"

class EMTempData {
	private:
		// Data to keep cached while
		AdjEdgeList 	*adjList;
		AdjFaceList 	*faceAdjList;
		FaceClusterList *faceCluster;
		DWORDTab		*vertCluster;
		EdgeClusterList	*edgeCluster;
		Point3Tab		*cnormals;
		Point3Tab       *ccenters;
		Interval alValid;
		BOOL lockInvalidate;

		Mesh 			*mesh;
		Point3Tab 		*vnormals;
		Interval 		meshValid;
		
		EditMeshMod 	*mod;
		EditMeshData 	*meshData;

	public:		
		
		~EMTempData();
		EMTempData(EditMeshMod *m,EditMeshData *md);
		void Invalidate(PartID part,BOOL meshValid=TRUE);
		
		AdjEdgeList 	*AdjList(TimeValue t);
		AdjFaceList 	*FaceAdjList(TimeValue t);
		FaceClusterList *FaceCluster(TimeValue t);
		EdgeClusterList *EdgeCluster(TimeValue t);
		DWORDTab		*VertCluster(TimeValue t);
		Point3Tab		*ClustNormals(TimeValue t);
		Point3Tab		*ClustCenters(TimeValue t);
		Point3Tab		*VertNormals(TimeValue t);
		Mesh 			*GetMesh(TimeValue t);
		
		BOOL MeshCached(TimeValue t);
		void UpdateCache(TriObject *triOb);
	};

class EditMeshDeleteEvent : public EventUser {
	public:
		EditMeshMod *em;

		void Notify() {if (em) em->DeleteKey();}
		void SetEditMeshMod(EditMeshMod *e) {em=e;}
	};
extern EditMeshDeleteEvent delEvent;

void ExtrudeFaces( 
		Mesh& mesh,
		BitArray& facesel,
		ExtrudeRestore *xrest, 
		AdjEdgeList& el, 
		TopoDelta& tdelta,
		VertexDelta& vdelta,
		EditMeshData *meshData,
		BOOL doFace );

void BuildClusterNormals( Mesh& mesh, FaceClusterList& clust, 
	Point3Tab& normals, Point3Tab& centers );
void BuildAverageNormals( Mesh& mesh, Point3Tab& normals );
void MatrixFromNormal( Point3& normal, Matrix3& mat );
void PolyFromFace(Mesh& mesh,AdjFaceList &al,DWORD f, DWORDTab& sel,float thresh);
void SwapEdgeVerts(Face *f,DWORD& v1,DWORD& v2);
void SelectElementFromFace(Mesh &mesh,AdjFaceList &al,DWORD f,BitArray &set,BitArray &fdone);
int GetEdgeIndex(Face &f,DWORD v0,DWORD v1);
DWORD GetOtherIndex(Face *f,DWORD v0,DWORD v1);
BOOL EdgeSelected(Face *faces,BitArray& esel,MEdge *e);
float AngleBetweenFaces(Mesh *mesh,DWORD f0, DWORD f1);
float AffectRegionFunction(float dist,float falloff,float pinch,float bubble);

void MeshDeleteVertices(Mesh &mesh,BitArray& set);
void MeshDeleteFaces(Mesh &mesh,BitArray& set);

void Point3TabDeleteSet(Point3Tab& tab,BitArray &set);
void BitArrayDeleteSet(BitArray &ba,BitArray &set,int m=1);

void SetupHideFlags(Mesh &mesh, BitArray &hide);
DWORD GetFaceVertIndex(Face &f,DWORD v);

BOOL GetDetachObjectName(HWND hDlg,TSTR &name,Interface *ip);

void DeselectHiddenFaces(Mesh &mesh);

#endif

