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

// generator object example (with no input objects)

#include "c4d.h"
#include "c4d_symbols.h"
#include "Oroundedtube.h"

class RoundedTube : public ObjectData
{
	public:
		String test;

		virtual Bool Init		(GeListNode *node);
		virtual Bool Read		(GeListNode *node, HyperFile *hf, LONG level);
		virtual Bool Write	(GeListNode *node, HyperFile *hf);

		virtual void GetDimension		(PluginObject *op, Vector *mp, Vector *rad);
		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 BaseObject* GetVirtualObjects(PluginObject *op, HierarchyHelp *hh);
		virtual Bool Message(GeListNode *node, LONG type, void *t_data);

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

Bool RoundedTube::Message(GeListNode *node, LONG type, void *t_data)
{
	if (type==MSG_DESCRIPTION_VALIDATE)
	{
		BaseContainer *data = ((BaseObject*)node)->GetDataInstance();

		CutReal(*data,TUBEOBJECT_IRADX,0.0,data->GetReal(TUBEOBJECT_RAD));
		CutReal(*data,TUBEOBJECT_ROUNDRAD,0.0,data->GetReal(TUBEOBJECT_IRADX));
	}
	else if (type==MSG_MENUPREPARE)
	{
		((BaseObject*)node)->SetPhong(TRUE,FALSE,0.0);
	}
	return TRUE;
}

void RoundedTube::GetDimension(PluginObject *op, Vector *mp, Vector *rad)
{
	BaseContainer *data = op->GetDataInstance(); 
	
	Real rado,radx,rady;
	rado = data->GetReal(TUBEOBJECT_RAD);
	radx = data->GetReal(TUBEOBJECT_IRADX);
	rady = data->GetReal(TUBEOBJECT_IRADY);

	*mp =0.0;
	switch (data->GetLong(PRIM_AXIS))
	{
		case 0: case 1: *rad=Vector(rady,rado+radx,rado+radx); break;
		case 2: case 3: *rad=Vector(rado+radx,rady,rado+radx); break;
		case 4: case 5: *rad=Vector(rado+radx,rado+radx,rady); break;
	}
}

static BaseObject *GenerateLathe(Vector *cpadr, LONG cpcnt, LONG sub, BaseThread *bt)
{
  PolygonObject *op=NULL;
	UVWStruct	    us;
	UVWTag				*tag =NULL;
	Vector				*padr=NULL;
	Polygon				*vadr=NULL;
	LONG					i,j,pcnt,vcnt,a,b,c,d;
	Real					len=0.0,sn,cs,v1,v2,*uvadr=NULL;

	pcnt = cpcnt*sub;
	vcnt = cpcnt*sub;

	op=PolygonObject::Alloc(pcnt,vcnt); 
	if (!op) goto Error;

	tag	= (UVWTag*)op->MakeVariableTag(Tuvw,vcnt); 
	if (!tag) goto Error;

	padr=op->GetPoint();
	vadr=op->GetPolygon();

	uvadr = (Real*) GeAlloc(sizeof(Real)*(cpcnt+1));
	if (!uvadr) goto Error;

	uvadr[0]=0.0;
	for (i=0; i<cpcnt; i++)
	{
		uvadr[i] = len;
		len+=Len(cpadr[(i+1)%cpcnt]-cpadr[i]);
	}

	if (len>0.0) len=1.0/len;
	for (i=0; i<cpcnt; i++)
		uvadr[i]*=len;

	uvadr[cpcnt]=1.0;

	vcnt=0;
	for (i=0; i<sub; i++)
	{
		SinCos(pi2*Real(i)/Real(sub),sn,cs);

		v1=Real(i  )/Real(sub);
		v2=Real(i+1)/Real(sub);

		if (bt && bt->TestBreak()) goto Error;

		for (j=0; j<cpcnt; j++)
		{
			a = cpcnt*i+j;
			padr[a] = Vector(cpadr[j].x*cs,cpadr[j].y,cpadr[j].x*sn);

			if (i<sub)
			{
				b = cpcnt*i          +((j+1)%cpcnt);
				c = cpcnt*((i+1)%sub)+((j+1)%cpcnt);
				d = cpcnt*((i+1)%sub)+j;
				
				us = UVWStruct(Vector(v1,1.0-uvadr[j],0.0),Vector(v1,1.0-uvadr[j+1],0.0),Vector(v2,1.0-uvadr[j+1],0.0),Vector(v2,1.0-uvadr[j],0.0));
				tag->Set(vcnt,us);

				vadr[vcnt++] = Polygon(a,b,c,d);
			}
		}
	}

	GeFree(uvadr);

	op->Message(MSG_UPDATE);
	op->SetPhong(TRUE,TRUE,Rad(80.0));
	return op;

Error:
	GeFree(uvadr);
	blDelete(op);
	return NULL;
}

static LineObject *GenerateIsoLathe(Vector *cpadr, LONG cpcnt, LONG sub)
{
	LONG i;

  LineObject *op = LineObject::Alloc(cpcnt*4+sub*4,8); if (!op) return NULL;
	Segment *sadr = op->GetSegment();
	Vector  *padr=op->GetPoint();

	for (i=0; i<4; i++)
	{
		sadr[i].cnt    = cpcnt;
		sadr[i].closed = TRUE;
	}
	for (i=0; i<4; i++)
	{
		sadr[4+i].cnt    = sub;
		sadr[4+i].closed = TRUE;
	}

	Real	sn,cs;
	LONG	j;

	for (i=0; i<4; i++)
	{
		SinCos(Real(i)*pi05,sn,cs);
		for (j=0; j<cpcnt; j++)
			padr[i*cpcnt+j] = Vector(cpadr[j].x*cs,cpadr[j].y,cpadr[j].x*sn);
	}

	for (i=0; i<sub; i++)
	{
		SinCos(Real(i)/sub*pi2,sn,cs);
		for (j=0; j<4; j++)
			padr[4*cpcnt+j*sub+i] = Vector(cpadr[cpcnt/4*j].x*cs,cpadr[cpcnt/4*j].y,cpadr[cpcnt/4*j].x*sn);
	}

	op->Message(MSG_UPDATE);
	return op;
}

Bool RoundedTube::Init(GeListNode *node)
{	
	test = String("Test");

	BaseObject		*op   = (BaseObject*)node;
	BaseContainer *data = op->GetDataInstance();

	data->SetReal(TUBEOBJECT_RAD,200.0);
	data->SetReal(TUBEOBJECT_IRADX,50.0);
	data->SetReal(TUBEOBJECT_IRADY,50.0);
	data->SetLong(TUBEOBJECT_SUB,1);
	data->SetLong(TUBEOBJECT_ROUNDSUB,8);
	data->SetReal(TUBEOBJECT_ROUNDRAD,10.0);
	data->SetLong(TUBEOBJECT_SEG,36);
	data->SetLong(PRIM_AXIS,PRIM_AXIS_YP);

	return TRUE;
}

Bool RoundedTube::Read(GeListNode *node, HyperFile *hf, LONG level)
{
	if (level>=0)
	{
		hf->ReadString(&test);
	}
	return TRUE;
}

Bool RoundedTube::Write(GeListNode *node, HyperFile *hf)
{
	hf->WriteString(test);
	return TRUE;
}

static void SetAxis(BaseObject *obj, LONG axis)
{
	PointObject *op = ToPoint(obj);
	if (axis==2) return;

	Vector  *padr = op->GetPoint();
	LONG		pcnt  = op->GetPointCount(),i;

	switch (axis)
	{
		case 0: // +X
			for (i=0; i<pcnt; i++)
				padr[i] = Vector(padr[i].y,-padr[i].x,padr[i].z);
			break;

		case 1: // -X
			for (i=0; i<pcnt; i++)
				padr[i] = Vector(-padr[i].y,padr[i].x,padr[i].z);
			break;

		case 3: // -Y
			for (i=0; i<pcnt; i++)
				padr[i] = Vector(-padr[i].x,-padr[i].y,padr[i].z);
			break;

		case 4: // +Z
			for (i=0; i<pcnt; i++)
				padr[i] = Vector(padr[i].x,-padr[i].z,padr[i].y);
			break;

		case 5: // -Z
			for (i=0; i<pcnt; i++)
				padr[i] = Vector(padr[i].x,padr[i].z,-padr[i].y);
			break;
	}

	op->Message(MSG_UPDATE);
}

BaseObject *RoundedTube::GetVirtualObjects(PluginObject *op, HierarchyHelp *hh)
{
	LineObject *lop = NULL;
	BaseObject *ret = NULL;

	Bool dirty = op->CheckCache(hh) || op->IsDirty(DIRTY_DATA);	
	if (!dirty) return op->GetCache(hh);

	BaseContainer *data = op->GetDataInstance();
	
	Real rad	  = data->GetReal(TUBEOBJECT_RAD,200.0);
	Real iradx  = data->GetReal(TUBEOBJECT_IRADX,50.0);
	Real irady  = data->GetReal(TUBEOBJECT_IRADY,50.0);
	Real rrad   = data->GetReal(TUBEOBJECT_ROUNDRAD,10.0);
	LONG sub    = CalcLOD(data->GetLong(TUBEOBJECT_SUB,1),hh->GetLOD(),1,1000);
	LONG rsub   = CalcLOD(data->GetLong(TUBEOBJECT_ROUNDSUB,8),hh->GetLOD(),1,1000);
	LONG seg		= CalcLOD(data->GetLong(TUBEOBJECT_SEG,36),hh->GetLOD(),3,1000);
	LONG axis   = data->GetLong(PRIM_AXIS);
	LONG i;
	Real sn,cs;

	LONG cpcnt  = 4*(sub+rsub);
	Vector *cpadr = (Vector*)GeAlloc(cpcnt*sizeof(Vector));
	if (!cpadr) return NULL;

	for (i=0; i<sub; i++)
	{
		cpadr[i             ]=Vector(rad-iradx,(1.0-Real(i)/Real(sub)*2.0)*(irady-rrad),0.0);
		cpadr[i+   sub+rsub ]=Vector(rad+(Real(i)/Real(sub)*2.0-1.0)*(iradx-rrad),-irady,0.0);
		cpadr[i+2*(sub+rsub)]=Vector(rad+iradx,(Real(i)/Real(sub)*2.0-1.0)*(irady-rrad),0.0);
		cpadr[i+3*(sub+rsub)]=Vector(rad+(1.0-Real(i)/Real(sub)*2.0)*(iradx-rrad),irady,0.0);
	}
	for (i=0; i<rsub; i++)
	{
		SinCos(Real(i)/Real(rsub)*pi05,sn,cs);
		cpadr[i+sub             ]=Vector(rad-(iradx-rrad+cs*rrad),-(irady-rrad+sn*rrad),0.0);
		cpadr[i+sub+  (sub+rsub)]=Vector(rad+(iradx-rrad+sn*rrad),-(irady-rrad+cs*rrad),0.0);
		cpadr[i+sub+2*(sub+rsub)]=Vector(rad+(iradx-rrad+cs*rrad),+(irady-rrad+sn*rrad),0.0);
		cpadr[i+sub+3*(sub+rsub)]=Vector(rad-(iradx-rrad+sn*rrad),+(irady-rrad+cs*rrad),0.0);
	}
	
	if (hh->GetVFlags()&VFLAG_POLYGONAL)
	{
		ret = GenerateLathe(cpadr,cpcnt,seg,hh->GetThread());
		if (!ret) goto Error;
		SetAxis(ret,axis);
		ret->KillTag(Tphong);
		if (!op->CopyTagsTo(ret,TRUE,FALSE,FALSE,NULL)) goto Error;
	}
	else
	{
		ret = PolygonObject::Alloc(0,0);
		if (!ret) goto Error;
	}
	ret->SetName(op->GetName());

	if (hh->GetVFlags()&VFLAG_ISOPARM)
	{
		lop = GenerateIsoLathe(cpadr,cpcnt,seg);
		if (!lop) goto Error;
		SetAxis(lop,axis);
		ret->SetIsoparm(lop);
	}

	GeFree(cpadr);
	return ret;

Error:
	GeFree(cpadr);
	blDelete(ret);
	return NULL;
}

static Vector SwapPoint(const Vector &p, LONG axis)
{
	switch (axis)
	{
		case 0: return Vector(p.y,-p.x,p.z); break;
		case 1: return Vector(-p.y,p.x,p.z); break;
		case 3: return Vector(-p.x,-p.y,p.z); break;
		case 4: return Vector(p.x,-p.z,p.y); break;
		case 5: return Vector(p.x,p.z,-p.y); break;
	}
	return p;
}

static Vector GetRTHandle(PluginObject *op, LONG id)
{
	BaseContainer *data = op->GetDataInstance();

	Vector	p;
	Real		rad	   = data->GetReal(TUBEOBJECT_RAD);
	Real		iradx  = data->GetReal(TUBEOBJECT_IRADX);
	Real		irady  = data->GetReal(TUBEOBJECT_IRADY);
	Real		rrad   = data->GetReal(TUBEOBJECT_ROUNDRAD);
	LONG		axis   = data->GetLong(PRIM_AXIS);

	switch (id)
	{
		case 0: p=Vector(rad,0.0,0.0); break;
		case 1: p=Vector(rad+iradx,0.0,0.0); break;
		case 2: p=Vector(rad,irady,0.0); break;
		case 3: p=Vector(rad+iradx,irady-rrad,0.0); break;
		case 4: p=Vector(rad+iradx-rrad,irady,0.0); break;
	}

	return SwapPoint(p,axis);
}

#define HANDLES 5

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

