//
// ObjectWithWidgetsData.cpp
//
//	Copyright © Nemetschek North America, Inc  2005.
//	All Rights Reserved.

#include "StdAfx.h"

#include "ObjectEventingData.h"

#include "CustomObjects.h"

// 	Index into Parameter list inside the record node is 1-based.
// 	must match Universal 'Prm#' resource or 'PARM' resource 
const short kTextParamIndex				= 1;	
const short kCheckParamIndex			= 2;
const short kPopupIndex					= 3;


// ---------------------------------------------------------------------------------
SObjectEventingData::SObjectEventingData()
{
}

SObjectEventingData::~SObjectEventingData()
{
}

CStatesManager& SObjectEventingData::GetStateManager()
{
	return fStateManager;
}

// ---------------------------------------------------------------------------------
CObjectEventingData::CObjectEventingData(long userData)
{
	fhObject				= NULL;
	fhFormat				= NULL;
	fhRecord				= NULL;
	fpUserData				= (SObjectEventingData*) userData;

	this->InitValidFlags();
}

CObjectEventingData::~CObjectEventingData()
{
}

void CObjectEventingData::InitUserdata(bool bSetup)
{
	if ( bSetup ) {
		ASSERTN( kEveryone, fpUserData == NULL );
		fpUserData		= NULL;
		// Construct explicitly with 'placement new'. Get memory from the app because
		// memory allocated with normal 'new' disappears when the dll unloads.
		void* pRawMemory = ::GS_NewPtr( gCBP, sizeof(SObjectEventingData) );
		if ( pRawMemory ) {
			fpUserData= (SObjectEventingData*) new(pRawMemory) SObjectEventingData();	
		}
	}
	else {
		// Destruct explicitly when using 'placement new'
		if ( fpUserData ) {
			fpUserData->~SObjectEventingData();
			::GS_DisposePtr( gCBP, fpUserData );
			fpUserData = NULL;
		}
	}
}

void CObjectEventingData::InitValidFlags()
{
	fTextValid				= false;
	fUseCustomTextValid		= false;
	fPopupTextValid			= false;
}

bool CObjectEventingData::SetObject(MCObjectHandle objHand)
{
	fhObject				= objHand;
	fhFormat				= NULL;
	fhRecord				= NULL;

	this->InitValidFlags();

	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	if ( type == kParametricNode ) {
		RecordHandle	hParmRecord	= NULL;
		FormatHandle	hParmFormat	= NULL;
		if ( ::GetCustomObjectDefinition( gCBP, fhObject, hParmRecord, hParmFormat ) ) {
			fhFormat		= hParmFormat;
			fhRecord		= hParmRecord;
		}
	}
	else {
		fhFormat		= ::GS_GetNamedObject( gCBP, TXString("ObjectWithWidgets") );
		fhRecord		= NULL;
	}

	return fhFormat != NULL;
}

MCObjectHandle CObjectEventingData::GetDataObject() const
{
	return fhObject ? fhObject : fhFormat;
}

long CObjectEventingData::GetUserData() const
{
	return (long) fpUserData;
}

SObjectEventingData& CObjectEventingData::GetData()
{
	// the user data should be set here
	ASSERTN( kEveryone, fpUserData );
	return *fpUserData;
}

TXString CObjectEventingData::GetText()
{
	if ( ! fTextValid ) {
		char*		szText		= NULL;
		size_t		textSize	= 0;

		if ( fhRecord ) {
			TRecordHandler	record( fhRecord );
			TFormatHandler	format( fhFormat );	
			TRecordItem		textItem( format.GetFieldStyle( kTextParamIndex ) );
			if ( record.GetFieldObject( kTextParamIndex, textItem ) ) {
				textItem.GetFieldValue( szText, textSize );
				fText	= TXString( szText, textSize );

				fTextValid		= true;
			}
		}
		else if ( fhFormat ) {
			TFormatHandler	format( fhFormat );	
			TRecordItem		textItem( format.GetFieldStyle( kTextParamIndex ) );
			if ( format.GetFieldDefault( kTextParamIndex, textItem ) ) {
				textItem.GetFieldValue( szText, textSize );
				fText	= TXString( szText, textSize );

				fTextValid		= true;
			}
		}
	}

	return fText;
}

