// CRnWidgetExample
#include "stdafx.h"
#include "RpExamplesPlugin.h"
#include "RnWidgetExample.h"

// {C131C6AB-1F55-4f6d-9B43-A928E08380D2}
RS_DEFINE_GUID(CON_WE_CONTROL_IN, 0xc131c6ab, 0x1f55, 0x4f6d, 0x9b, 0x43, 0xa9, 0x28, 0xe0, 0x83, 0x80, 0xd2);

// {9B017535-80FE-4dbf-B002-E08B93C05134}
RS_DEFINE_GUID(GUID_EXAMPLE_HIGHLIGHT, 0x9b017535, 0x80fe, 0x4dbf, 0xb0, 0x2, 0xe0, 0x8b, 0x93, 0xc0, 0x51, 0x34);


CRnWidgetExample::CRnWidgetExample()
{
	m_last_position_x = -1;
	m_last_position_y = -1;
};


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

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

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

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

STDMETHODIMP CRnWidgetExample::OnLoadDefault(void)
{
	//create node connectors
	HRESULT hr;
    CComQIPtr<IRsBaseNode> spBase = m_spUnkAgg;   // Get the BaseNode interface

	//control in connector serves for connecting to widget gesture controller
	RtConDesc sCtrlIn		= {&CON_WE_CONTROL_IN,  L"ConntrolIn",	L"Control in", VT_UI4,  NULL, CONFLG_IN | CONFLG_CONTROLFLOW};

	if (FAILED(hr = spBase->AddNewConnector(&sCtrlIn, NULL))) return hr;
	return S_OK;
}

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

HRESULT CRnWidgetExample::FinalConstruct()
{
    CComPtr<IRpKernel> spKernelPackage;
	
    HRESULT hr = CRpExamplesPlugin::GetKernelPackage(&spKernelPackage);
    if (FAILED(hr)) return hr;

	if (FAILED(hr = spKernelPackage->CreateBaseNode(GetControllingUnknown(), &m_spUnkAgg))) return hr;
	return hr;
}

//widget activation
HRESULT CRnWidgetExample::Activate()
{
	CComPtr<IRpKernel> spKernelPck;
	CRpExamplesPlugin::GetKernelPackage(&spKernelPck);

	//get sys core 
	CComPtr<IRsSysCore> spSysCore;
	spKernelPck->GetSysCore(&spSysCore);

	//get tool manager from sys core
	CComPtr<IRsSysToolManager> spSysToolMng;
	spSysCore->GetToolMng(&spSysToolMng);

	//register widget to tool manager so we get mouse messages
	spSysToolMng->RegisterToolManagerListener(this);
	return S_OK;
};

//widget deactivation
HRESULT CRnWidgetExample::Deactivate()
{
	//if object has been picked, remove it's highlight
	if (m_spLastPickedObj) {
		RemoveHighLightPreview(m_spLastPickedObj);
		m_spLastPickedObj = NULL;
	};

	//unregister from tool manager 
	CComPtr<IRpKernel> spKernelPck;
	CRpExamplesPlugin::GetKernelPackage(&spKernelPck);

	CComPtr<IRsSysCore> spSysCore;
	spKernelPck->GetSysCore(&spSysCore);
	CComPtr<IRsSysToolManager> spSysToolMng;
	spSysCore->GetToolMng(&spSysToolMng);

	spSysToolMng->UnregisterToolManagerListener(this);

	return S_OK;
};


STDMETHODIMP CRnWidgetExample::SetActiveWidget(IRsBaseNode *pWidget) 
{
	//if widget is activated pWidget will contain pointer to it's node. We use this pointer to distinguish between activation 
	//or deactivation of our tool

	if (pWidget) {
		Activate();
	}
	else {
		Deactivate();
	}
	return S_OK;
};


