// AnimNode.cpp: implementation of the CAnimNode class.
//
//////////////////////////////////////////////////////////////////////

// TODO : jkyle - need to implement SetValue and GetValue for the ParamNode derivatives

#include "stdafx.h"
#include "animscript.h"
#include "AnimNode.h"
#include "AnimScriptDoc.h"
#include "modifycurveparam.h"
#include "modifyfloat.h"
#include "modifypathdlg.h"
#include "modifyattribdlg.h"

#include <assert.h>
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

// This is the exact text used for save and load of data file key names, index matches enum in AnimNode.h
char g_szParamKeyName[][40] =
{
	"AnimType",
	"Start",
	"Position",
	"Anim",
	"StartOverlapCurve",
	"EndOverlapCurve",
	"TransitionStart",
	"PlayOldStateTill",
	"OverrideNewStatePosition",
	"StartNewState",
	"PitchAttrib",
	"RollAttrib",
	"SpeedAttrib",
	"FastAnim",
	"SlowAnim",
	"SlowSpeedMultiplier",
	"LerpTime",
	"SlowUpAnim",
	"SlowEvenAnim",
	"SlowDownAnim",
	"FastUpAnim",
	"FastEvenAnim",
	"FastDownAnim",
	"UpAnim",
	"EvenAnim",
	"DownAnim",
	"LeftAnim",
	"RightAnim",
	"SlowLeftAnim",
	"SlowRightAnim",
	"FastLeftAnim",
	"FastRightAnim",

	"InvalidAnim"

};

// This is a mapping of all animation types with the parameter type
int g_arrAnimTypeParams[][2] = 
{
	{MechWarrior4::AnimationStateEngine::PoseType,PARAM_POSITION},

	{MechWarrior4::AnimationStateEngine::CycleType,PARAM_POSITION},
	{MechWarrior4::AnimationStateEngine::CycleType,PARAM_ANIM},

	{MechWarrior4::AnimationStateEngine::SpeedCycleType,PARAM_POSITION},
	{MechWarrior4::AnimationStateEngine::SpeedCycleType,PARAM_ANIM},
	{MechWarrior4::AnimationStateEngine::SpeedCycleType,PARAM_SPEEDATTRIB},

	{MechWarrior4::AnimationStateEngine::SpeedBlenderCycleType,PARAM_POSITION},
	{MechWarrior4::AnimationStateEngine::SpeedBlenderCycleType,PARAM_SLOWANIM},
	{MechWarrior4::AnimationStateEngine::SpeedBlenderCycleType,PARAM_FASTANIM},
	{MechWarrior4::AnimationStateEngine::SpeedBlenderCycleType,PARAM_SPEEDATTRIB},
	{MechWarrior4::AnimationStateEngine::SpeedBlenderCycleType,PARAM_SLOWSPEEDMULTIPLIER},

	{MechWarrior4::AnimationStateEngine::LerpCycleType,PARAM_POSITION},
	{MechWarrior4::AnimationStateEngine::LerpCycleType,PARAM_LERPTIME},
	{MechWarrior4::AnimationStateEngine::LerpCycleType,PARAM_ANIM},

	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_POSITION},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_SLOWUPANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_SLOWEVENANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_SLOWDOWNANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_FASTUPANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_FASTEVENANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_FASTDOWNANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_PITCHATTRIB},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_ROLLATTRIB},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_SPEEDATTRIB},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_SLOWSPEEDMULTIPLIER},

	{MechWarrior4::AnimationStateEngine::FullHeightBlenderCycleType,PARAM_POSITION},
	{MechWarrior4::AnimationStateEngine::FullHeightBlenderCycleType,PARAM_UPANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightBlenderCycleType,PARAM_EVENANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightBlenderCycleType,PARAM_DOWNANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightBlenderCycleType,PARAM_ROLLATTRIB},
	{MechWarrior4::AnimationStateEngine::FullHeightBlenderCycleType,PARAM_PITCHATTRIB},
	{MechWarrior4::AnimationStateEngine::FullHeightBlenderCycleType,PARAM_SPEEDATTRIB},

	{MechWarrior4::AnimationStateEngine::FullHeightBlenderCycleType,PARAM_POSITION},
	{MechWarrior4::AnimationStateEngine::FullHeightBlenderCycleType,PARAM_UPANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightBlenderCycleType,PARAM_EVENANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightBlenderCycleType,PARAM_DOWNANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightBlenderCycleType,PARAM_LEFTANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightBlenderCycleType,PARAM_RIGHTANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightBlenderCycleType,PARAM_ROLLATTRIB},
	{MechWarrior4::AnimationStateEngine::FullHeightBlenderCycleType,PARAM_PITCHATTRIB},
	{MechWarrior4::AnimationStateEngine::FullHeightBlenderCycleType,PARAM_SPEEDATTRIB},

	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_POSITION},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_SLOWUPANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_SLOWEVENANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_SLOWDOWNANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_SLOWLEFTANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_SLOWRIGHTANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_FASTUPANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_FASTEVENANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_FASTDOWNANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_FASTLEFTANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_FASTRIGHTANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_ROLLATTRIB},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_PITCHATTRIB},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_SPEEDATTRIB},
	{MechWarrior4::AnimationStateEngine::FullHeightSpeedBlenderCycleType,PARAM_SLOWSPEEDMULTIPLIER},

	{MechWarrior4::AnimationStateEngine::FullHeightPoseHolderType,PARAM_POSITION},
	{MechWarrior4::AnimationStateEngine::FullHeightPoseHolderType,PARAM_UPANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightPoseHolderType,PARAM_EVENANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightPoseHolderType,PARAM_DOWNANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightPoseHolderType,PARAM_LEFTANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightPoseHolderType,PARAM_RIGHTANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightPoseHolderType,PARAM_ROLLATTRIB},
	{MechWarrior4::AnimationStateEngine::FullHeightPoseHolderType,PARAM_PITCHATTRIB},

	{MechWarrior4::AnimationStateEngine::FullHeightPoseHolderType,PARAM_POSITION},
	{MechWarrior4::AnimationStateEngine::FullHeightPoseHolderType,PARAM_EVENANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightPoseHolderType,PARAM_DOWNANIM},
	{MechWarrior4::AnimationStateEngine::FullHeightPoseHolderType,PARAM_ROLLATTRIB},
	{MechWarrior4::AnimationStateEngine::FullHeightPoseHolderType,PARAM_PITCHATTRIB}
};


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
//**************************//
// The base class for Animation States and Transition States
CAnimNode::CAnimNode(CString strName)
{
	m_bValid = false;
	m_strName = strName;
	m_hItem = NULL;
}