	BaseContainer *data = op->GetDataInstance();

	LONG    i;
	Matrix  m      = bh->GetMg();
	Real		rad	   = data->GetReal(TUBEOBJECT_RAD);
	Real		iradx  = data->GetReal(TUBEOBJECT_IRADX);
	Real		irady  = data->GetReal(TUBEOBJECT_IRADY);
	LONG		axis   = data->GetLong(PRIM_AXIS);
	
	bd->SetPen(GetWorldColor(COLOR_ACTIVEPOINT));

	for (i=0; i<HANDLES; i++)
		bd->Handle3D(GetRTHandle(op,i)*m,HANDLE_BIG);
	
	bd->Line3D(GetRTHandle(op,0)*m,GetRTHandle(op,1)*m);
	bd->Line3D(GetRTHandle(op,0)*m,GetRTHandle(op,2)*m);
	bd->Line3D(GetRTHandle(op,3)*m,SwapPoint(Vector(rad+iradx,irady,0.0),axis)*m);
	bd->Line3D(GetRTHandle(op,4)*m,SwapPoint(Vector(rad+iradx,irady,0.0),axis)*m);
	return TRUE;
}

LONG RoundedTube::DetectHandle(PluginObject *op, BaseDraw *bd, LONG x, LONG y, LONG qualifier)
{
	if (qualifier&QUALIFIER_CTRL) return NOTOK;

	Matrix	mg = op->GetMg();
	LONG    i,ret=NOTOK;
	Vector	p;

	for (i=0; i<HANDLES; i++)
	{
		p = GetRTHandle(op,i);
		if (bd->PointInRange(p*mg,x,y)) 
		{
			ret=i;
			if (!(qualifier&QUALIFIER_SHIFT)) break;
		}
	}
	return ret;
}