//handle tool manager message
STDMETHODIMP CRnWidgetExample::ProcessToolMngMessage(RtToolMsgId msgId,VARIANT *vParam)
{
	//tool manager message received and prepared for processing
	if (msgId == TMSGID_MSG_FETCHED) {
		RtToolEvent *m = (RtToolEvent*)(vParam->pvRecord);

		if (m->pViewNode) {
			//check last mouse position
			if (m->tMouseEvent.xPos != m_last_position_x || m->tMouseEvent.yPos != m_last_position_y) {
				m_last_position_x = m->tMouseEvent.xPos;
				m_last_position_y = m->tMouseEvent.yPos;


				//check if widgets have focus. Widget has focus iff widgets system tool has focus and there is tool (widget tool) active
				RtBool f;
				CComPtr<IRpWidgets> spWidgets;
				CRpExamplesPlugin::FindPackage(DEF_GUIDNAME_PTR(CRpWidgets), &spWidgets);
				CComPtr<IRsWidgetToolController > spToolCtrl;
				spWidgets->GetWidgetToolController(&spToolCtrl);
	
				//check focus of widget system tool
				spToolCtrl->GetFocus(&f);
				if (f) {
					//check tool manager if there is any active tool
					CComPtr<IRsSysToolManager> spSysToolMng;
					CRpExamplesPlugin::GetSysCore()->GetToolMng(&spSysToolMng);
					CComPtr<IRsTool> spTool;
					spSysToolMng->GetActiveTool(&spTool);
					if (!spTool) {
						//no tool in active state, we can show highlight
						return ProcessMouseMove(&m->tMouseEvent,m->pViewNode);
					};
				};

				//we lost focus, remove highlight
				if (m_spLastPickedObj) {
					RemoveHighLightPreview(m_spLastPickedObj);
					m_spLastPickedObj = NULL;
				};
			};
		};
	};
	return S_OK;
}

STDMETHODIMP CRnWidgetExample::WidgetToolActivate(IRcUndoRedoGroup *pUndoRedo)
{
	//widget action activated, prepare undo/redo helpers
	return S_OK;
};

STDMETHODIMP CRnWidgetExample::WidgetToolSetup(RtWidgetToolEvent *pEvent)
{
	//widget action activated, initialize processing

    CComQIPtr<IRsBaseNode> spBase = m_spUnkAgg;   // Get the BaseNode interface

	CComPtr<IRpWidgets> spWidgetsPackage;
	CRpExamplesPlugin::FindPackage(DEF_GUIDNAME_PTR(CRpWidgets), &spWidgetsPackage);
	if (!spWidgetsPackage)
		return E_FAIL;


	//get pick override manager interface from widgets package and use it for object picking. 
	//we can use direct object picking but pick override uses selection translation in case we 
	//picked combined objects like skeleton where one mesh contains meshes of many internal objects
	//like joints or bones
	CComPtr<IRsPickOverrideManager> spPicker;
	spWidgetsPackage->GetPickOverrideManager(&spPicker);

	CComPtr<IRsNode> spNode;
	RtPoint2f screenPoint; 
	screenPoint.x = (RtFloat) pEvent->m_MouseEvent.xPos;
	screenPoint.y = (RtFloat) pEvent->m_MouseEvent.yPos;
	RtPickOverrideResult por;
	spPicker->PickSubObject(pEvent->m_pViewNode, &screenPoint, &spNode, &por);
	if (!spNode) return S_OK;

	

	//check if picked object does not implement IRsCustomPickHandling - in this case we need
	//to translate selection using it's PickObject method. 
	CComQIPtr<IRsCustomPickHandling> spCph = spNode;
	CComPtr<IRsNode> spCustomPick;
	if (spCph) {
		if (spCph->PickObject(pEvent->m_pViewNode, (RtPoint2f*)&screenPoint, &spCustomPick) == S_OK) {
			spNode = spCustomPick;
		};
	}

	if (!spNode)
		return S_OK;


	//format pick message
	CComBSTR szObjPath;
	spNode->GetFullName(NULL, &szObjPath);

	CStringW msg;
	msg.Format(L"You have picked %s", szObjPath);
	MessageBox(NULL, msg.GetBuffer(), L"Widget example", MB_OK);
	return S_OK;
};


STDMETHODIMP CRnWidgetExample::WidgetToolUpdate(RtWidgetToolEvent *pEvent)
{
	//user continues the action
	return S_OK;
};

