
/**********************************************************************
 *<
	FILE: editpat.h

	DESCRIPTION:  Edit Patch OSM

	CREATED BY: Tom Hudson, Dan Silva & Rolf Berteig

	HISTORY: created 23 June 1995

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


#ifndef __EDITPATCH_H__
#define __EDITPATCH_H__

#include "namesel.h"

#define Alert(x) MessageBox(GetActiveWindow(),x,_T("Alert"),MB_OK);

#define EDITPAT_CHANNELS (PART_GEOM|SELECT_CHANNEL|PART_SUBSEL_TYPE|PART_DISPLAY|PART_TOPO|TEXMAP_CHANNEL)

// These are values for selLevel.
#define EP_OBJECT	0
#define EP_VERTEX	1
#define EP_EDGE		2
#define EP_PATCH	3

#define MAX_MATID	0xffff

#define UNDEFINED	0xffffffff

class VertInsertRecord;
class PickPatchAttach;

class EditPatchMod : public Modifier {
	friend BOOL CALLBACK PatchObjectParamDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );
	friend BOOL CALLBACK PatchPatchParamDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );
	friend BOOL CALLBACK PatchEdgeParamDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );
	friend BOOL CALLBACK PatchVertexParamDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );

	friend class EPTempData;
	friend class EditPatchData;
	friend class XFormProc;
	friend class PatchRestore;
	friend class PVertexRightMenu;
	friend class PatchRightMenu;
	friend class PickPatchAttach;

	private:
				
		static HWND hEditPatchParams;
		static IObjParam *iObjParams;		
		
		static MoveModBoxCMode *moveMode;
		static RotateModBoxCMode *rotMode;
		static UScaleModBoxCMode *uscaleMode;
		static NUScaleModBoxCMode *nuscaleMode;
		static SquashModBoxCMode *squashMode;
		static SelectModBoxCMode *selectMode;
		static ISpinnerControl *weldSpin;
		static ISpinnerControl *stepsSpin;
		static PickPatchAttach pickCB;
		static BOOL patchUIValid;

		// for the tessellation controls
		static BOOL settingViewportTess;  // are we doing viewport or renderer
		static ISpinnerControl *uSpin;
		static ISpinnerControl *vSpin;
		static ISpinnerControl *edgeSpin;
		static ISpinnerControl *angSpin;
		static ISpinnerControl *distSpin;

		RefResult NotifyRefChanged( Interval changeInt,RefTargetHandle hTarget, 
		   PartID& partID, RefMessage message ) { return REF_SUCCEED; }
		
		int selLevel;

		// RB:named sel sets
		Tab<TSTR*> namedSel[3];
		int FindSet(TSTR &setName,int level);
		void AddSet(TSTR &setName,int level);
		void RemoveSet(TSTR &setName,int level);
		void RemoveAllSets();
		void ClearSetNames();

		// Remembered info
		PatchMesh *rememberedPatch;	// NULL if using all selected patches
		int rememberedIndex;
		int rememberedData;

	public:
		BOOL displaySurface;
		BOOL displayLattice;
		int meshSteps;
		BOOL meshAdaptive;	// Future use (Not used now)
		TessApprox viewTess; // for GAP tessellation
		TessApprox prodTess;
		BOOL EPropagate;
		BOOL PPropagate;

		EditPatchMod();
		~EditPatchMod();

		Interval LocalValidity(TimeValue t);
		ChannelMask ChannelsUsed()  { return EDITPAT_CHANNELS; }
		ChannelMask ChannelsChanged() 	{ return EDITPAT_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 Class_ID(PATCHOBJ_CLASS_ID,0); }
		
		int CompMatrix(TimeValue t, ModContext& mc, Matrix3& tm, Interval& valid);
		
		// From Animatable
		void DeleteThis() { delete this; }
		void GetClassName(TSTR& s) { s= TSTR(_T("EditPatchMod")); }
		Class_ID ClassID() { return Class_ID(EDITPATCH_CLASS_ID,0);}

		// 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);

 		BOOL DependOnTopology(ModContext &mc);

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

		// Specialized xform for bezier handles
		void XFormHandles( XFormProc *xproc, TimeValue t, Matrix3& partm, Matrix3& tmAxis, int object, int handleIndex );

		// 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 );

		// The following is called before the first Move(), Rotate() or Scale() call
		void TransformStart(TimeValue t);

		// The following is called after the user has completed the Move, Rotate or Scale operation and
		// the undo object has been accepted.
		void TransformFinish(TimeValue t);		

		// The following is called when the transform operation is cancelled by a right-click and the undo
		// has been cancelled.
		void TransformCancel(TimeValue t);		

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

		void DoVertDelete();
		void DoPatchDelete();
		void DoPatchAdd(int type);
		void DoEdgeSubdivide();
		void DoPatchSubdivide();
		void DoVertWeld();
		void DoPatchDetach(int copy, int reorient);

		void ClearPatchDataFlag(ModContextList& mcList,DWORD f);
		void DeletePatchDataTempData();		
		void CreatePatchDataTempData();

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

		void ChangeRememberedPatch(int type);
		void ChangeSelPatches(int type);
		int RememberPatchThere(HWND hWnd, IPoint2 m);
		void SetRememberedPatchType(int type);
		void ChangeRememberedVert(int type);
		void ChangeSelVerts(int type);
		int RememberVertThere(HWND hWnd, IPoint2 m);
		void SetRememberedVertType(int type);

		// 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_TH_EDITPATCH); }
		void ActivateSubobjSel(int level, XFormModes& modes );
		int NeedUseSubselButton() { return 0; }
		void SelectSubComponent( HitRecord *hitRec, BOOL selected, BOOL all, BOOL invert );
		void ClearSelection(int selLevel);
		void SelectAll(int selLevel);
		void InvertSelection(int selLevel);
		
		void SetDisplaySurface(BOOL sw);
		void SetDisplayLattice(BOOL sw);
		void SetEPropagate(BOOL sw);		
		void SetPPropagate(BOOL sw);		
		void SetMeshSteps(int count);
// Future use (Not used now)
//		void SetMeshAdaptive(BOOL sw);
		void SetViewTess(TessApprox &tess);
		void SetProdTess(TessApprox &tess);
		void SetTessUI(HWND hDlg, TessApprox *tess);

		// 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);

		void PatchSelChanged();

		int DoAttach(INode *node, PatchMesh *attPatch);

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

		// Store current topology in the PatchObject
		void RecordTopologyTags();

		// Re-match named selection sets, etc. with changed topology (Call RecordTopologyTags
		// before making the changes to the shape, then call this)
		void ResolveTopoChanges();

		void RescaleWorldUnits(float f);
	};

class PickPatchAttach : 
		public PickModeCallback,
		public PickNodeCallback {
	public:		
		EditPatchMod *ep;
		
		PickPatchAttach() {ep=NULL;}

		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);

		HCURSOR GetHitCursor(IObjParam *ip);

		BOOL Filter(INode *node);
		
		PickNodeCallback *GetFilter() {return this;}

		BOOL RightClick(IObjParam *ip,ViewExp *vpt)	{return TRUE;}
	};

// Table to convert selLevel values to patch selLevel flags.
const int patchLevel[] = {PATCH_OBJECT,PATCH_VERTEX,PATCH_EDGE,PATCH_PATCH};

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

// For hit testing...
static int patchHitLevel[] = {0,SUBHIT_PATCH_VERTS | SUBHIT_PATCH_VECS,SUBHIT_PATCH_EDGES,SUBHIT_PATCH_PATCHES};

const int patchDlg[] = {IDD_EPPARAM_OBJECT,IDD_EPPARAM_VERTEX,IDD_EPPARAM_EDGE,IDD_EPPARAM_PATCH};
const int patchDlgString[] = {IDS_TH_EDITOBJECT,IDS_TH_EDITVERTEX,IDS_TH_EDITEDGE,IDS_TH_EDITPATCH};
const DLGPROC patchDlgProc[] = {PatchObjectParamDlgProc,PatchVertexParamDlgProc,PatchEdgeParamDlgProc,PatchPatchParamDlgProc};


class EditPatchClassDesc:public ClassDesc {
	public:
	int 			IsPublic() { return 1; }
	void *			Create(BOOL loading = FALSE ) { return new EditPatchMod; }
	const TCHAR *	ClassName() { return GetString(IDS_TH_EDITPATCH_CLASS); }
	SClass_ID		SuperClassID() { return OSM_CLASS_ID; }
	Class_ID		ClassID() { return Class_ID(EDITPATCH_CLASS_ID,0); }
	const TCHAR* 	Category() { return GetString(IDS_RB_DEFEDIT);}
	void			ResetClassParams(BOOL fileReset);
	};

typedef Tab<Point3> Point3Tab;

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); }
	};

typedef Tab<int> IntTab;

// General-purpose patch point table -- Maintains point table for each of n polygons
class PatchPointTab {
	public:
		Point3Tab ptab;	// Patch mesh points
		Point3Tab vtab;	// Patch mesh vectors
		IntTab pttab;	// Patch point types
		PatchPointTab();
		~PatchPointTab();
		void Empty();
		void Zero();
		void MakeCompatible(PatchMesh& patch, BOOL clear=TRUE);
		PatchPointTab& operator=(PatchPointTab& from);
		BOOL IsCompatible(PatchMesh &patch);
		void RescaleWorldUnits(float f);
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class PatchVertexDelta {
	public:
		PatchPointTab dtab;

		void SetSize(PatchMesh &patch, BOOL load=TRUE);
		void Empty() { dtab.Empty(); }
		void Zero() { dtab.Zero(); }
		void SetVert(int i, const Point3& p) { dtab.ptab[i] = p; }
		void SetVertType(int i, int k) { dtab.pttab[i] = k; }
		void SetVec(int i, const Point3& p) { dtab.vtab[i] = p; }
		void MoveVert(int i, const Point3& p) { dtab.ptab[i] += p; }
		void MoveVec(int i, const Point3& p) { dtab.vtab[i] += p; }
		void Apply(PatchMesh& patch);
		void UnApply(PatchMesh& patch);
		PatchVertexDelta& operator=(PatchVertexDelta& from) { dtab = from.dtab; return *this; }
		void ApplyHandlesAndZero(PatchMesh &patch, int handleVert);
		BOOL IsCompatible(PatchMesh &patch) { return dtab.IsCompatible(patch); }
		void RescaleWorldUnits(float f) { dtab.RescaleWorldUnits(f); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class AdjEdgeList;
class EPTempData;

/*-------------------------------------------------------------------*/