CAnimNode::~CAnimNode()
{
}

CParamNode *CAnimNode::GetParam(int iKey)
{
	CObject *pObj;
	if (m_mapParams.Lookup(iKey,pObj))
	{
		CParamNode *pNode =  (CParamNode *)pObj;
		return pNode;
	}
	return NULL;
}

// returns the value it replaced if at all
CParamNode *CAnimNode::SetParam(int iKey,CParamNode *pNode)
{
	assert(iKey>=0);
	assert(iKey<PARAM_COUNT);
	CObject *pObj = m_mapParams[iKey];
	CParamNode *pOldNode = NULL;
	if (pObj)
		pOldNode = (CParamNode *)pObj;
	m_mapParams[iKey] = pNode;
	return pOldNode;
}

bool CAnimNode::LoadState(Page *page)
{
	CAnimTypeNode *pType = (CAnimTypeNode *)GetParam(PARAM_ANIMTYPE);
	CreateAnimTypeParams(pType,page);
	return true;
}

CParamNode *CAnimNode::CreateParam(int iType,Page *page)
{
	CParamNode *pNode = NULL;

	switch (iType)
	{
	case PARAM_ANIMTYPE:
		return NULL;		// This type is not a created parameter
	case PARAM_START:
	case PARAM_POSITION:
	case PARAM_SLOWSPEEDMULTIPLIER:
	case PARAM_LERPTIME:
		{
			pNode = new CFloatNode(iType);
			break;
		}
	case PARAM_ANIM:
	case PARAM_FASTANIM:
	case PARAM_SLOWANIM:
	case PARAM_SLOWUPANIM:
	case PARAM_SLOWEVENANIM:
	case PARAM_SLOWDOWNANIM:
	case PARAM_FASTUPANIM:
	case PARAM_FASTEVENANIM:
	case PARAM_FASTDOWNANIM:
	case PARAM_UPANIM:
	case PARAM_EVENANIM:
	case PARAM_DOWNANIM:
	case PARAM_LEFTANIM:
	case PARAM_RIGHTANIM:
	case PARAM_SLOWLEFTANIM:
	case PARAM_SLOWRIGHTANIM:
	case PARAM_FASTLEFTANIM:
	case PARAM_FASTRIGHTANIM:
		{
			pNode = new CPathNode(iType);
			break;
		}
	case PARAM_STARTOVERLAPCURVE:
	case PARAM_ENDOVERLAPCURVE:
		{
			pNode = new CCurveTypeNode(iType,0);
			break;
		}
	case PARAM_TRANSITIONSTART:
	case PARAM_PLAYOLDSTATETILL:
	case PARAM_OVERRIDENEWSTATEPOSITION:
	case PARAM_STARTNEWSTATE:
		{
			pNode = new CFloatNode(iType);
			break;
		}
	case PARAM_SPEEDATTRIB:
	case PARAM_PITCHATTRIB:
	case PARAM_ROLLATTRIB:
		{
			pNode = new CAttribTypeNode(iType,0);
			break;
		}
	default:
		{
		}
	}
	if (pNode)
	{
		CString strValue;
		if (page)
		{
			const char *pData = NULL;
			if (page->GetEntry(pNode->GetKeyString(),&pData))
			{
				pNode->SetValue(pData);
			}
		}
		CParamNode *pOldNode = SetParam(iType,pNode);
		if (pOldNode)
		{
			// Might want to set the new node's value to the old one
			CString strValue = pOldNode->GetValue();
			pNode->SetValue(strValue);
		}
		CAnimScriptDoc::m_listParamNodes.AddHead(pNode);
	}
	return pNode;
}

