/////////////////////////////////////////////////////////////
// CINEMA 4D SDK                                           //
/////////////////////////////////////////////////////////////
// (c) MAXON Computer GmbH, all rights reserved            //
/////////////////////////////////////////////////////////////

#ifndef _LIB_SCULPT_H_
#define _LIB_SCULPT_H_

#include <stdarg.h>

#include "c4d_library.h"
#include "c4d_baseobject.h"
#include "c4d_basetag.h"

#define LIBRARY_SCULPT	1027453 
#define SCULPT_LAYER_ID 1026657
#define SCULPT_FOLDER_ID 1025836
#define SCULPT_OBJECT_ID 1024033
#define SCULPT_LAYERDATA_ID 1024016

class SculptLayerData : public BaseList2D
{
private:
	SculptLayerData();
	~SculptLayerData();

public:
	LONG GetSubdivisionLevel();
	LONG GetPointCount();
	Bool GetOffset(LONG index, Vector &offset);					
	void SetOffset(LONG index, const Vector &fullOffset);	
	void AddOffset(LONG index, const Vector &offset);	
	Bool GetMask(LONG index, SReal &mask);							
	void SetMask(LONG index, SReal mask);									
	void AddToMask(LONG index, SReal mask);				
	Bool HasMask();
};

class SculptLayerBase : public BaseList2D
{
private:
	SculptLayerBase();
	~SculptLayerBase();

public:
	Bool Select();				//Select the layer. Will only work if the subdivision level is at the same level as this layer.
	Bool IsVisible();
	void SetVisible(Bool value);
	Bool IsLocked();
	void SetLocked(Bool value);
	Real GetStrength();
	void SetStrength(Real value);
};

class SculptLayer : public SculptLayerBase
{
private:
	SculptLayer();
	~SculptLayer();

public:
	SculptLayerData *GetSculptLayerData();					//Get the first layer data for this layer, the BaseLayer will have more than one, other layers only have 1.

	//These methods will get/set data on the currently selected level if data exists for the layer at the current subdivision level
	LONG GetPointCount();									//Get the point count
	Bool GetOffset(LONG index, Vector &offset);				//Get the offset value for this point on the layer.
	void SetOffset(LONG index, const Vector &fullOffset);	//Set the full offset value for this point on the layer
	void AddOffset(LONG index, const Vector &offset);		//Add an offset amount to the point on this layer

	Bool GetMask(LONG index, SReal &mask);					//Set the mask value for the point (between 0 and 1)
	void SetMask(LONG index, SReal mask);					//Set the mask value for the point (between 0 and 1)
	void AddToMask(LONG index, SReal mask);					//Add to the mask value for the point (the final value will be clipped to be within 0 and 1)

	Bool IsBaseLayer();										//True if it is the base object layer, which contains multiple SculptLayerData children
	Bool HasMask();											//Does this layer have a mask?
	Bool IsMaskEnabled();
	void SetMaskEnabled(Bool value);
};

class SculptFolder : public SculptLayerBase
{
private:
	SculptFolder();
	~SculptFolder();
};

struct SculptHitData
{
	Vector localHitPoint;			//Location of the hitpoint on the surface of the object in the objects local coordinate system
	SVector localHitNormal;			//The Normal of the hitpoint on the surface of the object in the objects local coordinate system
	LONG selectedPolygon;			//The polygon that was hit
	Real distance;					//The distance from rayp
};

class SculptObject : public BaseList2D
{
private:
	SculptObject();
	~SculptObject();

public:
	LONG GetSubdivisionCount();
	PolygonObject *GetPolygonCopy(LONG subdivisionLevel, Bool includeTopLevels=TRUE);
	PolygonObject *GetOriginalObject();
	
	//To use the hit functions the mesh must be unfrozen and both NeedCollisionUpdate(TRUE) and UpdateCollision() should be called to initialize the collision data.
	//You only need to do this once to use the hit method, but if you change any points then you need to call NeedCollisionUpdate(FALSE) and UpdateCollision() afterwards.
	Bool Hit(BaseDraw* bd, Real mx, Real my, SculptHitData  &hitData);				//mx and my are mouse coordinates
	Bool Hit(const Vector &rayp, const Vector &rayv, SculptHitData  &hitData);		//rayp = Eye Point, rayv = Ray Direction, both are in object space
 
	LONG	GetPolygonCount();
	SculptLayerBase	*GetCurrentLayer();
	LONG	GetCurrentLevel();
	ULONG	GetMemoryUsage();

	SculptLayer *AddLayer();		//Adds a layer at the current subdivision level
	SculptFolder *AddFolder();		//Add a folder
	Bool DeleteSelectedLayer();		//Delete the currently selected layer
	void Update();					//Re-composite and update the sculpt object

	Bool	Subdivide();
	Bool	IncreaseSubdivisionLevel(); 
	Bool	DecreaseSubdivisionLevel();

	SculptLayerBase *GetFirstLayer();
	SculptLayer			*GetBaseLayer();

	Bool	IsFrozen(); 
	void	SetFrozen(Bool value);
	Bool	GetAllowDeformations(); 
	void	SetAllowDeformations(Bool value);
	void	UpdateCollision();								//Call to update the collision data.
	void	NeedCollisionUpdate(Bool fullUpdate=FALSE);		//Flag the collision so that it gets updated next time a tool requires it. fullUpdate = This will rebuild the entire collision mesh, not always required.
};

class SculptTag : public BaseTag
{
private:
	SculptTag();
	~SculptTag();
public:
	static SculptTag *Alloc() { return (SculptTag*)BaseTag::Alloc(Tsculpt); }
	static void Free(SculptTag *&pTag) { BaseTag *tag=pTag; BaseTag::Free(tag); pTag=NULL; }