// Class for recording changes -- This is used to reconstruct an object from the original whenever
// the modifier is re-entered or whenever the system needs to reconstruct an object's cache.  This may be
// slow if a lot of changes have been recorded, but it's about the only way to properly reconstruct an object
// because the output of one operation becomes the input of the next.

// These are used as follows:
// When a user makes a modification to an object, a StartChangeGroup call needs to be made to the EditPatchData
// object.  Then a change record needs to be added for each sub-operation that makes up the modification.  These
// records are owned by the EditPatchData object, but they should also be referenced by the undo object for that
// operation.  If an undo is done, ownership of the modification record transfers to the undo/redo object and the
// record is REMOVED (NOT DELETED) from the EditPatchData object.  This keeps the record around for a redo operation
// but removes it from the list of records for the modifier.  If the undo is redone, ownership transfers back to
// the modifier, when it is re-added to the modification record list.

// Note that this class contains load and save methods, necessary because the modifier needs to be able to save
// and load them.  When you subclass off of this, be sure your load and save methods call the base class's first!

class PatchRestore;

class PModRecord {
	public:
		PModRecord() { groupNumber = -1; serialNumber = -1; restoreObj = NULL; }
		virtual ~PModRecord();
		int groupNumber;
		int serialNumber;
		PatchRestore *restoreObj;
		void ClearRestore() { restoreObj = NULL; }
		void SetRestore(PatchRestore *r) { restoreObj = r; }
		virtual BOOL Undo(PatchMesh *patch)=0;
		virtual BOOL Redo(PatchMesh *patch,int reRecord)=0;
		virtual DWORD Parts()=0;				// Flags of parts modified by this
		virtual int ChunkID()=0;
		virtual PModRecord* Clone()=0;
		virtual IOResult Save(ISave *isave)=0;
		virtual IOResult Load(ILoad *iload)=0;
		virtual void RescaleWorldUnits(float f) {}
	};