// Given a known Animation type, create the parameters that go with it
void CAnimNode::CreateAnimTypeParams(CAnimTypeNode *pNode,Page *page)
{
	assert(pNode);
	CMapWordToPtr mapInvalid; //map the invalid types so we can remove them

	// Add the valid parameters
	int iSize = sizeof(g_arrAnimTypeParams)>>3;
	for (int x=0;x<iSize;x++)
	{
		int iType = g_arrAnimTypeParams[x][0];
		int iParam = g_arrAnimTypeParams[x][1];

		if (pNode->m_iType == iType)
		{
			CreateParam(iParam,page);
			mapInvalid[iParam] = 0;
		}
		else
		{
			void *pNul;
			if (!mapInvalid.Lookup(iParam,pNul))
				pNul = (void *)1;
			mapInvalid[iParam] = pNul;
		}
	}

	// Now go through the param map and remove anything that is also in m_mapInvalid
	POSITION pos = m_mapParams.GetStartPosition();
	while (pos)
	{
		WORD iKey;
		CObject *pObj;
		m_mapParams.GetNextAssoc(pos,iKey,pObj);
		CParamNode *pParam = (CParamNode *)pObj;
		void *pNul;
		if (mapInvalid.Lookup(iKey,pNul))
		{
			if (pNul)
			{
				// iKey is invalid
				m_mapParams.RemoveKey(iKey);
				POSITION delpos = CAnimScriptDoc::m_listParamNodes.Find(pParam);
				CAnimScriptDoc::m_listParamNodes.RemoveAt(delpos);
				delete pParam;
			}
		}
	}
}

bool CAnimNode::SaveState(NotationFile *file)
{
	if (this->m_bValid)
	{
		Page *page = file->AddPage(this->m_strName);
		Register_Object(page);
		
		POSITION pos = m_mapParams.GetStartPosition();
		while (pos)
		{
			WORD iKey;
			CObject *pObj;
			m_mapParams.GetNextAssoc(pos,iKey,pObj);
			CParamNode *pParam = (CParamNode *)pObj;
			page->AppendEntry(pParam->GetKeyString(),pParam->GetValue());
		}
		Unregister_Object(page);
//		delete page;
	}
	return true;
}





//**************************//
CAnimStateNode::CAnimStateNode(CString strName) : CAnimNode(strName)
{
	m_bIsTransition = false;
}


CAnimStateNode::~CAnimStateNode()
{
}