	SculptObject *GetSculptObject();	
};

struct SculptLibrary : public C4DLibrary
{
	//SculptLayerData
	LONG						(*sculptlayerdataGetSubdivisionLevel)(SculptLayerData *layerData);
	LONG						(*sculptlayerdataGetPointCount)(SculptLayerData *layerData);
	Bool						(*sculptlayerdataGetOffset)(SculptLayerData *layerData, LONG index, Vector &offset);									
	void						(*sculptlayerdataSetOffset)(SculptLayerData *layerData, LONG index, const Vector &fullOffset);	
	void						(*sculptlayerdataAddOffset)(SculptLayerData *layerData, LONG index, const Vector &offset);		
	Bool						(*sculptlayerdataGetMask)(SculptLayerData *layerData, LONG index, SReal &mask);									
	void						(*sculptlayerdataSetMask)(SculptLayerData *layerData, LONG index, SReal mask);									
	void						(*sculptlayerdataAddToMask)(SculptLayerData *layerData, LONG index, SReal mask);
	Bool						(*sculptlayerdataSelect)(SculptLayerData *layerData);
	Bool						(*sculptlayerdataHasMask)(SculptLayerData *layerData);

	//SculptLayerBase
	Bool						(*sculptlayerbaseSelect)(SculptLayerBase *layer);
	Bool						(*sculptlayerbaseIsVisible)(SculptLayerBase *layer);
	void						(*sculptlayerbaseSetVisible)(SculptLayerBase *layer, Bool value);
	Bool						(*sculptlayerbaseIsLocked)(SculptLayerBase *layer);
	void						(*sculptlayerbaseSetLocked)(SculptLayerBase *layer, Bool value);
	Real						(*sculptlayerbaseGetStrength)(SculptLayerBase *layer);
	void						(*sculptlayerbaseSetStrength)(SculptLayerBase *layer, Real value);

	//SculptLayer
	SculptLayerData*(*sculptlayerGetSculptLayerData)(SculptLayer *layer);
	LONG						(*sculptlayerGetPointCount)(SculptLayer *layer);
	Bool						(*sculptlayerGetOffset)(SculptLayer *layer, LONG index, Vector &offset);									
	void						(*sculptlayerSetOffset)(SculptLayer *layer, LONG index, const Vector &fullOffset);	
	void						(*sculptlayerAddOffset)(SculptLayer *layer, LONG index, const Vector &offset);		
	Bool						(*sculptlayerGetMask)(SculptLayer *layer, LONG index, SReal &mask);									
	void						(*sculptlayerSetMask)(SculptLayer *layer, LONG index, SReal mask);									
	void						(*sculptlayerAddToMask)(SculptLayer *layer, LONG index, SReal mask);
	Bool						(*sculptlayerIsBaseLayer)(SculptLayer *layer);
	Bool						(*sculptlayerHasMask)(SculptLayer *layer);
	Bool						(*sculptlayerIsMaskEnabled)(SculptLayer *layer);
	void						(*sculptlayerSetMaskEnabled)(SculptLayer *layer, Bool value);	

	//SculptObject
	LONG							(*sculptobjGetSubdivisionCount)(SculptObject *obj);
	PolygonObject*		(*sculptobjGetPolygonCopy)(SculptObject *obj,LONG subdivisionLevel,Bool includeTopLevels);
	PolygonObject*		(*sculptobjGetOriginalObject)(SculptObject *obj);
	LONG							(*sculptobjGetPolygonCount)(SculptObject *obj);
	SculptLayerBase*	(*sculptobjGetCurrentLayer)(SculptObject *obj);
	LONG							(*sculptobjGetCurrentLevel)(SculptObject *obj);
	ULONG							(*sculptobjGetMemoryUsage)(SculptObject *obj);
	Bool							(*sculptobjHitScreen)(SculptObject *obj,BaseDraw* bd, Real mx, Real my, SculptHitData &hitData);
	Bool							(*sculptobjHitObject)(SculptObject *obj,const Vector &rayp, const Vector &rayv, SculptHitData &hitData);
	SculptLayer*			(*sculptobjAddLayer)(SculptObject *obj);
	SculptFolder*			(*sculptobjAddFolder)(SculptObject *obj);
	Bool							(*sculptobjDeleteSelectedLayer)(SculptObject *obj);
	void							(*sculptobjUpdate)(SculptObject *obj);
	Bool							(*sculptobjSubdivide)(SculptObject *obj);
	Bool							(*sculptobjIncreaseSubdivisionLevel)(SculptObject *obj);
	Bool							(*sculptobjDecreaseSubdivisionLevel)(SculptObject *obj);
	SculptLayerBase*  (*sculptobjGetFirstLayer)(SculptObject *obj);
	SculptLayer*			(*sculptobjGetBaseLayer)(SculptObject *obj);
	Bool							(*sculptobjIsFrozen)(SculptObject *obj);
	void							(*sculptobjSetFrozen)(SculptObject *obj, Bool value);
	void							(*sculptobjUpdateCollision)(SculptObject *obj);
	void							(*sculptobjNeedCollisionUpdate)(SculptObject *obj, Bool fullUpdate);
	Bool							(*sculptobjGetAllowDeformations)(SculptObject *obj);
	void							(*sculptobjSetAllowDeformations)(SculptObject *obj, Bool value);

	//SculptTag
	SculptObject*			(*sculpttagGetSculptObject)(SculptTag *tag);
};


SculptObject *GetSelectedSculptObject(BaseDocument *doc);
SculptObject *MakeSculptObject(PolygonObject *poly, BaseDocument *doc);

#endif