typedef PModRecord* PPModRecord;
typedef Tab<PPModRecord> ModRecordTab;

/*-------------------------------------------------------------------*/

// Here are the types of modification records we use!

#define CLEARVERTSELRECORD_CHUNK	0x2000
#define SETVERTSELRECORD_CHUNK		0x2001
#define INVERTVERTSELRECORD_CHUNK	0x2002
#define CLEAREDGESELRECORD_CHUNK	0x2005
#define SETEDGESELRECORD_CHUNK		0x2006
#define INVERTEDGESELRECORD_CHUNK	0x2007
#define CLEARPATCHSELRECORD_CHUNK	0x2010
#define SETPATCHSELRECORD_CHUNK		0x2011
#define INVERTPATCHSELRECORD_CHUNK	0x2012
#define VERTSELRECORD_CHUNK			0x2020
#define EDGESELRECORD_CHUNK			0x2025
#define PATCHSELRECORD_CHUNK		0x2030
#define VERTMOVERECORD_CHUNK		0x2040
#define PATCHDELETERECORD_CHUNK		0x2050
#define VERTDELETERECORD_CHUNK		0x2060
#define PATCHCHANGERECORD_CHUNK		0x2070
#define VERTCHANGERECORD_CHUNK		0x2080
#define PATCHADDRECORD_CHUNK		0x2090
#define EDGESUBDIVIDERECORD_CHUNK	0x20A0
#define PATCHSUBDIVIDERECORD_CHUNK	0x20B0
#define VERTWELDRECORD_CHUNK		0x20C0
#define PATTACHRECORD_CHUNK			0x20D0
#define PATCHDETACHRECORD_CHUNK		0x20E0
#define PATCHMTLRECORD_CHUNK		0x20F0
										 
