#include "stdafx.h"
#include "RnConnectorExample.h"
#include "RpExamplesPlugin.h"

HRESULT CRnConnectorExample::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 CRnConnectorExample::OnGetValue(CONID con_id, IRsDataCache* pDataCache, VARIANT* pVtData, RtBool* pHandled)
{
    // calculate values of output connectors here
    if (con_id==CON_CONEX_RESULT)
    {
        // calculate sum of two operands

        // retrieve values of operand connectors
        CComVariant vOperand1;
        CComQIPtr<IRsNode> spNode = m_spUnkAgg;
        spNode->GetValue(CON_CONEX_REGISTERED_CONNECTOR, &vOperand1);
        if (vOperand1.ChangeType(VT_R4)!=S_OK){
            return E_FAIL;
        }
        RtFloat fOperand1 = vOperand1.fltVal;

        CComVariant vOperand2;
        spNode->GetValue(CON_CONEX_LOCAL_CONNECTOR, &vOperand2);
        if (vOperand2.ChangeType(VT_R4)!=S_OK){
            return E_FAIL;
        }
        RtFloat fOperand2 = vOperand2.fltVal;

        // calculate sum
        RtFloat fResult = fOperand1 + fOperand2;

        // put result value into output connector
        CComPtr<IRsDataCacheItem> spDi;
        pDataCache->GetDataCacheItem(con_id, &spDi);
        if (!spDi){
            // always check presence of data item, as connector may have been removed
            return E_FAIL;
        }
        CComVariant vResult(fResult);

        // finally, put the data into data item and validate connector
        spDi->PutData(&vResult);
        spDi->Validate();
    }
    else if (con_id==CON_CONEX_INPUT_BITMAP)
    {
        // get the input bitmap, convert it to grayscale and put it into output connector
        CComQIPtr<IRsNode> spNode = m_spUnkAgg;

        // retrieve input bitmap
        CComVariant vBitmap;
        spNode->GetValue(CON_CONEX_INPUT_BITMAP, &vBitmap);
        if (vBitmap.ChangeType(VT_UNKNOWN)!=S_OK){
            return E_FAIL;
        }

        CComQIPtr<IRdBitmap> spBitmap = vBitmap.punkVal;
        if (!spBitmap){
            // always check if NULL IUnknown is not present in connector (converted from VT_EMPTY)
            return E_FAIL;
        }

        // clone the bitmap object
        CComQIPtr<IRsCloneable> spCloneable = spBitmap;
        CComPtr<IRsObject> spClone;
        spCloneable->Clone(&spClone);
        CComQIPtr<IRdBitmap> spOutBitmap = spClone;
        if (!spClone){
            return E_FAIL;
        }

        // convert bitmap copy to grayscale
        spOutBitmap->ConvertFormat(BFMT_R8);

        // put it into output connector
        CComPtr<IRsDataCacheItem> spDi;
        pDataCache->GetDataCacheItem(con_id, &spDi);
        if (!spDi){
            return E_FAIL;
        }
        CComVariant v(spOutBitmap);
        spDi->PutData(&v);
        spDi->Validate();

    }
    return S_OK;
}

STDMETHODIMP CRnConnectorExample::OnSetValue(CONID con_id, IRsDataCache* pDataCache, VARIANT* pvtData, RtBool* pHandled)
{
    // additional handling when particular connector has been set can be put here
    // don't do any time-consuming operations here, but rather defer them to OnGetValue handler
    return S_OK;
}

STDMETHODIMP CRnConnectorExample::OnDefaultValue(CONID con_id, VARIANT* pVtData, RtBool* pHandled)
{
    // determines default (initial) values connectors have when node created or when connector reset
    // it is enough to provide default values for input connectors
    if (con_id==CON_CONEX_LOCAL_CONNECTOR){
        // first operand
        CComVariant v(2.0f);
        *pVtData = v;
        *pHandled = true;
    }
    else if (con_id==CON_CONEX_REGISTERED_CONNECTOR){
        // second operand
        CComVariant v(3.0f);
        *pVtData = v;
        *pHandled = true;
    }
    else if (con_id==CON_CONEX_INPUT_BITMAP){
        CComPtr<IRdBitmap> spBitmap;
        CRpExamplesPlugin::CreateDataObject(&spBitmap);
        if (!spBitmap){
            _ASSERT(0); //Please check if CommonData package is installed;
            return E_FAIL;
        }
        CComVariant v(spBitmap);
        v.Detach(pVtData);
        *pHandled = true;
    }
    return S_OK;
}

STDMETHODIMP CRnConnectorExample::OnInvalidate(CONID con_id, RtBool* pHandled)
{
    // additional handling when particular connector has been invalidated can be put here
    // don't do any time-consuming operations here, but rather defer them to OnGetValue handler
    return S_OK;
}

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

STDMETHODIMP CRnConnectorExample::OnLoadDefault(void)
{
    // here connectors will be added to node
    CComQIPtr<IRsBaseNode> spBase = m_spUnkAgg;

    HRESULT hr;

    // we will define connector descriptors here (Operand 1 connector is defined among registered connectors (see RsNodeFactory.cpp)
    // Operand 2: input connector holding VT_R4 (float) type. Notice NULL as 5th parameter: this specifies that threre is no custom 
    // data type 
    RtConDesc sOperand2 = {&CON_CONEX_LOCAL_CONNECTOR, L"Operand 1", L"Operand 1", VT_R4, NULL, CONFLG_IN};
    // Result: output connector holding float value
    RtConDesc sResult = {&CON_CONEX_RESULT, L"Operand 2", L"Operand 2", VT_R4, NULL, CONFLG_OUT};
    // Input bitmap connector holding VT_UNKNOWN (IUnknown) as value. Required type is IRdBitmap
    RtConDesc sInputBitmap = {&CON_CONEX_INPUT_BITMAP, L"Input bitmap", L"Input bitmap", VT_UNKNOWN, DEF_GUIDNAME_PTR(IRdBitmap), CONFLG_IN};
    // Output bitmap connector
    RtConDesc sOutputBitmap = {&CON_CONEX_BITMAP, L"Bitmap", L"Bitmap", VT_UNKNOWN, DEF_GUIDNAME_PTR(IRdBitmap), CONFLG_OUT};

    // now when connectors are defined, they can be added into node
    CComPtr<IRsConnector> spOperand1, spOperand2, spResult, spInputBitmap, spOutputBitmap;


    // this is how registered connector is added (see RsNodeFactory.cpp for declaration of connector descriptor)
    if (FAILED(hr = spBase->AddRegisteredConnector(CON_CONEX_REGISTERED_CONNECTOR, &spOperand1)))
        return hr;
    // other (local connectors)
    if (FAILED(hr = spBase->AddNewConnector(&sOperand2, &spOperand2)))
        return hr;
    if (FAILED(hr = spBase->AddNewConnector(&sResult, &spResult)))
        return hr;
    if (FAILED(hr = spBase->AddNewConnector(&sInputBitmap, &spInputBitmap)))
        return hr;
    if (FAILED(hr = spBase->AddNewConnector(&sOutputBitmap, &spOutputBitmap)))
        return hr;

    // dependencies between connectors: dependency makes output (right) connector invalidate always when 
    // input (left) connector has been invalidated. In this particular case, result connector depends on 
    // Operand 1 and Operand 2 connectors. Output bitmap depends on input bitmap
    spOperand1->AddDependency(spResult);
    spOperand2->AddDependency(spResult);
    spInputBitmap->AddDependency(spOutputBitmap);

    return S_OK;
}

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