// RcCreate3DLoader.cpp : Implementation of CRcCreate3DLoader
// Purpose of this command is to create the Rn3DLoader node object,
// put it into Dependency Graph and properly set its parameters.

#include "stdafx.h"
#include "RcCreate3DLoader.h"
#include "Rn3DLoader.h"
#include "shlobj.h"


// CRcCreate3DLoader

// OnGetValue - method is called when a connector's value is required. Due to fact this
// command will not be put into Dependency Graph (as activity node), we don't need to
// react on it. Similarly it is with OnSetValue, OnDefaultValue and OnInvalidate that
// treats the connectors' events of this command
STDMETHODIMP CRcCreate3DLoader::OnGetValue(CONID conID, IRsDataCache* pDataCache, VARIANT* pVtData, RtBool* pHandled)
{
	return S_OK;
}

// Called when a connector value was set
STDMETHODIMP CRcCreate3DLoader::OnSetValue(CONID con_id, IRsDataCache* pDataCache, VARIANT* pvtData, RtBool* pHandled)
{
	return S_OK;
}

// Called to initialize a connector's value
STDMETHODIMP CRcCreate3DLoader::OnDefaultValue(CONID con_id, VARIANT* pVtData, RtBool* pHandled)
{
	return S_OK;
}

// Called when a connector was invalidated
STDMETHODIMP CRcCreate3DLoader::OnInvalidate(CONID con_id, RtBool* pHandled)
{
	return S_OK;
}

// Called when this command was created
STDMETHODIMP CRcCreate3DLoader::OnInitialize(void)
{
	return S_OK;
}

// Called only once, when this command was created. Usually you
// should create connectors for the command here. This command
// is not using connectors, thus we do nothing here.
STDMETHODIMP CRcCreate3DLoader::OnLoadDefault(void)
{
	return S_OK;
}

// Called when the command is to be destroyed. You should
// release here all necessary stuff that cannot be released
// automatically.
STDMETHODIMP CRcCreate3DLoader::OnDestroy(void)
{
	return S_OK;
}

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

	return hRes;
}

// Function display directory browser
// Return S_OK if valid path was choose or S_FALSE if the browser was
// closed without selected directory.
HRESULT CRcCreate3DLoader::GetPathDir(BSTR *pPathDir)
{
    if (pPathDir == NULL)
        return E_INVALIDARG;

    LPITEMIDLIST pItemList = NULL;
    BROWSEINFO info;
    ZeroMemory(&info, sizeof(info));
    info.ulFlags = BIF_NEWDIALOGSTYLE|BIF_VALIDATE;
    CComBSTR bstrTitle;
    bstrTitle.LoadString(IDS_RCC3D_TITLE);
    info.lpszTitle = bstrTitle;

    pItemList = ::SHBrowseForFolder(&info);
    if (pItemList == NULL)
        return S_FALSE;

    CComPtr<IMalloc> shellMalloc;
    ::SHGetMalloc(&shellMalloc);
    if (shellMalloc == NULL)
        return E_FAIL;

    TCHAR tmpPath[MAX_PATH];
    BOOL bPath = SHGetPathFromIDList(pItemList, tmpPath);
    shellMalloc->Free(pItemList);
    if (!bPath)
        return S_FALSE;

    CComBSTR bstrPathDir;
    bstrPathDir = tmpPath;
    *pPathDir = bstrPathDir.Detach();

    return S_OK;
}

// This method is called when this command is executed
// Choose directory from a dialog here and create the 3DLoader node
STDMETHODIMP CRcCreate3DLoader::Execute(void)
{
    // Choose directory with 3D objects
    if (GetPathDir(&m_bstrPathDir) != S_OK)
        return S_FALSE;

    // Other actions will be the same as when Redo action is called
    // as we want the dialog display only when the command is entered.
	return Redo();
}

// When Undo action is required, find the node object we created
// and simply delete it.
STDMETHODIMP CRcCreate3DLoader::Undo(void)
{
    if (m_bstr3DLoaderID.Length() == 0)
	    return S_OK;

    // Find the 3DLoader node in graph
    CComPtr<IRpKernel> spKernelPckg;
    CRp3DLoader::GetKernelPackage(&spKernelPckg);
    if (spKernelPckg == NULL)
        return E_UNEXPECTED;

    CComQIPtr<IRnKernel> spKernel;
    spKernelPckg->GetKernelNode(&spKernel);
    CComQIPtr<IRnEncapsulator> spKernelEnc = spKernel;
    if (spKernelEnc == NULL)
        return S_FALSE;

    CComPtr<IRsNode> spNode;
    if (spKernelEnc->FindNodeByName(m_bstr3DLoaderID, &spNode) != S_OK)
        return S_FALSE;

    // If the node is found, delete it
    if (spNode != NULL)
        spNode->Destroy();

    m_bstr3DLoaderID.Empty();

    return S_OK;
}


// For re-do action we want create the node object and set its
// parameters, but we don't want asks user for the parameters.
// Instead we will use m_bstrPathDir that we set during the
// Execute call.
STDMETHODIMP CRcCreate3DLoader::Redo(void)
{
    HRESULT hr;

    // Create the 3D Loader node object and set its parameters
    CComPtr<IRsNode> sp3DLoaderNode;
    CRp3DLoader::CreateNode(DEF_GUIDNAME_PTR(IRn3DLoader), &sp3DLoaderNode);
    if (sp3DLoaderNode == NULL)
        return E_FAIL;

    // Add the loader into scene. To load it we need find Space3D package
    // and then asks for the current scene object
    CComPtr<IRpKernel> spKernelPackage;
    CRp3DLoader::GetKernelPackage(&spKernelPackage);
    if (spKernelPackage == NULL)
        return E_UNEXPECTED;

    CComPtr<IRsUnknownPackage> spSpace3DUnknPck;
    spKernelPackage->FindPackage(DEF_GUIDNAME(CRpSpace3DPck), &spSpace3DUnknPck);
    CComQIPtr<IRpSpace3DPck> spSpace3dPck = spSpace3DUnknPck;
    if (spSpace3dPck == NULL)
        return E_FAIL;          // Missing Space3D package!

    CComPtr<IRfSceneHelper> spRfSceneHelper;
    spSpace3dPck->CreateSceneHelper(&spRfSceneHelper);
    if (spRfSceneHelper == NULL)
        return E_FAIL;          // Failed to create scene helper

    CComPtr<IRnSpace3D> spSpace3D;
    spRfSceneHelper->GetCurrentScene(&spSpace3D);
    CComQIPtr<IRnEncapsulator> spSceneRoot = spSpace3D;
    if (spSceneRoot == NULL)
        return E_FAIL;          // Scene doesn't exist!

    // Finally add the node into Dependency Graph
    if (FAILED(hr = spSceneRoot->AddSubObject(sp3DLoaderNode)))
        return E_FAIL;

    // Remember the node's full string ID so it could be deleted if the undo will be called
    // It is better to remember string ID as the objects could be changed by another commands
    sp3DLoaderNode->GetFullName(NULL, &m_bstr3DLoaderID);

    // Set the directory path. This activate the object too
    if (FAILED(hr = sp3DLoaderNode->SetValue(CON_3DLOADER_NODE_DIR_PATH, CComVariant(m_bstrPathDir))))
        return hr;

    return S_OK;
}

STDMETHODIMP CRcCreate3DLoader::HasUndo(RtBool* isUndoable)
{
	// Command is undo-able.
	*isUndoable = true;

	return S_OK;
}


