#include "operatingsystem.h"
#include "c4d_objectplugin.h"
#include "c4d_memory.h"
#include "c4d_general.h"

#ifdef __API_INTERN__
	#include "c4d_resource.h"
	#include "basedraw.h"
	#include "res_colortab_priv.h"
#else
	#include "c4d_tools.h"
	#include "c4d_basebitmap.h"
	#include "c4d_file.h"
	#include "c4d_resource.h"
	#include "c4d_baseobject.h"
	#include "c4d_basedraw.h"
	#include "dbasedraw.h"
#endif

Vector HandleInfo::CalculateNewPosition(BaseDraw *bd, const Matrix &mg, const Vector &mouse_pos) const
{
	if (!bd) return position;
	Vector result = position * mg;
	switch (type)
	{
	case HANDLECONSTRAINTTYPE_LINEAR: 
		result = bd->ProjectPointOnLine(position * mg, direction ^ mg, mouse_pos.x, mouse_pos.y); 
		break;
	case HANDLECONSTRAINTTYPE_PLANAR:
		{
			LONG err;
			result = bd->ProjectPointOnPlane(position * mg, direction ^ mg, mouse_pos.x, mouse_pos.y, &err);
			if (err)
			{
				Vector dir = !(bd->WS((position + direction) * mg) - bd->WS(position * mg)) % Vector(0.0, 0.0, 1.0);
				dir = !(bd->SW(bd->WS(position * mg) + dir) - (position * mg));
				result = bd->ProjectPointOnLine(position * mg, dir, mouse_pos.x, mouse_pos.y);
			}
		}
		break;
	case HANDLECONSTRAINTTYPE_FREE:
		if (mouse_pos.z != 0.0) result = bd->SW(mouse_pos);
		else result = bd->SW(Vector(mouse_pos.x, mouse_pos.y, bd->WC(result).z)); 
		break;
	case HANDLECONSTRAINTTYPE_RADIAL: 
		result = bd->ProjectPointOnPlane(center * mg, direction ^ mg, mouse_pos.x, mouse_pos.y); 
		result = center + (radius * !(result - center)); 
		break;
	case HANDLECONSTRAINTTYPE_SPHERICAL:
		{
			Real r, cp;

			r = radius * bd->WP_W(center * mg, TRUE);
			if (r == 0.0) r = 0.1;
			
			result = (mouse_pos - bd->WS(center * mg));
			result.z = 0.0;
			cp = FMin(Len(result), r);
			result = cp * !result;
			result.z = -Cos(ASin(cp / r)) * r;
			result = bd->SW(result + bd->WS(center * mg));
		} break;
	default: break;
	}
	return result * !mg;
}

void ObjectData::GetDimension(BaseObject *op, Vector *mp, Vector *rad) 
{
	// override GetDimension if you do not want C4D's default handling of bounding boxes and set *mp/*rad to 0.0
}

DRAWRESULT ObjectData::Draw(BaseObject *op, DRAWPASS drawpass, BaseDraw *bd, BaseDrawHelp *bh) 
{ 
	if (drawpass!=DRAWPASS_HANDLES) return DRAWRESULT_SKIP;

	LONG	 i,pass;
	Vector p;
	LONG   cnt = GetHandleCount(op);
	if (!cnt) return DRAWRESULT_SKIP;

	#ifdef __API_INTERN__
		LONG hitid = C4DOS.Bo->GetHighlightHandle(op,bd);
		const Matrix &mg = bh->m;
	#else
		LONG hitid = op->GetHighlightHandle(bd);
		const Matrix &mg = bh->GetMg();
	#endif
	Vector col1 = GetViewColor(VIEWCOLOR_SELECTION_PREVIEW);
	Vector col2 = GetViewColor(VIEWCOLOR_ACTIVEPOINT);

	bd->SetMatrix_Matrix(op, mg);

	for (pass=0; pass<2; pass++)
	{
		if (pass==0)
			bd->SetPen(col2);
		else
			bd->SetPen(col1);

		for (i=0; i<cnt; i++)
		{
			if ((i==hitid)!=pass) continue;
			HandleInfo info;
			GetHandle(op, i, info);
			if (info.type == HANDLECONSTRAINTTYPE_INVALID) continue;
			bd->DrawHandle(info.position, DRAWHANDLE_BIG, 0);

			//#ifndef __API_INTERN__
				//bd->Line3D(Vector(0.0,0.0,0.0)*mg,p);
			//#endif
		}
	}

	return DRAWRESULT_OK;
}

