//***************************************************************************************
// function	:	SumTargets
// description: multiply all the weights by the set of new orientations and positions
// and sum the result to give the new orientation and position.
//
// aweights	: array of weights
// qrot  : resulting summed rotation quaternion
// vpos  : resulting summed position vector3
// atargetori : array of target orientations
// atargetpos : array of target positions
//***************************************************************************************
void CAxisInterpOp::SumTargets( XSI::MATH::CQuaternion& qrot, XSI::MATH::CVector3&  vpos, 
	unsigned long size, double* aweights, double* atargetori, double* atargetpos )
{
	double w;

	XSI::MATH::CRotation r1;
	XSI::MATH::CQuaternion q1;
	XSI::MATH::CQuaternion q2;
	XSI::MATH::CVector3 v1;

	qrot.Set(0,0,0,0);

	for ( unsigned long i=0; i < size; i++ )  
	{
		w = aweights[i];

		if ( w != 0 ) { 
			q1.SetFromXYZAnglesValues( d2r(atargetori[(i*3)+0]), d2r(atargetori[(i*3)+1]), d2r(atargetori[(i*3)+2]) );

			// w.qi
			q2.Set( q1.GetW() * w, q1.GetX() * w, q1.GetY() * w, q1.GetZ() * w );
			qrot.AddInPlace( q2 );

			// w.vi
			v1.Set( atargetpos[(i*3)+0], atargetpos[(i*3)+1], atargetpos[(i*3)+2] );
			v1.ScaleInPlace( w );

			vpos.AddInPlace( v1 );
		}

	}

	qrot.Normalize();
}
CStatus CHelperBoneOp::Update
(
	UpdateContext&	ctx,
	OutputPort&	output
)
{
	///////////////////////////////////////////////////////////////
	// get operator
	///////////////////////////////////////////////////////////////
	Operator op(ctx.GetOperator());

	///////////////////////////////////////////////////////////////
	// get output port
	///////////////////////////////////////////////////////////////
 
	KinematicState gkHelper(output.GetValue());

	///////////////////////////////////////////////////////////////
	// get helper bone data
	///////////////////////////////////////////////////////////////

	InputPort bonedataport(op.GetPort(L"bonedataport",L"HelperBoneGroup",0));
	Property  HelperBoneData(bonedataport.GetValue());

	bool enabled = HelperBoneData.GetParameterValue(L"Enabled");

	// not enabled: do nothing
	if (!enabled) 
		return CStatus::OK;

	///////////////////////////////////////////////////////////////
	// evaluate new transformation for helperbone
	///////////////////////////////////////////////////////////////
	XSI::MATH::CTransformation tNewPose;

	bool bComputeInWorldSpace = (0!=(long)HelperBoneData.GetParameterValue(L"ComputationSpace"));

	///////////////////////////////////////////////////////////////
	// get objects connected to input & output ports
	///////////////////////////////////////////////////////////////

	InputPort rootboneport(op.GetPort(L"globalkineport",L"RootBoneGroup",0));
	InputPort parentboneport(op.GetPort(L"globalkineport",L"ParentBoneGroup",0));
	InputPort childboneport(op.GetPort(L"globalkineport",L"ChildBoneGroup",0));

	KinematicState gkRoot(rootboneport.GetValue());
	KinematicState gkParent(parentboneport.GetValue());
	KinematicState gkChild(childboneport.GetValue());

	// get helperbonedata values
	XSI::MATH::CVector3 vBasePos(
		HelperBoneData.GetParameterValue(L"BoneOffsetX"), 
		HelperBoneData.GetParameterValue(L"BoneOffsetY"), 
		HelperBoneData.GetParameterValue(L"BoneOffsetZ") );

	double perc_along_root = (double)HelperBoneData.GetParameterValue(L"BoneDistance") / 100.0;
	double root_bone_length = (double)HelperBoneData.GetParameterValue(L"RootBoneLength");

	XSI::GridData griddata(HelperBoneData.GetParameterValue(L"Triggers"));

	// read triggers
	ReadTriggerData(griddata);

	// GET TRANSFORMATIONS OF ROOT, PARENT & CHILD
	CTransformation tGRoot = gkRoot.GetTransform();
	CTransformation tGParent = gkParent.GetTransform();
	CTransformation tGChild = gkChild.GetTransform();

	///////////////////////////////////////////////////////////////
	// compute new orientation and position based on triggers
	///////////////////////////////////////////////////////////////
	DebugPrint( L"parent", tGParent.GetRotationQuaternion() );
	DebugPrint( L"child", tGChild.GetRotationQuaternion() );

	// GET LOCAL TRANSFORM OF CHILD RELATIVE TO PARENT
	XSI::MATH::CMatrix3 m3Parent( tGParent.GetRotationMatrix3() );
	DebugPrint(L"m3Parent->", m3Parent);

	XSI::MATH::CMatrix3 m3Child( tGChild.GetRotationMatrix3() );
	DebugPrint(L"m3Parent->", m3Child);

	m3Parent.TransposeInPlace();
	DebugPrint(L"m3Parent.TransposeInPlace->", m3Parent);

	// bug #90494
	// m3Child.MulInPlace( m3Parent );
	MulInPlace( m3Child, m3Parent );
	DebugPrint(L"m3Child.MulInPlace->", m3Child);

	// GET ORIENTATION OF BONE2 RELATIVE TO BONE1 AS A QUATERNION
	XSI::MATH::CQuaternion qBone2 = m3Child.GetQuaternion();
	DebugPrint( L"child2parent", qBone2 );

	// MATCH QUATERNIONS
	ComputeWeights( qBone2, m_cTriggers, m_aEnabled, m_aWeights, m_aTriggerOri, m_aTriggerTol );

	// SUM TARGET ORIENTATIONS & POSITIONS
	XSI::MATH::CQuaternion qNewOri;
	XSI::MATH::CVector3 vNewPos;

	SumTargets( qNewOri, vNewPos, m_cTriggers, m_aWeights, m_aTargetOri, m_aTargetPos );	
	DebugPrint( L"qNewPos->", qNewPos );
	DebugPrint( L"vNewOri->", qNewOri );

	// UPDATE TRANSFORMATION
	if (bComputeInWorldSpace)
	{
		// not implemented
	}
	else // Root object space
	{
		// compute initial helperbone position
		vBasePos.PutX( vBasePos.GetX() + (root_bone_length * perc_along_root) );
		vNewPos.AddInPlace(vBasePos);

		// apply changes from triggers
		tNewPose.SetRotationFromQuaternion( qNewOri );
		tNewPose.SetTranslation( vNewPos );

		// map root object space to worldspace
		tNewPose.MulInPlace( tGRoot );
	}

	///////////////////////////////////////////////////////////////
	// update output port
	///////////////////////////////////////////////////////////////

	gkHelper.PutTransform( tNewPose );


	return CStatus::OK;
}