/////////////////////////////////////////////////////////////
// CINEMA SDK : OBJECT PLUGINS														 //
/////////////////////////////////////////////////////////////
// VERSION    : CINEMA 4D																	 //
/////////////////////////////////////////////////////////////
// (c) 1989-2002 MAXON Computer GmbH, all rights reserved	 //
/////////////////////////////////////////////////////////////

// spline example 

#include "c4d.h"
#include "c4d_symbols.h"
#include "Odoublecircle.h"

class DoubleCircleData : public ObjectData
{
	INSTANCEOF(DoubleCircleData,ObjectData)

	public:
		virtual Bool Init(GeListNode *node);

		virtual Bool Message				(GeListNode *node, LONG type, void *data);
		virtual Bool Draw						(PluginObject *op, LONG type, BaseDraw *bd, BaseDrawHelp *bh);
		virtual LONG DetectHandle		(PluginObject *op, BaseDraw *bd, LONG x, LONG y, LONG qualifier);
		virtual Bool MoveHandle			(PluginObject *op, PluginObject *undo, const Matrix &tm, LONG hit_id, LONG qualifier);
		virtual SplineObject* GetContour(PluginObject *op, BaseDocument *doc, Real lod, BaseThread *bt);
		virtual Bool GetDEnabling(GeListNode *node, const DescID &id,GeData &t_data,LONG flags,const BaseContainer *itemdesc);

		static NodeData *Alloc(void) { return gNew DoubleCircleData; }
};

Bool DoubleCircleData::Message(GeListNode *node, LONG type, void *data)
{
	if (type==MSG_MENUPREPARE)
	{
		BaseDocument *doc = (BaseDocument*)data;
		((BaseObject*)node)->GetDataInstance()->SetLong(PRIM_PLANE,doc->GetSplinePlane());
	}

	return TRUE;
}


Bool DoubleCircleData::Init(GeListNode *node)
{	
	BaseObject		*op   = (BaseObject*)node;
	BaseContainer *data = op->GetDataInstance();

	data->SetReal(CIRCLEOBJECT_RAD,200.0);
	data->SetLong(PRIM_PLANE,0);
	data->SetBool(PRIM_REVERSE,FALSE);
	data->SetLong(SPLINEOBJECT_INTERPOLATION,Iadaptive);
	data->SetLong(SPLINEOBJECT_SUB,8);
	data->SetReal(SPLINEOBJECT_ANGLE,Rad(5.0));

	return TRUE;
}

static Vector SwapPoint(const Vector &p, LONG plane)
{
	switch (plane)
	{
		case 1: return Vector(-p.z,p.y,p.x); break;
		case 2: return Vector(p.x,-p.z,p.y); break;
	}
	return p;
}

static Vector GetRTHandle(PluginObject *op, LONG id)
{
	BaseContainer *data = op->GetDataInstance();
	Real rad	  = data->GetReal(CIRCLEOBJECT_RAD);
	LONG plane  = data->GetLong(PRIM_PLANE);
	return SwapPoint(Vector(rad,0.0,0.0),plane);
}

Bool DoubleCircleData::Draw(PluginObject *op, LONG type, BaseDraw *bd, BaseDrawHelp *bh)
{
	if (type!=DRAWPASS_HANDLES) return TRUE;

	BaseContainer *data = op->GetDataInstance();

	Matrix  m = bh->GetMg();
	
	bd->SetPen(GetWorldColor(COLOR_ACTIVEPOINT));

	bd->Handle3D(GetRTHandle(op,0)*m,HANDLE_BIG);
	bd->Line3D(GetRTHandle(op,0)*m,m.off);
	
	return TRUE;
}

LONG DoubleCircleData::DetectHandle(PluginObject *op, BaseDraw *bd, LONG x, LONG y, LONG qualifier)
{
	if (qualifier&QUALIFIER_CTRL) return NOTOK;
	Matrix	mg = op->GetMg();
	if (bd->PointInRange(GetRTHandle(op,0)*mg,x,y)) return 0;
	return NOTOK;
}

Bool DoubleCircleData::MoveHandle(PluginObject *op, PluginObject *undo, const Matrix &tm, LONG hit_id, LONG qualifier)
{
	BaseContainer *src = undo->GetDataInstance();
	BaseContainer *dst = op  ->GetDataInstance();
	
	Vector handle_dir=Vector(1.0,0.0,0.0); 
	handle_dir=SwapPoint(handle_dir,src->GetLong(PRIM_PLANE));
	
	Real val = tm.off*handle_dir;

	dst->SetReal(CIRCLEOBJECT_RAD,FCut(src->GetReal(CIRCLEOBJECT_RAD)+val,0.0,MAXRANGE));
	return TRUE;
}