bool CObjectEventingData::GetUseCustomText()
{
	if ( ! fUseCustomTextValid ) {
		if ( fhRecord ) {
			TRecordHandler	record( fhRecord );
			TFormatHandler	format( fhFormat );	
			TRecordItem		booleanItem( format.GetFieldStyle( kCheckParamIndex ) );
			if ( record.GetFieldObject( kCheckParamIndex, booleanItem ) ) {
				Boolean	value;
				booleanItem.GetFieldValue( value );
				fUseCustomText	= value ? true : false;
			}
		}
		else if ( fhFormat ) {
			TFormatHandler	format( fhFormat );	
			TRecordItem		booleanItem( format.GetFieldStyle( kCheckParamIndex ) );
			if ( format.GetFieldDefault( kCheckParamIndex, booleanItem ) ) {
				Boolean	value;
				booleanItem.GetFieldValue( value );
				fUseCustomText	= value ? true : false;
			}
		}
	}

	return fUseCustomText;
}

void CObjectEventingData::SetPopupText(const TXString& text)
{
	fPopupTextValid	= true;
	fPopupText		= text;

	TRecordHandler	rh( fhRecord );
	TRecordItem		recItem( rh.GetFieldStyle( kPopupIndex ) );
	if ( rh.GetFieldObject( kPopupIndex, recItem ) ) {
		const char* szText		= fPopupText;
		size_t		textLen		= fPopupText.GetLength();

		if ( recItem.SetFieldValue( szText, textLen ) ) {
			rh.SetFieldObject( kPopupIndex, recItem );
		}
	}
}


TXString CObjectEventingData::GetPopupText()
{
	if ( ! fPopupTextValid ) {
		char*		szText		= NULL;
		size_t		textSize	= 0;

		if ( fhRecord ) {
			TRecordHandler	record( fhRecord );
			TFormatHandler	format( fhFormat );	
			TRecordItem		textItem( format.GetFieldStyle( kPopupIndex ) );
			if ( record.GetFieldObject( kPopupIndex, textItem ) ) {
				textItem.GetFieldValue( szText, textSize );
				fPopupText		= TXString( szText, textSize );

				fPopupTextValid	= true;
			}
		}
		else if ( fhFormat ) {
			TFormatHandler	format( fhFormat );	
			TRecordItem		textItem( format.GetFieldStyle( kPopupIndex ) );
			if ( format.GetFieldDefault( kPopupIndex, textItem ) ) {
				textItem.GetFieldValue( szText, textSize );
				fPopupText		= TXString( szText, textSize );

				fPopupTextValid	= true;
			}
		}
	}

	return fPopupText;
}

// ---------------------------------------------------------------------------------
CStatesManager::CStatesManager()
{
}

CStatesManager::~CStatesManager()
{
}

void CStatesManager::ClearStatesToMonitor()
{
	fmapStateRegistry.clear();
}

void CStatesManager::AddStateToMonitor(ObjectState::EStateType type)
{
	fmapStateRegistry.insert( TVWMemoryStateRegistryMap::value_type( type, true ) );
}

void CStatesManager::ClearStates()
{
	fmapValues.clear();
}

void CStatesManager::ClearStates(MCObjectHandle hObject)
{
	fmapValues.erase( hObject );
}

void CStatesManager::AddState(MCObjectHandle hObject, ObjectState* pState)
{
	if ( pState ) {
		bool		valueOk	= false;
		SStateValue	value;
		value.type			= (ObjectState::EStateType) pState->fSpecifier;
		// check if we are monitoring this state
		TVWMemoryStateRegistryMap::const_iterator	it	= fmapStateRegistry.find( value.type );
		if ( it != fmapStateRegistry.end() ) {
			switch( value.type ) {
				case ObjectState::kCreatedReset:
					valueOk		= true;
					break;
				case ObjectState::kMovedReset:
					if ( pState->fData ) {
						valueOk				= true;
						value.pos			= * ((const ObjectStateData_Position*) pState->fData);
					}
					break;
				case ObjectState::kRotatedReset:
					if ( pState->fData ) {
						valueOk				= true;
						value.pos			= * ((const ObjectStateData_Position*) pState->fData);
					}
					break;
				case ObjectState::kParameterChangedReset:
					if ( pState->fData ) {
						valueOk				= true;
						value.param			= * ((const ObjectStateData_ParamChanged*) pState->fData);
					}
					break;
				case ObjectState::kObjectChangedReset:
					if ( pState->fData ) {
						valueOk				= true;
						value.objChanged	= * ((const ObjectStateData_ObjectChanged*) pState->fData);
					}
					break;
				case ObjectState::kLayerChangedReset:
					if ( pState->fData ) {
						valueOk				= true;
						value.layerChanged	= * ((const ObjectStateData_LayerChanged*) pState->fData);
					}
					break;
				case ObjectState::kExitFromEditGroup:
					if ( pState->fData ) {
						valueOk				= true;
						value.editGroup		= * ((const ObjectStateData_ExitFromEditGroup*) pState->fData);
					}
					break;
				case ObjectState::kNameChanged:
					if ( pState->fData ) {
						valueOk					= true;
						value.objNameChanged	= * ((ObjectStateData_ObjectNameChanged*) pState->fData);
					}
					break;
				case ObjectState::kObjectUndoRestore:
					valueOk		= true;
					break;
				case ObjectState::kObjectUndoRemove:
					valueOk		= true;
					break;
				default:
					valueOk		= false;
					break;
			}

			if ( valueOk ) {
				fmapValues.insert( TVWMemoryValuesMap::value_type( hObject, value ) );
			}
		}
	}
}

