// RcRcPhysShotCmd.cpp : Implementation of CRcRcPhysShotCmd

#include "stdafx.h"
#include "RcPhysShotCmd.h"
#include "RpExamplesPlugin.h"

#include "../../Include/PhysicsGuid.h"
#include <math.h>


// CRcPhysShotCmd

STDMETHODIMP CRcPhysShotCmd::OnGetValue(CONID conID, IRsDataCache* pDataCache, VARIANT* pVtData, RtBool* pHandled)
{
	return S_OK;
}

STDMETHODIMP CRcPhysShotCmd::OnSetValue(CONID con_id, IRsDataCache* pDataCache, VARIANT* pvtData, RtBool* pHandled)
{
	return S_OK;
}
STDMETHODIMP CRcPhysShotCmd::OnDefaultValue(CONID con_id, VARIANT* pVtData, RtBool* pHandled)
{
	HRESULT hRes = S_OK;
	if (con_id == CON_SHOT_POWER)
	{
		CComVariant data(15.0f);
		hRes = data.Detach(pVtData);
		*pHandled = TRUE;

	}
	return S_OK;
}

STDMETHODIMP CRcPhysShotCmd::OnInvalidate(CONID con_id, RtBool* pHandled)
{
	return S_OK;
}

STDMETHODIMP CRcPhysShotCmd::OnInitialize(void)
{
	return S_OK;
}

STDMETHODIMP CRcPhysShotCmd::OnLoadDefault(void)
{
	CComQIPtr<IRsBaseNode> spBase = m_spUnkAgg;   // Get the BaseNode interface
	ATLASSERT(spBase);

	HRESULT hr;

	//declare command connector which will serve as input parameter
	RtConDesc sCenter	= {&CON_SHOT_CENTER, L"Center",	  L"Center", VT_UNKNOWN,  &DEF_GUIDNAME(IRdVertex), CONFLG_IN};
	RtConDesc sPower	= {&CON_SHOT_POWER, L"Power",	  L"Power", VT_R4,  NULL, CONFLG_IN};

	//create connectors from descriptor and add it to our command node
	if (FAILED(hr = spBase->AddNewConnector(&sCenter, NULL))) 
		return hr;

	if (FAILED(hr = spBase->AddNewConnector(&sPower, NULL))) 
		return hr;

	return S_OK;
	return S_OK;
}

STDMETHODIMP CRcPhysShotCmd::OnDestroy(void)
{

	if (m_spKernel)
		m_spKernel.Release();

	return S_OK;
}

HRESULT CRcPhysShotCmd::FinalConstruct()
{
	// kernel package
	CComPtr<IRpKernel> spKernelPck;
	HRESULT hRes = CRpExamplesPlugin::GetKernelPackage(&spKernelPck);
	
	// create base node
	if (SUCCEEDED(hRes))
		hRes = spKernelPck->CreateBaseNode(GetControllingUnknown(), &m_spUnkAgg);

	return hRes;
}