Bool RoundedTube::MoveHandle(PluginObject *op, PluginObject *undo, const Matrix &tm, LONG hit_id, LONG qualifier)
{
	BaseContainer *src = undo->GetDataInstance();
	BaseContainer *dst = op  ->GetDataInstance();
	
	Vector handle_dir;
	switch (hit_id)
	{
		case 0: handle_dir = Vector( 1.0, 0.0,0.0); break;
		case 1: handle_dir = Vector( 1.0, 0.0,0.0); break;
		case 2: handle_dir = Vector( 0.0, 1.0,0.0); break;
		case 3: handle_dir = Vector( 0.0,-1.0,0.0); break;
		case 4: handle_dir = Vector(-1.0, 0.0,0.0); break;
	}

	handle_dir=SwapPoint(handle_dir,src->GetLong(PRIM_AXIS));
	
	Real val = tm.off*handle_dir;

	switch (hit_id)
	{
		case 0: dst->SetReal(TUBEOBJECT_RAD,FCut(src->GetReal(TUBEOBJECT_RAD)+val,dst->GetReal(TUBEOBJECT_IRADX),MAXRANGE)); break;
		case 1: dst->SetReal(TUBEOBJECT_IRADX,FCut(src->GetReal(TUBEOBJECT_IRADX)+val,dst->GetReal(TUBEOBJECT_ROUNDRAD),dst->GetReal(TUBEOBJECT_RAD))); break;
		case 2: dst->SetReal(TUBEOBJECT_IRADY,FCut(src->GetReal(TUBEOBJECT_IRADY)+val,dst->GetReal(TUBEOBJECT_ROUNDRAD),MAXRANGE)); break;
		case 3: case 4: dst->SetReal(TUBEOBJECT_ROUNDRAD,FCut(src->GetReal(TUBEOBJECT_ROUNDRAD)+val,0.0,FMin(dst->GetReal(TUBEOBJECT_IRADX),dst->GetReal(TUBEOBJECT_IRADY)))); break;
	}
	return TRUE;
}

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

Bool RegisterRoundedTube(void)
{
	// decide by name if the plugin shall be registered - just for user convenience
	String name=GeLoadString(IDS_ROUNDED_TUBE); if (!name.Content()) return TRUE;
	return RegisterObjectPlugin(ID_ROUNDEDTUBEOBJECT,name,OBJECT_GENERATOR,RoundedTube::Alloc,"Oroundedtube","roundedtube.tif","roundedtube_small.tif",0);
}
