#include "MAXProxyHeaders.hpp"

//
//############################################################################
//############################    MAXLight    #############################
//############################################################################
//

MemoryBlock*
	MAXLight::AllocatedMemory = NULL;
MAXLight::ClassData*
	MAXLight::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXLight::InitializeClass()
{
	Verify(!AllocatedMemory);
	AllocatedMemory =
		new MemoryBlock(
			sizeof(MAXLight),
			10,
			10,
			"MAXLight"
		);
	Register_Object(AllocatedMemory);

	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			MAXLightClassID,
			"MAXLight",
			LightProxy::DefaultData
		);
	Register_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXLight::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;

	Unregister_Object(AllocatedMemory);
	delete AllocatedMemory;
	AllocatedMemory = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXLight::Destroy()
{
	Check_Object(this);
	Verify(referenceCount == 1);
	STOP(("Not implemented"));
	DetachReference();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MAXLight::MAXLight(
	MAXScene *scene,
	MAXGroup *parent,
	INode *light,
	int index
):
	Proxies::LightProxy(DefaultData, scene, parent),
	proxiedLight(light),
	childIndex(index)
{
	Check_Pointer(this);
	Check_Object(scene);
	Check_Pointer(light);
	Check_Object(this);

	lightObject = NULL;
	TimeValue time = scene->GetTime();
	Object *obj = light->EvalWorldState(time).obj;
	if (obj->SuperClassID()==LIGHT_CLASS_ID) 
	{ 
		lightObject = (LightObject *)obj->ConvertToType(time, 
			Class_ID(LIGHT_CLASS_ID, 0));
		lightObject = (LightObject *)obj;
		Interval valid;
		lightObject->EvalLightState(0,valid, &lightState);
	}
	if (obj->CanConvertToType(Class_ID(TARGET_CLASS_ID, 0)))	
	{
		// This is the a light target for directional lights, From Solution 1888
		// The directional light looks down its negative Z axis (just like a camera does). You'll have to get the NodeTM of the light using GetNodeTMAfterWSM(), since the TM can be affected by a SpaceWarp and you need to take that into account.
	}
	//Verify(lightObject != NULL);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MAXLight::~MAXLight()
{
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXLight::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
	Check_Pointer(proxiedLight);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXLight::TransferAndAppendToParentGroup(Proxies::GroupProxy *parent)
{
	Check_Object(this);
	STOP(("Not implemented"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Proxies::ChildProxy*
	MAXLight::UseNextSiblingProxy()
{
	Check_Object(this);
	Check_Pointer(proxiedLight);
	INode *parent = proxiedLight->GetParentNode();
	Check_Pointer(parent);

	unsigned children = parent->NumberOfChildren();;
	INode *next = NULL;

	if (!children)
	{
		return NULL;
	}
	if (childIndex+1 < children)
	{
		next = parent->GetChildNode(childIndex+1);
	}
	if (next)
	{
		return
			GetSceneProxy()->InterpretRecord(GetParentGroupProxy(), next, childIndex+1,true);
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Proxies::ChildProxy*
	MAXLight::UsePreviousSiblingProxy()
{
	
	Check_Object(this);
	Check_Pointer(proxiedLight);
	INode *parent = proxiedLight->GetParentNode();
	Check_Pointer(parent);

	unsigned children = parent->NumberOfChildren();;
	INode *prev = NULL;

	if (!children)
	{
		return NULL;
	}
	if (childIndex-1 >= 0)
	{
		prev = parent->GetChildNode(childIndex+1);
	}
	if (prev)
	{
		return
			GetSceneProxy()->InterpretRecord(GetParentGroupProxy(), prev, childIndex-1,true);
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	MAXLight::GetName(MString *name)
{
	Check_Object(this);
	Check_Object(name);
	Check_Pointer(proxiedLight);

	TCHAR *string = proxiedLight->GetName();
	int len = strlen(string);
	if (len != 0)
	{
		*name = string;
		return true;
	}
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXLight::SetName(const char* name)
{
	Check_Object(this);
	Check_Pointer(name);
	Check_Pointer(proxiedLight);

	TCHAR *string = (TCHAR *)name;
	proxiedLight->SetName(string);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	MAXLight::GetLocalToParent(LinearMatrix4D *matrix)
{
	Check_Object(this);
	Check_Pointer(matrix);
	Check_Pointer(proxiedLight);

#if 1

	Matrix3 parentTM, nodeTM, localTM;
	nodeTM = proxiedLight->GetNodeTM(0);
	parentTM = proxiedLight->GetParentTM(0);
	localTM = nodeTM*Inverse(parentTM);
	//Matrix3 parentTM = proxiedLight->GetNodeTM(GetSceneProxy()->GetTime(),NULL);
	//Matrix3 myTM = proxiedLight->GetParentTM(GetSceneProxy()->GetTime());
	//Matrix3 mat = Inverse(parentTM) * myTM;

#else

	Matrix3 parentTM = proxiedLight->GetObjTMAfterWSM(GetSceneProxy()->GetTime(),NULL);
	Matrix3 myTM = proxiedLight->GetObjTMAfterWSM(GetSceneProxy()->GetTime(), NULL);

	Matrix3 mat = Inverse(parentTM) * myTM;

#endif

	AffineParts parts;
	decomp_affine(localTM, &parts);

	Point3D translation;
	translation=ConvertMaxToMW(parts.t);

	UnitQuaternion rotation;
	rotation=ConvertMaxToMW(parts.q);


	matrix->BuildRotation(rotation);
	matrix->BuildTranslation(translation);
	return *matrix != LinearMatrix4D::Identity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXLight::SetLocalToParent(const LinearMatrix4D &matrix)
{
	Check_Object(this);
	Check_Object(&matrix);
//	Matrix3	*mat3 = matrix;
//	SetNodeTM(time, mat3);
	STOP(("Not implemented"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
MAXLight::GetOBB(OBB *obb)
{
	Check_Object(this);
	Check_Pointer(obb);
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXLight::SetOBB(const OBB &obb)
{
	Check_Object(this);
	Check_Object(&obb);
	STOP(("Not Possible"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	MAXLight::GetBoundingSphere(Sphere *sphere)
{
	Check_Object(this);
	Check_Pointer(sphere);
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXLight::SetBoundingSphere(const Sphere &sphere)
{
	Check_Object(this);
	Check_Object(&sphere);
	STOP(("Not Possible"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXLight::GetColor(RGBColor *color)
{
	Check_Object(this);
	Check_Pointer(color);
	if (lightObject)
	{
		Check_Pointer(lightObject);

		//
		//--------------------
		// Get the light color
		//--------------------
		//
		color->red = lightState.color.r;
		color->green = lightState.color.g;
		color->blue = lightState.color.b;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXLight::SetColor(const RGBColor &color)
{
	Check_Object(this);
	Check_Object(&color);
	Check_Pointer(lightObject);
	//
	//--------------------
	// Set the light color
	//--------------------
	//
	//lightData->color.x = color.red;
	//lightData->color.y = color.green;
	//lightData->color.z = color.blue;
	Point3 c(color.red,color.green,color.blue);
	lightObject->SetRGBColor(0,c);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	MAXLight::IsAmbient()
{
	Check_Object(this);
	//Check_Pointer(lightData);
	if (lightObject)
	{
		if (lightState.type == AMBIENT_LGT)
		{
			return true;
		}
	}
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXLight::SetAmbient(bool ambient)
{
	Check_Object(this);
	STOP(("Not implemented"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	MAXLight::GetFalloffDistance(
		Scalar *n,
		Scalar *f
	)
{
	Check_Object(this);
	Check_Pointer(n);
	Check_Pointer(f);

	//
	// this routine seems to ask for the fall off distance but
	// JM's version returns the start of the fall off and the 
	// end of the fall off I think!  So I will check the type of light
	// to see if it can have falloff, get the fall off angle.  If 
	// the falloff angle is not equal 0.0 I set the start to 0 and
	// get the fall off distance and set that as the end
	//
	if (!lightObject)
		return false;
	if (lightState.type == OMNI_LGT || lightState.type == SPOT_LGT 
		|| lightState.type == DIRECT_LGT)
	{
		Interval valid(0,0);

		float falloff_angle = 
			lightObject->GetFallsize(GetSceneProxy()->GetTime(),valid);
		if (falloff_angle != 0.0)
		{
			*n = 0.0;
			*f = lightObject->GetTDist(GetSceneProxy()->GetTime(),valid);
			return true;
		}
		else
		{
			*n = 0.0;
			*f = 0.0;
		}
	}
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXLight::SetFalloffDistance(
		Scalar n,
		Scalar f
	)
{
	Check_Object(this);
	Check_Pointer(lightObject);
	Scalar distance = f - n;
	lightObject->SetTDist(GetSceneProxy()->GetTime(),(float)distance);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	MAXLight::GetSpreadAngle(Radian *angle)
{
	Check_Object(this);
	Check_Pointer(angle);
	if (lightObject)
	{
		Check_Pointer(lightObject);

		MAXScene *scene = GetSceneProxy();
		Check_Pointer(scene);

		if (lightState.type != SPOT_LGT)
		{
			return false;
		}

		Interval valid = Interval(0,0);
		angle->angle = lightObject->GetFallsize(scene->GetTime(), valid);
		return true;
	}
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXLight::SetSpreadAngle(const Radian &radian)
{
	Check_Object(this);
	Check_Pointer(lightObject);

	MAXScene *scene = GetSceneProxy();
	Check_Pointer(scene);
	lightObject->SetFallsize(scene->GetTime(),radian.angle);
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
INode 
	*MAXLight::GetNextChild()
{
	Check_Pointer(proxiedLight);
	INode *parent = proxiedLight->GetParentNode();
	Check_Pointer(parent);
	unsigned children = parent->NumberOfChildren();;

	if (!children)
	{
		return NULL;
	}
	if (childIndex < children)
	{
		INode *child = parent->GetChildNode(childIndex);
		childIndex++;
		return child;
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
INode 
	*MAXLight::GetPreviousChild()
{
	Check_Pointer(proxiedLight);
	INode *parent = proxiedLight->GetParentNode();
	Check_Pointer(parent);
	unsigned children = parent->NumberOfChildren();;

	if (!children)
	{
		return NULL;
	}
	if (childIndex > -1)
	{
		INode *child = parent->GetChildNode(childIndex);
		childIndex--;
		return child;
	}
	return NULL;
}