CStatesManager::TVWMemoryValuesMap::iterator CStatesManager::FindState(MCObjectHandle hObject, ObjectState::EStateType type)
{
	TVWMemoryValuesMap::iterator	res		= fmapValues.end();

	TVWMemoryValuesMap::iterator	itCurr	= fmapValues.lower_bound( hObject );
	TVWMemoryValuesMap::iterator	itEnd	= fmapValues.upper_bound( hObject );
	for(; itCurr != itEnd; ++itCurr ) {
		const SStateValue&	value	= itCurr->second;
		if ( value.type == type ) {
			res	= itCurr;
			// STOP! We found it!
			break;
		}
	}

	return res;
}

bool CStatesManager::PopState(MCObjectHandle hObject, ObjectState::EStateType type)
{
	bool	ok		= false;

	TVWMemoryValuesMap::iterator	it	= this->FindState( hObject, type );
	if ( it != fmapValues.end() ) {
		fmapValues.erase( it );
		ok		= true;
	}

	return ok;
}

bool CStatesManager::PopState(MCObjectHandle hObject, ObjectStateData_Position& outData, bool isMove)
{
	bool	ok	= false;

	TVWMemoryValuesMap::iterator	it	= this->FindState( hObject, isMove ? ObjectState::kMovedReset : ObjectState::kRotatedReset );
	if ( it != fmapValues.end() ) {
		const SStateValue&	value	= it->second;
		outData						= value.pos;

		fmapValues.erase( it );
		ok							= true;
	}

	return ok;
}

bool CStatesManager::PopState(MCObjectHandle hObject, ObjectStateData_ParamChanged& outData)
{
	bool	ok	= false;

	TVWMemoryValuesMap::iterator	it	= this->FindState( hObject, ObjectState::kParameterChangedReset );
	if ( it != fmapValues.end() ) {
		const SStateValue&	value	= it->second;
		outData						= value.param;

		fmapValues.erase( it );
		ok							= true;
	}

	return ok;
}

bool CStatesManager::PopState(MCObjectHandle hObject, ObjectStateData_ObjectChanged& outData)
{
	bool	ok	= false;

	TVWMemoryValuesMap::iterator	it	= this->FindState( hObject, ObjectState::kObjectChangedReset );
	if ( it != fmapValues.end() ) {
		const SStateValue&	value	= it->second;
		outData						= value.objChanged;

		fmapValues.erase( it );
		ok							= true;
	}

	return ok;
}

bool CStatesManager::PopState(MCObjectHandle hObject, ObjectStateData_LayerChanged& outData)
{
	bool	ok	= false;

	TVWMemoryValuesMap::iterator	it	= this->FindState( hObject, ObjectState::kLayerChangedReset );
	if ( it != fmapValues.end() ) {
		const SStateValue&	value	= it->second;
		outData						= value.layerChanged;

		fmapValues.erase( it );
		ok							= true;
	}

	return ok;
}

bool CStatesManager::PopState(MCObjectHandle hObject, ObjectStateData_ExitFromEditGroup& outData)
{
	bool	ok	= false;

	TVWMemoryValuesMap::iterator	it	= this->FindState( hObject, ObjectState::kExitFromEditGroup );
	if ( it != fmapValues.end() ) {
		const SStateValue&	value	= it->second;
		outData						= value.editGroup;

		fmapValues.erase( it );
		ok							= true;
	}

	return ok;
}

bool CStatesManager::PopState(MCObjectHandle hObject, ObjectStateData_ObjectNameChanged& outData)
{
	bool	ok	= false;

	TVWMemoryValuesMap::iterator	it	= this->FindState( hObject, ObjectState::kNameChanged );
	if ( it != fmapValues.end() ) {
		const SStateValue&	value	= it->second;
		outData						= value.objNameChanged;

		fmapValues.erase( it );
		ok							= true;
	}

	return ok;
}

