// RcRcTopologyExample.cpp : Implementation of CRcRcTopologyExample

#include "stdafx.h"
#include "RcTopologyExample.h"
#include "RpExamplesPlugin.h"


// CRcTopologyExample

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

STDMETHODIMP CRcTopologyExample::OnSetValue(CONID con_id, IRsDataCache* pDataCache, VARIANT* pvtData, RtBool* pHandled)
{
	return S_OK;
}
STDMETHODIMP CRcTopologyExample::OnDefaultValue(CONID con_id, VARIANT* pVtData, RtBool* pHandled)
{
	return S_OK;
}

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

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

STDMETHODIMP CRcTopologyExample::OnLoadDefault(void)
{
	return S_OK;
}

STDMETHODIMP CRcTopologyExample::OnDestroy(void)
{
	return S_OK;
}

HRESULT CRcTopologyExample::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 CRcTopologyExample::Execute(void)
{
	HRESULT hRes;
	
	CComPtr<IRpKernel> spKernel;
	CRpExamplesPlugin::GetKernelPackage(&spKernel);
	
	//assemble new object


	CComPtr<IRnEncapsulator>	spNewObjEncaps;

	//create empty encapsulator
	if(S_OK == (hRes = CreateEncapsulatorNode(&spNewObjEncaps)))
	{
		// add sphere node to newly created encapsulator
		if(FAILED(hRes = AddSphereNode(spNewObjEncaps)))
			return hRes;

		// add transform node to newly created encapsulator
		if(FAILED(hRes = AddTransformNode(spNewObjEncaps)))
			return hRes;
	}

	/*
	//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> spOurObjEncpasBase = spOurObjEncaps;
	spOurObjEncpasBase->put_Name(L"Topology Example Node");

	

	//create sphere node
	CComPtr<IRnSphere2> spSphere2;
	CRpExamplesPlugin::CreateNode(&spSphere2);

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

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

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

	
	//find radius connector by name and export it using encapsulator fn set
	CComPtr<IRsConnector> spRadiusCon2;
	spSphereBase->FindConByName(L"Radius", &spRadiusCon2);
	spEncapsSet->ExportConnector(spRadiusCon2, true, false, false, NULL);

	//find mesh connector by ID 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);
	
	//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);

*/

	//now add our assembled object into scene with recorder enabled.
	
	
	//create event recorder for handling undo/redo. Note that m_spLogSession is NULL as execute is called only once.
	spKernel->CreateEventLogSession(&m_spLogSession);
	if (!m_spLogSession) {
		//some error has occured and recorder failed to create.
		return E_FAIL;
	};
	
	//initialize event recording on whole dependency graph. From now on, each change to dependency graph will be recorded into m_spLogSession.
	//it it strongly advised to record only subgraph you are modifying for performance reasons. We are recording only events
	//that occur in current scene as we are modifying it.
	CComQIPtr<IRnEncapsulator> spSceneEncap;
	if(FAILED(hRes = GetCurrentScene(&spSceneEncap)))
		return hRes;

	CComQIPtr<IRsNode>	spSceneNode = spSceneEncap;
	if(SUCCEEDED(m_spLogSession->StartLog(spSceneNode)))
	{
		//add our new object into scene.
		CComQIPtr<IRsNode> spNewObj = spNewObjEncaps;
		spSceneEncap->AddSubObject(spNewObj);

		//stop event recording. 
		m_spLogSession->FinishLog();
	}
	return S_OK;
}

STDMETHODIMP CRcTopologyExample::Undo(void)
{
	//apply previous states to dependency graph
	if (m_spLogSession)
		m_spLogSession->Undo();
	return S_OK;
}

STDMETHODIMP CRcTopologyExample::Redo(void)
{
	//apply new states to dependency graph
	if (m_spLogSession)
		m_spLogSession->Redo();
	return S_OK;
}

STDMETHODIMP CRcTopologyExample::HasUndo(RtBool* isUndoable)
{
	//Command IS undoable.
	*isUndoable = true;
	return S_OK;
}