bool CAnimStateNode::LoadState(Page *page)
{

	CreateParam(PARAM_START,page);
	return CAnimNode::LoadState(page);
}

//**************************//

CTransStateNode::CTransStateNode (CAnimStateNode *pNodeX,CAnimStateNode *pNodeY) 
: CAnimStateNode((pNodeX?pNodeX->GetName():"")+"::"+(pNodeY?pNodeY->GetName():""))
{
	m_pStartNode = pNodeX;
	m_pEndNode = pNodeY;
	m_bIsTransition = true;
}

CTransStateNode::~CTransStateNode()
{
}

bool CTransStateNode::LoadState(Page *page)
{
	bool bret = CAnimStateNode::LoadState(page);
	CreateParam(PARAM_STARTOVERLAPCURVE,page);
	CreateParam(PARAM_ENDOVERLAPCURVE,page);
	CreateParam(PARAM_TRANSITIONSTART,page);
	CreateParam(PARAM_PLAYOLDSTATETILL,page);
	CreateParam(PARAM_OVERRIDENEWSTATEPOSITION,page);
	CreateParam(PARAM_STARTNEWSTATE,page);
	return bret;
}


















//**************************//


CParamNode::CParamNode()
{
	m_iParamType = -1;
}

CString CParamNode::GetKeyString()
{

	return g_szParamKeyName[m_iParamType];
}

CString CParamNode::GetValueString()
{
	CString strValue("UNDEFINED");
	return strValue;
}

bool CParamNode::OnModify()
{
	// Can't modify CParamNode because it is the base class
	return false;
}

CString CParamNode::GetValue()
{
	CString strValue("UNDEFINED");
	return strValue;
}

bool CParamNode::SetValue(CString)
{
	return true;
}


//Enum defines for this are in AnimationState.hpp enumerated
// in the range of {0,MechWarrior4::AnimationStateEngine::AnimTypeCount}
// **********************************************************************
char g_szAnimationTypeNames[][50] =
{
	"PoseType",
	"FullHeightPoseHolderType",
	"CycleType",
	"SpeedCycleType",
	"LerpCycleType",
	"FullHeightSpeedBlenderCycleType",
	"FullHeightBlenderCycleType",
	"SpeedBlenderCycleType",
/*
	"PoseType",
	"HeightPoseHolderType",
	"FullHeightPoseHolderType",
	"CycleType",
	"SpeedCycleType",
	"LerpCycleType",
	"HeightSpeedBlenderCycleType",
	"HeightBlenderCycleType",
	"FullHeightSpeedBlenderCycleType",
	"FullHeightBlenderCycleType",
	"SpeedBlenderCycleType",
*/
};

#define ANIMATIONTYPENAME_COUNT 11

CAnimTypeNode::CAnimTypeNode(int iType)
{
	m_iParamType = PARAM_ANIMTYPE;
	m_iType = iType;
	m_iIndex = -1;
	if ((iType>0)&&(iType<=ANIMATIONTYPENAME_COUNT))
		m_strName = g_szAnimationTypeNames[iType-1];
}

CString CAnimTypeNode::GetValueString()
{
	return m_strName;
}

bool CAnimTypeNode::OnModify()
{
	// Can't modify AnimType from the modify button (actually shouldn't ever show up because it is filtered out
	return false;
}

CString CAnimTypeNode::GetValue()
{
	return m_strName;
}

bool CAnimTypeNode::SetValue(CString)
{
	return true;
}



//Enum defines for this are in AnimationState.hpp enumerated
// in the range of {0,MechWarrior4::AnimCurve::CurveTypeCount}
// **********************************************************************
char g_szCurveTypeNames[][40] = 
{
	" None",
	"LinearUpCurveType",
	"CubedCurveType",
	"EaseUpCurveType",
	"EdgeCurveType",
	"BumpCurveType",
	"EaseUpSplineCurveType",
	"CurveUpCurveType",
	"SpikeUpCurveType",
	"BellCurveType",
	"SpikeCurveType",
	"LongBumpCurveType",
	"BumpSpikeCurveType",
	"SpikeBellCurveType"
};