SplineObject *GenerateCircle(Real rad)
{
	#define TANG 0.415

	Real	sn,cs;
	LONG	i,sub=4;

	SplineObject *op = SplineObject::Alloc(sub*2,Thermite);
	if (!op || !op->MakeVariableTag(Tsegment,2)) { blDelete(op); return NULL; }
  op->GetDataInstance()->SetBool(SPLINEOBJECT_CLOSED,TRUE);

	Vector  *padr = op->GetPoint();
	Tangent *hadr = op->GetTangent();
	Segment *sadr = op->GetSegment();

	if (sadr)
	{
		sadr[0].closed = TRUE;
		sadr[0].cnt    = sub;
		sadr[1].closed = TRUE;
		sadr[1].cnt    = sub;
	}

	for (i=0; i<sub; i++)
	{
		SinCos(2.0*pi*i/Real(sub),sn,cs);
		
		padr[i]    = Vector(cs*rad,sn*rad,0.0);
		hadr[i].vl = Vector(sn*rad*TANG,-cs*rad*TANG,0.0);
		hadr[i].vr = -hadr[i].vl;

		padr[i+sub]    = Vector(cs*rad,sn*rad,0.0)*0.5;
		hadr[i+sub].vl = Vector(sn*rad*TANG,-cs*rad*TANG,0.0)*0.5;
		hadr[i+sub].vr = -hadr[i+sub].vl;
	}

	op->Message(MSG_UPDATE);

	return op;
}

static void OrientObject(SplineObject *op, LONG plane, Bool reverse)
{
	Vector  *padr=ToPoint(op)->GetPoint();
	Tangent *hadr=ToSpline(op)->GetTangent(),h;
	LONG		pcnt =ToPoint(op)->GetPointCount(),i;

	if (plane>=1)
	{
		switch (plane)
		{
			case 1: // ZY
				for (i=0; i<pcnt; i++)
				{
					padr[i]    = Vector(-padr[i].z,padr[i].y,padr[i].x);
					if (!hadr) continue;
					hadr[i].vl = Vector(-hadr[i].vl.z,hadr[i].vl.y,hadr[i].vl.x);
					hadr[i].vr = Vector(-hadr[i].vr.z,hadr[i].vr.y,hadr[i].vr.x);
				}
				break;

			case 2: // XZ
				for (i=0; i<pcnt; i++)
				{
					padr[i] = Vector(padr[i].x,-padr[i].z,padr[i].y);
					if (!hadr) continue;
					hadr[i].vl = Vector(hadr[i].vl.x,-hadr[i].vl.z,hadr[i].vl.y);
					hadr[i].vr = Vector(hadr[i].vr.x,-hadr[i].vr.z,hadr[i].vr.y);
				}
				break;
		}
	}
	
	if (reverse)
	{
		Vector	p;
		LONG		to=pcnt/2;
		if (pcnt%2) to++;
		for (i=0; i<to; i++)
		{
			p=padr[i]; padr[i]=padr[pcnt-1-i]; padr[pcnt-1-i]=p;
			if (!hadr) continue;
			h=hadr[i]; 
			hadr[i].vl=hadr[pcnt-1-i].vr; 
			hadr[i].vr=hadr[pcnt-1-i].vl; 
			hadr[pcnt-1-i].vl=h.vr;
			hadr[pcnt-1-i].vr=h.vl;
		}
	}
	op->Message(MSG_UPDATE);
}

SplineObject *DoubleCircleData::GetContour(PluginObject *op, BaseDocument *doc, Real lod, BaseThread *bt)
{
	BaseContainer *bc=op->GetDataInstance();
	SplineObject *bp = GenerateCircle(bc->GetReal(CIRCLEOBJECT_RAD));
	if (!bp) return NULL;
	BaseContainer *bb=bp->GetDataInstance();

	bb->SetLong(SPLINEOBJECT_INTERPOLATION,bc->GetLong(SPLINEOBJECT_INTERPOLATION));
	bb->SetLong(SPLINEOBJECT_SUB,bc->GetLong(SPLINEOBJECT_SUB));
	bb->SetReal(SPLINEOBJECT_ANGLE,bc->GetReal(SPLINEOBJECT_ANGLE));

	OrientObject(bp,bc->GetLong(PRIM_PLANE),bc->GetBool(PRIM_REVERSE));

	return bp;
}

Bool DoubleCircleData::GetDEnabling(GeListNode *node, const DescID &id,GeData &t_data,LONG flags,const BaseContainer *itemdesc)
{
	LONG inter;
	BaseContainer &data = *((BaseObject*)node)->GetDataInstance();
	switch (id[0].id)
	{
		case SPLINEOBJECT_SUB:		
			inter=data.GetLong(SPLINEOBJECT_INTERPOLATION);
			return inter==SPLINEOBJECT_INTERPOLATION_NATURAL || inter==SPLINEOBJECT_INTERPOLATION_UNIFORM;

		case SPLINEOBJECT_ANGLE:	
			return data.GetLong(SPLINEOBJECT_INTERPOLATION)==SPLINEOBJECT_INTERPOLATION_ADAPTIVE;
	}
	return TRUE;
}

// be sure to use a unique ID obtained from www.plugincafe.com
#define ID_CIRCLEOBJECT 1001154

Bool RegisterCircle(void)
{
	// decide by name if the plugin shall be registered - just for user convenience
	String name=GeLoadString(IDS_CIRCLE); if (!name.Content()) return TRUE;
	return RegisterObjectPlugin(ID_CIRCLEOBJECT,name,OBJECT_GENERATOR|OBJECT_SPLINE,DoubleCircleData::Alloc,"Odoublecircle","circle.tif","circle_small.tif",0);
}