// create new encpapsulator
HRESULT CRcTopologyExample::CreateEncapsulatorNode(IRnEncapsulator** ppNewEncapsNode)
{
	HRESULT hRes;

	//create new encapsulator which will contain sphere shape node and matrix node.
	CComPtr<IRnEncapsulator> spNewObjEncaps;
	if(FAILED(hRes = CRpExamplesPlugin::CreateNode(&spNewObjEncaps)))
		return hRes;

	//query IRsBaseNode interface so we can manipulate object name and connectors
	CComQIPtr<IRsBaseNode> spOurObjEncpasBase = spNewObjEncaps;
	spOurObjEncpasBase->put_Name(L"Topology Example Node");

	return spNewObjEncaps.CopyTo(ppNewEncapsNode);
}


// add sphere node to newly created encapsulator
HRESULT CRcTopologyExample::AddSphereNode(IRnEncapsulator* pEncapsNode)
{
	HRESULT hRes;

	//create sphere node
	CComPtr<IRnSphere2> spSphere2;
	if(FAILED(hRes = CRpExamplesPlugin::CreateNode(&spSphere2)))
		return hRes;
	
	//add sphere into new encapsulator 
	CComQIPtr<IRsBaseNode> spSphereBase = spSphere2;
	pEncapsNode->AddSubObject(spSphereBase);

	//export connectors

	CComPtr<IRpKernel> spKernel;
	CRpExamplesPlugin::GetKernelPackage(&spKernel);
	//create encapsulator fn set which contains connector exporting functions
	CComPtr<IRfEncapsulatorSet> spEncapsSet;
	spKernel->CreateEncapsulatorFnSet(&spEncapsSet);

	CComQIPtr<IRsBaseNode>	spObjEncpasBase = pEncapsNode;
	spEncapsSet->Attach(spObjEncpasBase);
	
	//find local radius connector by name and export it using encapsulator fn set
	CComPtr<IRsConnector> spRadiusCon2;
	spSphereBase->FindConByName(L"Radius", &spRadiusCon2);
	if(FAILED(hRes = spEncapsSet->ExportConnector(spRadiusCon2, true, true, false, NULL)))
		return hRes;

	//find registered mesh connector by ID and export it using encapsulator fn set
	CComPtr<IRsConnector> spMeshCon;
	spSphereBase->FindConById(CON_MESH, &spMeshCon);
	if(FAILED(hRes = spEncapsSet->ExportConnector(spMeshCon, true, false, false, NULL)))
		return hRes;

	return S_OK;

}


// add transform node to newly created encpasualtor
HRESULT CRcTopologyExample::AddTransformNode(IRnEncapsulator* pEncapsNode)
{
	HRESULT hRes;

	//create transform node
	CComPtr<IRnTransform> spTransform;
	if(FAILED(hRes = CRpExamplesPlugin::CreateNode(&spTransform)))
		return hRes;
	

	//add transform node into our object. 
	CComQIPtr<IRsBaseNode> spTransformBase = spTransform;
	pEncapsNode->AddSubObject(spTransformBase);
	
	//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 connectors


	CComPtr<IRpKernel> spKernel;
	CRpExamplesPlugin::GetKernelPackage(&spKernel);
	//create encapsulator fn set which contains connector exporting functions
	CComPtr<IRfEncapsulatorSet> spEncapsSet;
	spKernel->CreateEncapsulatorFnSet(&spEncapsSet);

	CComQIPtr<IRsBaseNode>	spObjEncpasBase = pEncapsNode;
	spEncapsSet->Attach(spObjEncpasBase);
	
	
	//export output matrix connector
	CComPtr<IRsConnector> spTransformOutCon;
	spTransformBase->FindConById(CON_TRANSFORM_MATRIX, &spTransformOutCon);
	if(FAILED(hRes = spEncapsSet->ExportConnector(spTransformOutCon, true, false, false, NULL)))
		return hRes;

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

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

	return S_OK;

}

HRESULT CRcTopologyExample::GetCurrentScene(IRnEncapsulator** ppCurrSceneEncaps)
{
	CComPtr<IRpKernel> spKernel;
	CRpExamplesPlugin::GetKernelPackage(&spKernel);

	//Find Space3D package which handles 3D scene.
	CComPtr<IRsUnknownPackage> spUnkPck;
	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<IRnEncapsulator> spSceneEncapsulator = spCurrentScene;
	if (spSceneEncapsulator != NULL) 
		return spSceneEncapsulator.CopyTo(ppCurrSceneEncaps);

	//no 3d scene found.
	return E_FAIL;
}