class ClearPVertSelRecord : public PModRecord {
	public:
		BitArray sel;	// Old state
		ClearPVertSelRecord() : PModRecord() { }
		ClearPVertSelRecord(BitArray& oldsel) : PModRecord() { sel = oldsel; }
		ClearPVertSelRecord(ClearPVertSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			 sel = from->sel; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return CLEARVERTSELRECORD_CHUNK; }
		PModRecord *Clone() { return new ClearPVertSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class SetPVertSelRecord : public PModRecord {
	public:
		BitArray sel;	// Old state
		SetPVertSelRecord() : PModRecord() { }
		SetPVertSelRecord(BitArray& oldsel) : PModRecord() { sel = oldsel; }
		SetPVertSelRecord(SetPVertSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			 sel = from->sel; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return SETVERTSELRECORD_CHUNK; }
		PModRecord *Clone() { return new SetPVertSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class InvertPVertSelRecord : public PModRecord {
	public:
		InvertPVertSelRecord() : PModRecord() { }
		InvertPVertSelRecord(InvertPVertSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			 }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return INVERTVERTSELRECORD_CHUNK; }
		PModRecord *Clone() { return new InvertPVertSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class ClearPEdgeSelRecord : public PModRecord {
	public:
		BitArray sel;	// Old state
		ClearPEdgeSelRecord() : PModRecord() { }
		ClearPEdgeSelRecord(BitArray& oldsel) : PModRecord() { sel = oldsel; }
		ClearPEdgeSelRecord(ClearPEdgeSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			 sel = from->sel; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return CLEAREDGESELRECORD_CHUNK; }
		PModRecord *Clone() { return new ClearPEdgeSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class SetPEdgeSelRecord : public PModRecord {
	public:
		BitArray sel;	// Old state
		SetPEdgeSelRecord() : PModRecord() { }
		SetPEdgeSelRecord(BitArray& oldsel) : PModRecord() { sel = oldsel; }
		SetPEdgeSelRecord(SetPEdgeSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			 sel = from->sel; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return SETEDGESELRECORD_CHUNK; }
		PModRecord *Clone() { return new SetPEdgeSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class InvertPEdgeSelRecord : public PModRecord {
	public:
		InvertPEdgeSelRecord() : PModRecord() { }
		InvertPEdgeSelRecord(InvertPEdgeSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			 }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return INVERTEDGESELRECORD_CHUNK; }
		PModRecord *Clone() { return new InvertPEdgeSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class ClearPatchSelRecord : public PModRecord {
	public:
		BitArray sel;	// Old state
		ClearPatchSelRecord() : PModRecord() { }
		ClearPatchSelRecord(BitArray& oldsel) : PModRecord() { sel = oldsel; }
		ClearPatchSelRecord(ClearPatchSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			 sel = from->sel; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return CLEARPATCHSELRECORD_CHUNK; }
		PModRecord *Clone() { return new ClearPatchSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class SetPatchSelRecord : public PModRecord {
	public:
		BitArray sel;	// Old state
		SetPatchSelRecord() : PModRecord() { }
		SetPatchSelRecord(BitArray& oldsel) : PModRecord() { sel = oldsel; }
		SetPatchSelRecord(SetPatchSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			 sel = from->sel; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return SETPATCHSELRECORD_CHUNK; }
		PModRecord *Clone() { return new SetPatchSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class InvertPatchSelRecord : public PModRecord {
	public:
		InvertPatchSelRecord() : PModRecord() { }
		InvertPatchSelRecord(InvertPatchSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			 }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return INVERTPATCHSELRECORD_CHUNK; }
		PModRecord *Clone() { return new InvertPatchSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class PVertSelRecord : public PModRecord {
	public:
		BitArray oldSel;	// Old state
		BitArray newSel;	// New state
		PVertSelRecord() : PModRecord() {}
		PVertSelRecord(PVertSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			 oldSel = from->oldSel; newSel = from->newSel; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return VERTSELRECORD_CHUNK; }
		PModRecord *Clone() { return new PVertSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class PEdgeSelRecord : public PModRecord {
	public:
		BitArray oldSel;	// Old state
		BitArray newSel;	// New state
		PEdgeSelRecord() : PModRecord() {}
		PEdgeSelRecord(PEdgeSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			 oldSel = from->oldSel; newSel = from->newSel; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return EDGESELRECORD_CHUNK; }
		PModRecord *Clone() { return new PEdgeSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class PatchSelRecord : public PModRecord {
	public:
		BitArray oldSel;	// Old state
		BitArray newSel;	// New state
		PatchSelRecord() : PModRecord() {}
		PatchSelRecord(PatchSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			 oldSel = from->oldSel; newSel = from->newSel; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return PATCHSELRECORD_CHUNK; }
		PModRecord *Clone() { return new PatchSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class PVertMoveRecord : public PModRecord {
	public:
		PatchVertexDelta delta;	// Position changes for each vertex (Wasteful!  Change later?)
		PVertMoveRecord() : PModRecord() {}
		PVertMoveRecord(PVertMoveRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			 delta = from->delta; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_GEOM; }
		int ChunkID() { return VERTMOVERECORD_CHUNK; }
		PModRecord *Clone() { return new PVertMoveRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { delta.RescaleWorldUnits(f); }
	};

class PatchDeleteRecord : public PModRecord {
	public:
		PatchMesh oldPatch;		// How the spline looked before the delete
		PatchDeleteRecord() : PModRecord() {}
		PatchDeleteRecord(PatchMesh *patch);
		PatchDeleteRecord(PatchDeleteRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			 oldPatch = from->oldPatch; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_TEXMAP | PART_SELECT; }
		int ChunkID() { return PATCHDELETERECORD_CHUNK; }
		PModRecord *Clone() { return new PatchDeleteRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldPatch.Transform(stm);
			}
	};

class PVertDeleteRecord : public PModRecord {
	public:
		PatchMesh oldPatch;		// How the patch looked before the delete
		PVertDeleteRecord() : PModRecord() {}
		PVertDeleteRecord(PatchMesh *patch);
		PVertDeleteRecord(PVertDeleteRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			 oldPatch = from->oldPatch; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_TEXMAP | PART_SELECT; }
		int ChunkID() { return VERTDELETERECORD_CHUNK; }
		PModRecord *Clone() { return new PVertDeleteRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldPatch.Transform(stm);
			}
	};

class PatchChangeRecord : public PModRecord {
	public:
		PatchMesh oldPatch;		// How the patch mesh looked before the change
		int index;
		int type;
		PatchChangeRecord() : PModRecord() {}
		PatchChangeRecord(PatchMesh *patch, int index, int type);
		PatchChangeRecord(PatchChangeRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			oldPatch = from->oldPatch; index = from->index; type = from->type; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return PATCHCHANGERECORD_CHUNK; }
		PModRecord *Clone() { return new PatchChangeRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldPatch.Transform(stm);
			}
	};

class PVertChangeRecord : public PModRecord {
	public:
		PatchMesh oldPatch;		// How the patch mesh looked before the change
		int index;
		int type;
		PVertChangeRecord() : PModRecord() {}
		PVertChangeRecord(PatchMesh *patch, int index, int type);
		PVertChangeRecord(PVertChangeRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			oldPatch = from->oldPatch; index = from->index; type = from->type; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return VERTCHANGERECORD_CHUNK; }
		PModRecord *Clone() { return new PVertChangeRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldPatch.Transform(stm);
			}
	};

class PatchAddRecord : public PModRecord {
	public:
		BOOL postWeld;			// Present in MAX 2.0 and up
		int type;				// 3 or 4 sides!
		PatchMesh oldPatch;		// How the patch looked before the addition
		PatchAddRecord() : PModRecord() { postWeld = TRUE; }
		PatchAddRecord(int type, PatchMesh *patch);
		PatchAddRecord(PatchAddRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			type = from->type; oldPatch = from->oldPatch; postWeld = TRUE; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_TEXMAP | PART_SELECT; }
		int ChunkID() { return PATCHADDRECORD_CHUNK; }
		PModRecord *Clone() { return new PatchAddRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldPatch.Transform(stm);
			}
	};

class EdgeSubdivideRecord : public PModRecord {
	public:
		BOOL propagate;			// Carry around entire patch mesh?
		PatchMesh oldPatch;		// How the patch looked before the addition
		EdgeSubdivideRecord() : PModRecord() { }
		EdgeSubdivideRecord(BOOL propagate, PatchMesh *patch);
		EdgeSubdivideRecord(EdgeSubdivideRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			propagate = from->propagate; oldPatch = from->oldPatch; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_TEXMAP | PART_SELECT; }
		int ChunkID() { return EDGESUBDIVIDERECORD_CHUNK; }
		PModRecord *Clone() { return new EdgeSubdivideRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldPatch.Transform(stm);
			}
	};

class PatchSubdivideRecord : public PModRecord {
	public:
		BOOL propagate;			// Carry around entire patch mesh?
		PatchMesh oldPatch;		// How the patch looked before the addition
		PatchSubdivideRecord() : PModRecord() { }
		PatchSubdivideRecord(BOOL propagate, PatchMesh *patch);
		PatchSubdivideRecord(PatchSubdivideRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			propagate = from->propagate; oldPatch = from->oldPatch; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_TEXMAP | PART_SELECT; }
		int ChunkID() { return PATCHSUBDIVIDERECORD_CHUNK; }
		PModRecord *Clone() { return new PatchSubdivideRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldPatch.Transform(stm);
			}
	};

class PVertWeldRecord : public PModRecord {
	public:
		float thresh;			// Weld threshold
		BOOL propagate;			// Carry around entire patch mesh?
		PatchMesh oldPatch;		// How the patch looked before the addition
		PVertWeldRecord() : PModRecord() { }
		PVertWeldRecord(float thresh, PatchMesh *patch);
		PVertWeldRecord(PVertWeldRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			thresh = from->thresh; propagate = from->propagate; oldPatch = from->oldPatch; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_TEXMAP | PART_SELECT; }
		int ChunkID() { return VERTWELDRECORD_CHUNK; }
		PModRecord *Clone() { return new PVertWeldRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldPatch.Transform(stm);
			}
	};

class PAttachRecord : public PModRecord {
	public:
		PatchMesh attPatch;			// The patch we're attaching
		int oldPatchCount;		// The number of splines present before attaching
		int mtlOffset;
		PAttachRecord() : PModRecord() {}
		PAttachRecord(PatchMesh *patch, PatchMesh *attPatch, int mtlOffset);
		PAttachRecord(PAttachRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			attPatch = from->attPatch; oldPatchCount = from->oldPatchCount; mtlOffset = 0; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_TEXMAP | PART_SELECT; }
		int ChunkID() { return PATTACHRECORD_CHUNK; }
		PModRecord* Clone() { return new PAttachRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			attPatch.Transform(stm);
			}
	};

class PatchDetachRecord : public PModRecord {
	public:
		int copy;
		PatchMesh oldPatch;
		PatchDetachRecord() : PModRecord() {}
		PatchDetachRecord(PatchMesh *patch, int copy);
		PatchDetachRecord(PatchDetachRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			copy = from->copy; oldPatch = from->oldPatch; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_TEXMAP | PART_SELECT; }
		int ChunkID() { return PATCHDETACHRECORD_CHUNK; }
		PModRecord* Clone() { return new PatchDetachRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldPatch.Transform(stm);
			}
	};

typedef Tab<MtlID> MtlIDTab;

class PatchMtlRecord : public PModRecord {
	public:
		MtlIDTab materials;		// Materials from selected patches
		MtlID index;				// New material index assigned
		PatchMtlRecord() : PModRecord() {}
		PatchMtlRecord(PatchMesh *patch, MtlID index);
		PatchMtlRecord(PatchMtlRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			materials = from->materials; index = from->index; }
		BOOL Undo(PatchMesh *patch);
		BOOL Redo(PatchMesh *patch,int reRecord);
		DWORD Parts() { return PART_TOPO; }
		int ChunkID() { return PATCHMTLRECORD_CHUNK; }
		PModRecord *Clone() { return new PatchMtlRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

/*-------------------------------------------------------------------*/


// EditPatchData flags
#define EPD_BEENDONE			(1<<0)
#define EPD_UPDATING_CACHE		(1<<1)
#define EPD_HASDATA				(1<<2)
#define EMD_HELD				(1<<3) // equivalent to A_HELD

// This is the data that each mod app will have.
class EditPatchData : public LocalModData {
	public:
		int changeGroup;	// Current change group
		int changeSerial;	// Current change group's serial number
		BOOL handleFlag;
		int handleVert;

		// Stuff we need to have for the patch's mesh conversion -- These are
		// Here because they're kind of a global change -- not undoable.
		int meshSteps;
		BOOL meshAdaptive;	// Future use (Not used now)
		TessApprox viewTess;
		TessApprox prodTess;
		BOOL displaySurface;
		BOOL displayLattice;

		DWORD flags;

		// This records the changes to the incoming object.
		ModRecordTab changes;

		// A pointer to the change record's vertex delta object
		PatchVertexDelta *vdelta;

		// RB: Named selection set lists
		NamedSelSetList vselSet;  // Vertex
		NamedSelSetList eselSet;  // Edge
		NamedSelSetList pselSet;  // Patch

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

		EditPatchData(EditPatchMod *mod);
		EditPatchData(EditPatchData& emc);
		~EditPatchData() { ResetChanges(); }
		
		// Applies modifications to a patchObject
		void Apply(TimeValue t,PatchObject *patchOb,int selLevel);

		// 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 EditPatchData(*this); }
		
		void SetFlag(DWORD f,BOOL on) 
			{ 
			if ( on ) {
				flags|=f;
			} else {
				flags&=~f; 
				}
			}
		DWORD GetFlag(DWORD f) { return flags&f; }

		EPTempData *TempData(EditPatchMod *mod);

		// Change recording functions:
		int StartChangeGroup();					// Start a new group of changes
		int AddChangeRecord(PModRecord *ptr);	// Add a change record to the end of the list & serialize
		int AdoptChangeRecord(PModRecord *ptr);	// Add a change record to the end of the list (don't serialize)
		int RemoveChangeRecord();				// Remove the last change record from the list (don't delete)
		int RemoveChangeGroup();				// Remove the last change group from the list (don't delete)
		int DeleteChangeRecord();				// Delete last change record from the list
		int DeleteChangeGroup();				// Delete last change group from the list
		int ResetChanges();						// Delete all changes
		int FindChangeGroup(int group);			// Locate first entry for this group
		void ClearHandleFlag() { handleFlag = FALSE; }
		void SetHandleFlag(int vert) { handleVert = vert; handleFlag = TRUE; }
		BOOL DoingHandles() { return handleFlag; }
		void ApplyHandlesAndZero(PatchMesh &patch) { vdelta->ApplyHandlesAndZero(patch, handleVert); }
		void RescaleWorldUnits(float f);

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

// My generic restore class

class PatchRestore : public RestoreObj {
	public:
		PModRecord *rec;		// Modification record
		BOOL iOwn;			// Does restore object own it?

		TimeValue t;

		EditPatchData *patchData;
		EditPatchMod	 *mod;

		PatchRestore(EditPatchData* md, EditPatchMod* mod, PModRecord *rec);
		~PatchRestore();

		void Restore(int isUndo);
		void Redo();
		int Size() { return 1; }
		void EndHold() {mod->ClearAFlag(A_HELD);}
		void RemoveRecord() { rec = NULL; }	
		TSTR Description() { return TSTR(_T("Generic patch restore")); }
	};

class MEdge {
	public:
		DWORD f[2];
		DWORD v[2];
	};

class AdjEdgeList {
	public:
		DWORDTab *list;
		Tab<MEdge> edges;
		int nverts;

		AdjEdgeList( PatchMesh& apatch );
		~AdjEdgeList();
		AdjEdgeList& operator=(AdjEdgeList& a) {assert(0);return *this;}
		void AddEdge( DWORD fi, DWORD v1, DWORD v2 );
		DWORDTab& operator[](int i) { return list[i]; }
	};

/*-------------------------------------------------------------------*/

class EPTempData {
	private:
		// Data to keep cached while
		AdjEdgeList 	*adjList;
		DWORDTab		*vertCluster;
		Point3Tab		*cnormals;
		Point3Tab       *ccenters;
		Interval alValid;

		PatchMesh		*patch;
		Point3Tab 		*vnormals;
		Interval 		patchValid;
		
		EditPatchMod 	*mod;
		EditPatchData 	*patchData;

	public:		
		
		~EPTempData();
		EPTempData(EditPatchMod *m,EditPatchData *md);
		void Invalidate(PartID part,BOOL meshValid=TRUE);
		
		AdjEdgeList 	*AdjList(TimeValue t);
		PatchMesh		*GetPatch(TimeValue t);
		
		BOOL PatchCached(TimeValue t);
		void UpdateCache(PatchObject *patchOb);
		EditPatchMod	*GetMod() { return mod; }
	};


// Spline hit override functions

extern void SetPatchHitOverride(int value);
extern void ClearPatchHitOverride();

#endif // __EDITPATCH_H__