#define CURVETYPENAMECOUNT 14
CCurveTypeNode::CCurveTypeNode(int iParamType,int iType)
{
	m_iParamType = iParamType;
	m_iType = iType-1;	// So that -1 is "None" or NULL
//	m_iIndex = -1;
	if ((iType>=0)&&(iType<=CURVETYPENAMECOUNT))
		m_strName = g_szCurveTypeNames[iType];
	InitializeClass();

	curveType = m_iType;
	curveMin = 0.0;
	curveMax = 1.0;
	timeStart = 0.0;
	timeEnd = 1.0;
	invertTime = false;
	invertScale = false;
}

CString CCurveTypeNode::GetValueString()
{
	return CString(g_szCurveTypeNames[m_iType+1]);
}

bool CCurveTypeNode::OnModify()
{
	CModifyCurveParam dlg;
	dlg.m_pCurveNode = this;
	return (IDOK == dlg.DoModal());
}

CString CCurveTypeNode::GetValue()
{
	CString str;
	str.Format("%d %f %f %f %f %d %d", curveType, curveMin, curveMax, timeStart, timeEnd, invertTime, invertScale);
	return str;
}

bool CCurveTypeNode::SetValue(CString str)
{
	if (str.GetLength())
	{
		LoadCurve(str);
		m_iType = curveType;
	}
	return true;
}

// **********************************************************************
char g_szAttribTypeNames[][30] = 
{
	"SpeedDemandMPS",
	"CurrentSpeedMPS",
	"LocalGroundPitch",
	"LocalGroundRoll"
};

#define ATTRIBTYPENAMECOUNT 4

CAttribTypeNode::CAttribTypeNode(int iParamType,int iType)
{
	m_iParamType = iParamType;
	m_iType = iType;	// So that -1 is "None" or NULL
	if ((iType>=0)&&(iType<=ATTRIBTYPENAMECOUNT))
		m_strName = g_szAttribTypeNames[iType];
}

CString CAttribTypeNode::GetValueString()
{
	return m_strName;
}

bool CAttribTypeNode::OnModify()
{
	CModifyAttribDlg dlg;
	dlg.m_pAttribNode = this;
	return (IDOK == dlg.DoModal());
}

CString CAttribTypeNode::GetValue()
{
	return m_strName;
}

bool CAttribTypeNode::SetValue(CString str)
{
	void *pData = MechWarrior4::Vehicle::DefaultData;
	return true;
}

// **********************************************************************
CFloatNode::CFloatNode(int iParamType)
{
	m_iParamType = iParamType;
	switch (iParamType)
	{
	case PARAM_SLOWSPEEDMULTIPLIER:
		{
			m_fValue = 1.0f;
			break;
		}
	default:
		{
			m_fValue = 0.0f;
		}
	}
//	void *pData = MechWarrior4::Vehicle::DefaultData;
}

CString CFloatNode::GetValueString()
{
	CString strValue;
	strValue.Format("%f",m_fValue);

	return strValue;
}

bool CFloatNode::OnModify()
{
	CModifyFloat dlg;
	dlg.m_pFloatNode = this;
	return (IDOK == dlg.DoModal());
}

CString CFloatNode::GetValue()
{
	CString strValue;
	strValue.Format("%f",m_fValue);
	return strValue;
}

bool CFloatNode::SetValue(CString str)
{
	m_fValue = atof(str);
	return true;
}

// **********************************************************************
CPathNode::CPathNode(int iParamType)
{
	m_iParamType = iParamType;
	m_pAnimData = NULL;
}

CPathNode::~CPathNode()
{
	if (m_pAnimData)
		delete m_pAnimData;
	m_pAnimData = NULL;
}

CString CPathNode::GetValueString()
{
	return m_strPath;
}

bool CPathNode::OnModify()
{
	CModifyPathDlg dlg;
	dlg.m_pPathNode = this;
	return (IDOK == dlg.DoModal());
}

CString CPathNode::GetValue()
{
	if (m_strPath.GetLength()==0)
	{
		int x=1;
		x++;
	}
	return m_strPath;
}

bool CPathNode::SetValue(CString str)
{
	m_strPath = str;
	if (m_pAnimData)
		delete m_pAnimData;
	m_pAnimData = new MW4Animation::AnimData();
	if (str.GetLength()&&!m_pAnimData->Load(m_strPath+".mw4anim"))
	{
		// We had a problem loading the animation
		assert(false);
		delete m_pAnimData;
		m_pAnimData = NULL;
	}
	return true;
}