STDMETHODIMP CRnWidgetExample::WidgetToolDeactivate()
{
	//widget sequence finished
	return S_OK;
};


HRESULT CRnWidgetExample::ProcessMouseMove(RtMouseEvent *pEvent,IRsNode *pViewNode)
{
	//called when tool manager event is fetched into tool manager (mouse move, button pressed etc)

	//in our example we pick object below cursor and show red wireframe highlight around it
	CComPtr<IRsNode> spNode;
	CComQIPtr<IRsBaseNode> spBase = m_spUnkAgg;   // Get the BaseNode interface

	CComPtr<IRpWidgets> spWidgetsPackage;
	CRpExamplesPlugin::FindPackage(DEF_GUIDNAME_PTR(CRpWidgets), &spWidgetsPackage);
	if (!spWidgetsPackage)
		return E_FAIL;


	//pick object
	CComPtr<IRsPickOverrideManager> spPicker;
	spWidgetsPackage->GetPickOverrideManager(&spPicker);

	RtPoint2f screenPoint; 
	screenPoint.x = (RtFloat) pEvent->xPos;
	screenPoint.y = (RtFloat) pEvent->yPos;
	
	RtPickOverrideResult por;
	spPicker->PickSubObject(pViewNode, &screenPoint, &spNode, &por);

	if (spNode) {
		//handle objects translation
		CComQIPtr<IRsCustomPickHandling> spCph = spNode;
		CComPtr<IRsNode> spCustomPick;
		if (spCph) {
			if (spCph->PickObject(pViewNode, (RtPoint2f*)&screenPoint, &spCustomPick) == S_OK) {
				spNode = spCustomPick;
			};
		};
	};
		
	//nothing has changed, do not change state
	if (m_spLastPickedObj == spNode) {
		return S_OK;
	};

	//change highlight to new object
	if (m_spLastPickedObj)
		RemoveHighLightPreview(m_spLastPickedObj);

	m_spLastPickedObj = spNode;
	if (m_spLastPickedObj) {
		SetHighLightPreview(m_spLastPickedObj);
	};

	return S_OK;
}



HRESULT CRnWidgetExample::SetHighLightPreview(IRsNode *pNode)
{
	//render attributes override mechanism is used to show red wireframe highlight around object

	//management is present in view3d, so locate it's package
	CComPtr<IRpView3D> spView3D;
	CRpExamplesPlugin::FindPackage(DEF_GUIDNAME_PTR(CRpView3D),&spView3D);
	if (!spView3D) 
		return E_FAIL;

	//get render attributes override manager
	CComPtr<IRsRenderAttributesOverride> spSet;
	spView3D->GetRenderAttributesOverrideManager(&spSet);

	//create render attributes data object with required elements 
	CComPtr<IRdRenderAttributes> spAttribs;
	CRpExamplesPlugin::CreateDataObject(&spAttribs);
	//set wire color to red, 20% opacity
	spAttribs->SetWireColor(TRUE,1.0f,0,0);
	spAttribs->SetWireOpacity(0.2f);
	
	//insert render attributes override to our object. Set flags according to spAttribs data object and 
	//update wire color and wire opacity
	spSet->InsertRenderingAttributesOverride(
		GUID_EXAMPLE_HIGHLIGHT,
		D3DERA_SET_EXTRA_BY_FLAGS,
		D3DRACF_WIRECOLOR | D3DRACF_WIREOPACITY,
		0,
		pNode,
		spAttribs);

	return S_OK;
};

HRESULT CRnWidgetExample::RemoveHighLightPreview(IRsNode *pNode)
{
	//clear our render attributes override

	//locate view3d package
	CComPtr<IRpView3D> spView3D;
	CRpExamplesPlugin::FindPackage(DEF_GUIDNAME_PTR(CRpView3D),&spView3D);
	if (!spView3D) 
		return E_FAIL;

	//get render attributes override fn set
	CComPtr<IRsRenderAttributesOverride> spSet;
	spView3D->GetRenderAttributesOverrideManager(&spSet);

	//remove attributes override from our node. 
	spSet->RemoveRenderingAttributesOverride(GUID_EXAMPLE_HIGHLIGHT,pNode);

	return S_OK;
};


