/****************************************************************************************************

		SpatialForce.cpp
		Copyright: (c) 2001 Eovia Corp. All rights reserved.

		Author:	pascalc
		Date:	12/6/2005

****************************************************************************************************/

#include "SpatialForce.h"
#include "SpatialForceDef.h"

#include "I3DShTreeElement.h"
#include "IShUtilities.h"
#include "MCCountedPtrHelper.h"

const MCGUID CLSID_SpatialForce (R_CLSID_SpatialForce);

/*********** Plugin functions ***********/

void Extension3DInit(IMCUnknown* utilities)
{
	// Perform your dll initialization here
}

void Extension3DCleanup()
{
	// Perform any nec clean-up here
}

TBasicUnknown* MakeCOMObject(const MCCLSID& classId)	// This method instanciate
{														// the object Sphere
	TBasicUnknown* res = NULL;

	if (classId == CLSID_SpatialForce) res = new SpatialForce;

	return res;
}

/*********** SpatialForce component functions ***********/

SpatialForce::SpatialForce()
{
	fData.fStrength = 78.74f;
	fData.fSoftness = 0;
}

MCCOMErr SpatialForce::QueryInterface(const MCIID &riid, void** ppvObj)
{
	if (MCIsEqualIID(riid, IID_I3DExForce))
	{
		TMCCountedGetHelper<I3DExForce> result(ppvObj);
		result = this;
		return MC_S_OK;
	}
	else if (TBasicWireframeSet::QueryInterface(riid, ppvObj) == MC_S_OK)
	{
		return MC_S_OK;
	}
	return TBasicDataComponent::QueryInterface(riid, ppvObj);
}

void* SpatialForce::GetExtensionDataBuffer()
{
	return &fData;
}

MCCOMErr SpatialForce::ExtensionDataChanged()
{
	if (fData.fSoftness < 0) fData.fSoftness = 0;
	else if (fData.fSoftness > 1) fData.fSoftness = 1;
	return MC_S_OK;
}

/*********** SpatialForce force functions ***********/

MCCOMErr SpatialForce::GetForceAndTorque	(const TPhysicalState& inPhysicalState, TVector3& outForce, TVector3& outTorque)
{
	if (!inPhysicalState.fForceTree)
		return MC_S_FALSE;

	const TTransform3D& invT = inPhysicalState.fForceTree->GetInverseGlobalTransform3D();
	TTreeTransform treeT;
	inPhysicalState.fForceTree->GetGlobalTreeTransform(treeT);

	TVector3 applyPoint = kStdInvWidth * invT.TransformPoint(inPhysicalState.fPosition);

	real spatialFactor = 1;
	for (int i=0; i<3; i++)
	{
		if (applyPoint[i] < 0) applyPoint[i] = -applyPoint[i];
		if (applyPoint[i] > 1)
			spatialFactor = 0;
		else if (1 - applyPoint[i] < fData.fSoftness)
			spatialFactor *= 1 - (1 - applyPoint[i]) / fData.fSoftness;
	}

	outForce = spatialFactor * inPhysicalState.fMass * fData.fStrength * treeT.GetRotation() * (TVector3::kUnitZ);
	return MC_S_OK;
}

/*********** SpatialForce graphic functions ***********/

MCCOMErr SpatialForce::TrackWireFrame (I3DShWireFrameSet* wireFrame,int16 proj,I3DShTreeElement* tree,const TWFHitInfo& handle,const TRACKINFO& startinfo,const TRACKINFO& previnfo,const TRACKINFO& nextinfo)
{

	return MC_S_OK;
}

