// RaClip.cpp : Implementation of CRaClip

#include "stdafx.h"
#include "RpAnimPluginPackage.h"
#include "RaClip.h"
#include "RsAnimAttr.h"
#include <math.h>


// CRaClip

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

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

    if(con_id == CON_KFANIMDATA)
	{
		CComPtr<IRsSysFactory>	spSysFactory;
		CRpAnimPlugin::GetSysFactory(&spSysFactory);

		CComPtr<IRsDataObject> spDo;
		if(FAILED(hr = spSysFactory->CreateDataObj(DEF_GUIDNAME_PTR(IRdAnimData),&spDo)))
			return hr;

		CComVariant v(spDo);
		v.Detach(pVtData);
		*pHandled = true;
	}
	else if (con_id == CON_ANIMTIME)
	{
		CComVariant defValue(0.0);
		hr = defValue.Detach(pVtData);
		*pHandled = TRUE;
	}
	else if(con_id == CON_USELOCALTIME)
	{
		CComVariant defVal(RtBool(true));
		defVal.ChangeType(VT_BOOL);
		hr = defVal.Detach(pVtData);
		*pHandled = TRUE;
	}

    return hr;
}

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

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

STDMETHODIMP CRaClip::OnLoadDefault(void)
{
	HRESULT hr;
    CComQIPtr<IRsBaseNode> spBase = m_spUnkAgg;   // Get the BaseNode interface
    ATLASSERT(spBase);
    
  	// add keyframe anim data connector:
	RtConDesc   sKfAnimObj =  { &CON_KFANIMDATA,	L"KfAnimData",	L"Keframe Animation data",	VT_UNKNOWN, &DEF_GUIDNAME(IRdAnimData) , CONFLG_IN};   
	if (FAILED(hr = spBase->AddNewConnector(&sKfAnimObj, NULL)))
        return hr;

	// local time flag
	RtConDesc   slocalTime =  { &CON_USELOCALTIME,	L"Local Time",	L"Use Local Time",	VT_BOOL, NULL , CONFLG_IN};   
	if (FAILED(hr = spBase->AddNewConnector(&slocalTime, NULL)))
        return hr;

	// add registered time connector:
	if(FAILED(hr = spBase->AddRegisteredConnector(CON_ANIMTIME, NULL)))
		return hr;

	// add default transition connectors
	CComPtr<IRpKernel> spKernelPackage;
	if(FAILED(hr = CRpAnimPlugin::GetKernelPackage(&spKernelPackage)))
		return hr;

    CComPtr<IRsUnknownPackage> spPackage;
    hr = spKernelPackage->FindPackage(DEF_GUIDNAME(CRpGraph), &spPackage);
    if (FAILED(hr))
        return hr;
	CComQIPtr<IRpGraph>	spGraphPackage = spPackage;
	if(spGraphPackage)
	{
		CComPtr<IRsActivityHelper>	spActvHelper;
		spGraphPackage->GetActivityHelper(&spActvHelper);
		if(spActvHelper)
			spActvHelper->AddTransitionCon(this);
	}
    
    return S_OK;
}

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