STDMETHODIMP CRcPhysShotCmd::Execute(void)
{
	
	CRpExamplesPlugin::GetKernelPackage(&m_spKernel);

	//Find Space3D package which handles 3D scene.
	CComPtr<IRsUnknownPackage> spUnkPck;
	m_spKernel->FindPackage(DEF_GUIDNAME(CRpSpace3DPck), &spUnkPck);
	CComQIPtr<IRpSpace3DPck> spSpace3d = spUnkPck;
	if (!spSpace3d) {
		//Space3d is not registered
		return E_FAIL;
	};

	//create scene helper function set
	CComPtr<IRfSceneHelper> spSceneHelper;
	spSpace3d->CreateSceneHelper(&spSceneHelper);

	//get current scene
	CComPtr<IRnSpace3D> spCurrentScene;
	spSceneHelper->GetCurrentScene(&spCurrentScene);
	CComQIPtr<IRsNode> spSceneNode = spCurrentScene;
	if (!spSceneNode) {
		//no 3d scene found.
		return E_FAIL;
	};

	//query IRnEncapsulator interface so we can access scene sub-objects.
	CComQIPtr<IRnEncapsulator> spSceneEncap = spCurrentScene;

	//Find Physics package 
	CComPtr<IRsUnknownPackage> spPhysPck;
	m_spKernel->FindPackage(DEF_GUIDNAME(CRpPhysics), &spPhysPck);
	CComQIPtr<IRpPhysics> spPhysics = spPhysPck;
	if (!spPhysics) {
		//Physics is not registered
		return E_FAIL;
	};

	//create helper function set
	CComPtr<IRfPhysHelper> spHelper;
	spPhysics->CreatePhysicsFnSet(&spHelper);

	if (spHelper == NULL)
		return E_FAIL;

	//get current scene
	CComPtr<IRsNode> spEngineNode;
	spHelper->GetPhysEngine(NULL, &spEngineNode);
	CComQIPtr<IRnPhysEngine> spPhysEngine = spEngineNode;
	
	if (!spPhysEngine) {

		if (S_OK != spHelper->CreatePhysEngine(NULL, &spEngineNode))
			return E_FAIL;

		spPhysEngine = spEngineNode;
	};

	
	CComQIPtr<IRsBaseNode> spObjNode = m_spUnkAgg;
	
	//get shot power - speed of shot's objects
	CComVariant vPower;
	float fPower;

	spObjNode->GetValue(CON_SHOT_POWER, &vPower);
	if (S_OK == vPower.ChangeType(VT_R4))
	{
		fPower = vPower.fltVal;
	}
	else
	{
		fPower = 15.0f;
	}


	//Get position of shot
	CComVariant vPos;
	RtPoint ptCenter;
	spObjNode->GetValue(CON_SHOT_CENTER, &vPos);

	CComQIPtr<IRdVertex> spPos;
	if ((vPos.vt==VT_UNKNOWN) && (spPos = vPos.punkVal))
	{
		spPos->get_Value(&ptCenter);
	}
	else
	{
		ptCenter.x = ptCenter.y = 0.0f;
		ptCenter.z = 3.0f;
	}




	//create several sphere objects and place them to circle around ptCenter
	CInterfaceArray<IRsBaseNode> objlist;
	RtPoint dir;
	float alfa = 0;

	int objnum = 10;
	float step = 6.28f / (float) objnum;
	//add our new objects into scene. 
	for(int i = 0; i < objnum; i++)
	{
		alfa = i * 	step;
		dir.x = cosf(alfa);
		dir.y = sinf(alfa);
		dir.z = 0.0f;

		//create sphere
		CComPtr<IRsBaseNode> spSphere;
		if (S_OK!= CreateSphere(&spSphere))
			return E_FAIL;

		spSceneEncap->AddSubObject(spSphere);

		//create transformation matrix 
		CComPtr<IRdMatrixf> spMatrix;
		CRpExamplesPlugin::CreateDataObject(&spMatrix);
		
		//modify the scale of default sphere
		spMatrix->Scale(0.1f, 0.1f, 0.1f);

		//modify position of the sphere
		spMatrix->Translate(dir.x + ptCenter.x, dir.y + ptCenter.y, dir.z + ptCenter.z);
		
		//set object matrix to new valuse
		spSphere->SetValue(CON_OBJ_TRANSFORM_INP_MATRIX, CComVariant(spMatrix));

		//Get phys object from sphere	
		CComPtr<IRsNode> spPhysObj;
		spHelper->GetPhysObj(spSphere, &spPhysObj);

		if (!spPhysObj){
			continue;
		}
		
		//set x and y component of speed to be in direction from ptCenter
		spPhysObj->SetValue(CON_PHYSICS_XSPEED, CComVariant(fPower * dir.x));
		spPhysObj->SetValue(CON_PHYSICS_YSPEED, CComVariant(fPower * dir.y));

		//set mass of sphere
		spPhysObj->SetValue(CON_PHYSICS_WEIGHT, CComVariant(100.0f));

		//set special flag for destroying of object after stop of simulation
		spPhysObj->SetValue(CON_PHYSICS_DESTROY, CComVariant(TRUE));

		//stores pointer to object interface in array
		objlist.Add(spSphere);
	}

	//Obtain status of engine 
	RtBool bState;
	spPhysEngine->GetActiveState(&bState);

	if (bState)
	{
		//if engine is running, add new created objects to simulation
		RtUlong cnt = (RtUlong) objlist.GetCount();
		for (RtUlong ind = 0; ind < cnt; ind++)
		{
			spPhysEngine->RegisterObj(objlist[ind]);
		}
	}
	else
	{
		//if engine is not running, start simulation
		spPhysEngine->StartSimulation();
	}
		

	return S_OK;
}

STDMETHODIMP CRcPhysShotCmd::Undo(void)
{
	return S_OK;
}