DRAWRESULT ObjectData::DrawShadow(BaseObject *op, BaseDraw *bd, BaseDrawHelp *bh)
{
	return DRAWRESULT_SKIP;
}

void ObjectData::GetHandle(BaseObject *op, LONG i, HandleInfo &info)
{
}

void ObjectData::SetHandle( BaseObject *op, LONG i, Vector p, const HandleInfo &info )
{
}

LONG ObjectData::GetHandleCount(BaseObject *op)
{
	return 0;
}

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

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

	for (i=0; i<GetHandleCount(op); i++)
	{
		HandleInfo info;
		GetHandle(op,i, info);
		if (info.type == HANDLECONSTRAINTTYPE_INVALID) continue;

		if (bd->PointInRange(info.position*mg,x,y)) 
		{
			ret=i;
			if (!(qualifier&QUALIFIER_SHIFT)) break;
		}
	}
	return ret;
}

Bool ObjectData::MoveHandle( BaseObject *op, BaseObject *undo, const Vector &mouse_pos, LONG hit_id, QUALIFIER qualifier, BaseDraw *bd )
{ 
	Matrix mg = op->GetUpMg() * undo->GetMl();
	HandleInfo info;

	((ObjectData*)undo->GetNodeData())->GetHandle(undo,hit_id,info);
	SetHandle(op,hit_id,info.CalculateNewPosition(bd, mg, mouse_pos),info);

	return TRUE;
}

SplineObject* ObjectData::GetContour(BaseObject *op, BaseDocument *doc, Real lod, BaseThread *bt) 
{ 
	return NULL; 
}

BaseObject* ObjectData::GetVirtualObjects(BaseObject *op, HierarchyHelp *hh) 
{ 
	return NULL; 
}

Bool ObjectData::ModifyObject(BaseObject *op, BaseDocument *doc, BaseObject *mod, const Matrix &op_mg, const Matrix &mod_mg, Real lod, LONG flags, BaseThread *thread) 
{ 
	return TRUE; 
}

void ObjectData::CheckDirty(BaseObject *op, BaseDocument *doc) 
{
}

void ObjectData::ModifyParticles(BaseObject *op, Particle *pp, BaseParticle *ss, LONG pcnt, Real diff)
{
}

void ObjectData::GetModelingAxis(BaseObject *op, BaseDocument *doc, Matrix &axis)
{
}

Bool ObjectData::AddToExecution(BaseObject *op, PriorityList *list)
{
	return FALSE;
}

EXECUTIONRESULT ObjectData::Execute(BaseObject *op, BaseDocument *doc, BaseThread *bt, LONG priority, EXECUTIONFLAGS flags)
{
	return EXECUTIONRESULT_OK;
}

static void FillObjectPlugin(OBJECTPLUGIN *np, DataAllocator *npalloc, LONG info, LONG disklevel, BaseBitmap *icon, void *emulation)
{
	FillNodePlugin(np,info,npalloc,icon,disklevel,emulation);

	np->command_icon				= icon;
	np->GetDimension				= &ObjectData::GetDimension;
	np->Draw								= &ObjectData::Draw;
	np->DrawShadow					= &ObjectData::DrawShadow;
	np->DetectHandle				= &ObjectData::DetectHandle;
	np->MoveHandle					= &ObjectData::MoveHandle;
	np->Execute							= &ObjectData::Execute;
	np->AddToExecution			= &ObjectData::AddToExecution;
	np->GetModelingAxis			= &ObjectData::GetModelingAxis;
}

Bool RegisterObjectPlugin(LONG id, const String &str, LONG objectinfo, DataAllocator *npalloc, const String &description, BaseBitmap *icon, LONG disklevel)
{
	if (description.Content() && !RegisterDescription(id,description)) return FALSE;

	OBJECTPLUGIN np;
	ClearMem(&np,sizeof(np));
	FillObjectPlugin(&np,npalloc,objectinfo,disklevel,icon,NULL);

	np.CheckDirty		= &ObjectData::CheckDirty;

	if (objectinfo&OBJECT_MODIFIER)
		np.ModifyObject	= &ObjectData::ModifyObject;

	if (objectinfo&OBJECT_PARTICLEMODIFIER)
		np.ModifyParticles = &ObjectData::ModifyParticles;
	
	if (objectinfo&OBJECT_ISSPLINE) 
		np.GetContour = &ObjectData::GetContour;
	
	if (objectinfo&OBJECT_GENERATOR) 
		np.GetVirtualObjects = &ObjectData::GetVirtualObjects;

	return GeRegisterPlugin(PLUGINTYPE_OBJECT,id,str,&np,sizeof(np));
}