// create aggregated Space3dObject (Space3dObject agregates BaseNode)
HRESULT CRaClip::FinalConstruct()
{
    CComPtr<IRpKernel> spKernelPackage;
    HRESULT hr = CRpAnimPlugin::GetKernelPackage(&spKernelPackage);
    if (FAILED(hr))
        return hr;
    
    return spKernelPackage->CreateBaseNode(GetControllingUnknown(), &m_spUnkAgg);
}


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

	HRESULT hRes;

	// get keyframe data from connector
	CComPtr<IRdAnimData>	spAnimData;
	if(FAILED(hRes = GetAnimData(&spAnimData)))
		return hRes;

	// get time from the connector
	RtTime tTime;
	if(FAILED(hRes = GetAnimTime(&tTime)))
		return hRes;

	// get 'use local time' option
	CComVariant vLocTimeValue;
	if(FAILED(hRes = spBase->GetValue(CON_USELOCALTIME, &vLocTimeValue)))
		return hRes;
	RtBool bUseLocalTime = static_cast<RtBool>(V_BOOL(&vLocTimeValue));

	
	// convert anim time to local time of the clip
	if(bUseLocalTime)
	{
		RtTime tMinTime, tMaxTime;
		if(S_OK == spAnimData->GetMinMaxKeyFrame(&tMinTime, &tMaxTime))
		{
			if(tMaxTime - tMinTime > 0.01f)
			{
				RtTime fKoef = fmodf(tTime-tMinTime,tMaxTime - tMinTime);
				if(fKoef >=0.f)
					tTime = tMinTime + fKoef;
				else
					tTime = tMaxTime + fKoef;
			}
			else
			{
				tTime = tMinTime;
			}
		}
	}


	// get anim space, keyframed attributes are resolved relative to this encapsulator
	// in our case we apply value relative to the owner of this activity clip
	CComPtr<IRsNode>	spAnimSpace;
	spBase->GetOwner(&spAnimSpace);
	if(spAnimSpace == NULL)
		return E_FAIL;

	
	// apply kf data 
	CComPtr<IRsEnumAttrRec>	spEnumAttrRecords;
	spAnimData->EnumAttrRecords(&spEnumAttrRecords);
	if(spEnumAttrRecords)
	{
		spEnumAttrRecords->Reset();

		CComPtr<IRsAnimAttrRecord>	spAnimAttrRecord;
		while (S_OK == spEnumAttrRecords->Next(1, &spAnimAttrRecord, NULL))
		{
			ApplyKfData(spAnimSpace, spAnimAttrRecord, tTime);
			spAnimAttrRecord.Release();
		}
	}

    
    return hRes;
}

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

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

STDMETHODIMP CRaClip::HasUndo(RtBool* isUndoable)
{
    *isUndoable = static_cast<RtBool>(false);
    return S_OK;
}

HRESULT CRaClip::GetAnimData(IRdAnimData** ppAnimData)
{
	HRESULT hRes;
	CComQIPtr<IRsBaseNode> spBase = m_spUnkAgg; 

	CComVariant vData;
	if(FAILED(hRes = spBase->GetValue(CON_KFANIMDATA, &vData)))
	{
		ATLASSERT(!"Failed to obtain anim data");
		return hRes;
	}
	
	CComQIPtr<IRdAnimData> spAnimData = V_UNKNOWN(&vData);
	return spAnimData.CopyTo(ppAnimData);
}

HRESULT CRaClip::GetAnimTime(RtTime* ptTime)
{
	HRESULT hRes;
	CComQIPtr<IRsBaseNode> spBase = m_spUnkAgg;

	CComVariant vTimeValue;
	if(FAILED(hRes = spBase->GetValue(CON_ANIMTIME, &vTimeValue)))
		return hRes;
	if(FAILED(vTimeValue.ChangeType(VT_R8)))
		return hRes;

	*ptTime = static_cast<RtTime>(V_R8(&vTimeValue));
	return S_OK;
}


HRESULT CRaClip::ApplyKfData(IRsNode* pAnimSpace, IRsAnimAttrRecord* pAnimAttrRec, RtTime tTime)
{
	HRESULT hRes;

	// get id of attribute record ( id is relative identification of attribute in animation space
	CComBSTR bszAttrId;
	pAnimAttrRec->GetAttrId(&bszAttrId);

	CRsAnimAttr attrObj(bszAttrId);

	// get original value from the graph
	CComVariant vData;
	if(FAILED(hRes = attrObj.GetOrigValue(pAnimSpace, &vData)))
		return hRes;

	// get interpolated value evaluated from keyframes at time specified by tTime parameter
	if(FAILED(hRes = pAnimAttrRec->GetValueAt(tTime, &vData)))
		return hRes;

	// set new value to the graph
	return attrObj.SetNewValue(&vData);
}