STDMETHODIMP CRcPhysShotCmd::Redo(void)
{
	return S_OK;
}

STDMETHODIMP CRcPhysShotCmd::HasUndo(RtBool* isUndoable)
{
	//Command is not undoable.
	*isUndoable = false;
	return S_OK;
}

HRESULT CRcPhysShotCmd::CreateSphere(IRsBaseNode **ppNode)
{

	//create encapsulator fn set which contains connector exporting functions
	CComPtr<IRfEncapsulatorSet> spEncapsSet;
	m_spKernel->CreateEncapsulatorFnSet(&spEncapsSet);


	//assemble our new object

	//create new encapsulator which will contain cube shape node and matrix node.
	CComPtr<IRnEncapsulator> spOurObjEncaps;
	CRpExamplesPlugin::CreateNode(&spOurObjEncaps);

	//query IRsBaseNode interface so we can manipulate object name and connectors
	CComQIPtr<IRsBaseNode> spOurObjBase = spOurObjEncaps;
	spOurObjBase->put_Name(L"Sphere");

	spEncapsSet->Attach(spOurObjBase);

	//create cube node
	CComPtr<IRnSphere2> spSphere;
	CRpExamplesPlugin::CreateNode(&spSphere);

	
	//get sphere IRsBaseNode interface
	CComQIPtr<IRsBaseNode> spSphereBase = spSphere;

	//we are fine with default values, but we can use spSphereBase->SetValue(con_id,value) to change them.
	//also, if we wished we can change cube name.

	//add sphere into our object. 
	spOurObjEncaps->AddSubObject(spSphereBase);

	//find mesh connector and export it using encapsulator fn set
	CComPtr<IRsConnector> spMeshCon;
	spSphereBase->FindConById(CON_MESH, &spMeshCon);
	spEncapsSet->ExportConnector(spMeshCon, true, false, false, NULL);

	//create transform node
	CComPtr<IRnTransform> spTransform;
	CRpExamplesPlugin::CreateNode(&spTransform);
	CComQIPtr<IRsBaseNode> spTransformBase = spTransform;

	//add transform node into our object. 
	spOurObjEncaps->AddSubObject(spTransformBase);


	//create phys objects and add into our object
	CComPtr<IRnPhysActiveObj> spPhysObj;
	CRpExamplesPlugin::CreateNode(&spPhysObj);
	CComQIPtr<IRsBaseNode> spPhysBase = spPhysObj;
	spOurObjEncaps->AddSubObject(spPhysBase);

	//set identity matrices to parent and object connectors
	CComPtr<IRdMatrixf> spMatrixParent;
	CRpExamplesPlugin::CreateDataObject(&spMatrixParent);
	spMatrixParent->LoadIdentity();
	spTransformBase->SetValue(CON_TRANSFORM_PARENT_MATRIX, CComVariant(spMatrixParent));

	CComPtr<IRdMatrixf> spMatrixChild;
	CRpExamplesPlugin::CreateDataObject(&spMatrixChild);
	spMatrixChild->LoadIdentity();
	spTransformBase->SetValue(CON_OBJ_TRANSFORM_INP_MATRIX, CComVariant(spMatrixChild));

	//export output matrix connector
	CComPtr<IRsConnector> spTransformOutCon;
	spTransformBase->FindConById(CON_TRANSFORM_MATRIX, &spTransformOutCon);
	spEncapsSet->ExportConnector(spTransformOutCon, true, false, false, NULL);

	//export parent matrix connector
	CComPtr<IRsConnector> spParentCon;
	spTransformBase->FindConById(CON_TRANSFORM_PARENT_MATRIX, &spParentCon);
	spEncapsSet->ExportConnector(spParentCon, true, true, false, NULL);

	//export local space matrix connector
	CComPtr<IRsConnector> spLocalCon;
	spTransformBase->FindConById(CON_OBJ_TRANSFORM_INP_MATRIX, &spLocalCon);
	spEncapsSet->ExportConnector(spLocalCon, true, true, false, NULL);

	//export phys attr connector
	CComPtr<IRsConnector> spPhysCon;
	spPhysBase->FindConById(CON_PHYSICS_ACTIVEATTR, &spPhysCon);
	spEncapsSet->ExportConnector(spPhysCon, true, true, false, NULL);

	if (spOurObjBase)
		spOurObjBase.CopyTo(ppNode);
	

	return S_OK;
}