MCCOMErr SpatialForce::DataToWireFrame(I3DShWireFrameSet* wireFrame, int16 proj, I3DShTreeElement* tree)
{
	wireFrame->AllocateLists(1, 3);

	/*** Create cube ***/

	TWFConnectionList& cubeSegments = wireFrame->GetConnectionList(0);
	cubeSegments.PreAllocateConnections(12);
	cubeSegments.SetIsSelectable( false );
	cubeSegments.SetDrawWBProjs(true);
	for (int j=0; j<4; j++)
	{
		cubeSegments.AddConnection( TVector3((j % 2) * kStdWidth * 2 - kStdWidth, (j / 2) * kStdWidth * 2 - kStdWidth, -kStdWidth), TVector3((j % 2) * kStdWidth * 2 - kStdWidth, (j / 2) * kStdWidth * 2 - kStdWidth, kStdWidth) );
		cubeSegments.AddConnection( TVector3((j % 2) * kStdWidth * 2 - kStdWidth, -kStdWidth, (j / 2) * kStdWidth * 2 - kStdWidth), TVector3((j % 2) * kStdWidth * 2 - kStdWidth, kStdWidth, (j / 2) * kStdWidth * 2 - kStdWidth) );
		cubeSegments.AddConnection( TVector3(-kStdWidth, (j % 2) * kStdWidth * 2 - kStdWidth, (j / 2) * kStdWidth * 2 - kStdWidth), TVector3(kStdWidth, (j % 2) * kStdWidth * 2 - kStdWidth, (j / 2) * kStdWidth * 2 - kStdWidth) );
	}

	/*** Create softness handle ***/

	TVector3 softnessHandle(kStdWidth * (1 - fData.fSoftness),kStdWidth * (1 - fData.fSoftness),0);

	TWFPointList& softnessPoint = wireFrame->GetPointList(0);
	softnessPoint.PreAllocatePoints(1);
	softnessPoint.SetHandleType(kWFRoundHandle);
	softnessPoint.AddPoint( softnessHandle );
	softnessPoint.SetDrawWBProjs(true);

	TWFConnectionList& softnessSegment = wireFrame->GetConnectionList(1);
	softnessSegment.PreAllocateConnections(1);
	softnessSegment.AddConnection( TVector3::kZero, softnessHandle );

	/*** Create arrow ***/

	TWFConnectionList& arrowSegment = wireFrame->GetConnectionList(2);
	arrowSegment.PreAllocateConnections(42);
	TVector3 top(0,0,-kArrowHeight), middle(0,0,kArrowHeight/3), bottom(0,0,kArrowHeight);
	for (int i=0; i<6; i++)
	{
		TVector3 myV(cos(mypi/3 * i), sin(mypi/3 * i), 0), nextV(cos(mypi/3 * (i+1)), sin(mypi/3 * (i+1)), 0);
		arrowSegment.AddConnection(top, top + kArrowRadius * myV);
		arrowSegment.AddConnection(top + kArrowRadius * nextV, top + kArrowRadius * myV);
		arrowSegment.AddConnection(middle + kArrowRadius * myV, top + kArrowRadius * myV);
		arrowSegment.AddConnection(middle + kArrowRadius * myV, middle + kArrowRadius * nextV);
		arrowSegment.AddConnection(middle + kArrowRadius * myV, middle + kArrowLRadius * myV);
		arrowSegment.AddConnection(middle + kArrowLRadius * myV, middle + kArrowLRadius * nextV);
		arrowSegment.AddConnection(middle + kArrowLRadius * myV, bottom);
	}
	arrowSegment.SetDrawWBProjs(true);

	return MC_S_OK;
}

MCCOMErr SpatialForce::WireFrameToData (I3DShWireFrameSet* wireFrame, int16 proj, I3DShTreeElement* tree)
{
	return MC_S_OK;
}

MCCOMErr SpatialForce::GetWireFrameBBox(TBBox3D* outBBox, int16 proj, I3DShTreeElement* tree)
{
	*outBBox = TBBox3D(TVector3(-kStdWidth,-kStdWidth,-kStdWidth), TVector3(kStdWidth,kStdWidth,kStdWidth));

	return MC_S_OK;
}
