//
//	Copyright © 2005 Nemetschek North America 
//	All Rights Reserved.
//
// Created by Vladislav Stanev

#include "StdAfx.h"
//#include "VWParametricObj.h"

#include "CustomObjects.h"

using namespace VWFC::Tools;
using namespace VWFC::Math;
using namespace VWFC::VWObjects;

VWParametricObj::VWParametricObj(MCObjectHandle hParametric)
: VWGroupObj( 0 )	// call nothing doing constructor -- we dont want a new group created
{
	// this may come NULL
	if ( hParametric ) {
		VWFC_ASSERT( VWParametricObj::IsParametricObject( hParametric ) );
		if ( ! VWParametricObj::IsParametricObject( hParametric ) )
			THROW_VWFC_EXCEPTION( VWBadObjectTypeException, 0, "bad handle type when creating" );
	}

	this->SetParametricHandle( hParametric );
}

VWParametricObj::VWParametricObj(const TXString& parametricName, const VWPoint2D& location, double angle /*= 0*/)
: VWGroupObj( 0 )	// call nothing doing constructor -- we dont want a new group created
{
	::GS_DefineCustomObject( gCBP, parametricName, kCustomObjectPrefNever );
	MCObjectHandle hParametric = ::GS_CreateCustomObject( gCBP, parametricName, location, angle );
	VWFC_ASSERT( hParametric );
	if ( hParametric == NULL )
		THROW_VWFC_EXCEPTION( VWCannotCreateObjectException, 0, "cannot create parametric" );
	this->SetParametricHandle( hParametric );
}

VWParametricObj::VWParametricObj(const TXString& parametricName, MCObjectHandle hPathObject)
: VWGroupObj( 0 )	// call nothing doing constructor -- we dont want a new group created
{
	::GS_DefineCustomObject( gCBP, parametricName, kCustomObjectPrefNever );
	MCObjectHandle hParametric = ::GS_CreateCustomObjectPath( gCBP, parametricName, hPathObject, NULL );
	VWFC_ASSERT( hParametric );
	if ( hParametric == NULL )
		THROW_VWFC_EXCEPTION( VWCannotCreateObjectException, 0, "cannot create parametric" );
	this->SetParametricHandle( hParametric );
}

VWParametricObj::VWParametricObj(const VWParametricObj& src)
: VWGroupObj(0)
{
	this->SetParametricHandle( src.fhObject );
}

VWParametricObj::~VWParametricObj()
{
}

VWParametricObj& VWParametricObj::operator=(const VWParametricObj& src)
{
	this->SetParametricHandle( src.fhObject );
	return *this;
}

void VWParametricObj::GetObjectToWorldTransform(VWTransformMatrix& out) const
{
	TransformMatrix	matrix;
	::GS_GetEntityMatrix( gCBP, fhObject, matrix );
	out		= matrix;
}

bool VWParametricObj::IsParametricObject(MCObjectHandle hObj)
{
	short	type	= ::GS_GetObjectType( gCBP, hObj );
	return type == kParametricNode;
}

bool VWParametricObj::IsParametricObject(MCObjectHandle hObj, const char* szName)
{
	short	type	= ::GS_GetObjectType( gCBP, hObj );
	if ( type != kParametricNode )
		return false;

	bool	bFound	= false;
	RecordHandle	hRecord	= (RecordHandle) ::GS_FindAuxObject( gCBP, hObj, recordNode );
	FormatHandle	hFormat	= NULL;
	while( hRecord && ! bFound ){
		hFormat = (FormatHandle) ::GS_GetDefinition( gCBP, (MCObjectHandle) hRecord );
		if ( hFormat ) {
			TFormatHandler		currFormatHandler( hFormat );
			if ( currFormatHandler.IsParametric() ) {
				// If we find a recordNode on the aux list of the kParametricNode, and its formatNode has the parametric bit set,
				// then we can assume it is "the one".  No name checking is necessary.  JAK 9/27/99 Fix for VW 8.5.0.
				bFound = true;
			}
		}

		if ( ! bFound ) {
			// loop increment
			hRecord = ::GS_NextAuxObject( gCBP, ::GS_NextObject( gCBP, hRecord ), recordNode );
		}
	}

	if ( hFormat == NULL )
		return false;

	TFormatHandler	fh( hFormat );
	Str63 nameOfFormat;
	fh.GetNameOfFormat( nameOfFormat );
	return TXString( nameOfFormat ) == szName;
}

void VWParametricObj::SetParametricHandle(MCObjectHandle hParametric)
{
	fhObject		= hParametric;

	bool	bFound	= false;

	// Loop to find record among all record nodes on aux list.
	RecordHandle	hRecord	= (RecordHandle) ::GS_FindAuxObject( gCBP, hParametric, recordNode );
	FormatHandle	hFormat	= NULL;
	while( hRecord && ! bFound ){
		hFormat = (FormatHandle) ::GS_GetDefinition( gCBP, (MCObjectHandle) hRecord );
		if ( hFormat ) {
			TFormatHandler		currFormatHandler( hFormat );
			if ( currFormatHandler.IsParametric() ) {
				// If we find a recordNode on the aux list of the kParametricNode, and its formatNode has the parametric bit set,
				// then we can assume it is "the one".  No name checking is necessary.  JAK 9/27/99 Fix for VW 8.5.0.
				bFound = true;
			}
		}

		if ( ! bFound ) {
			// loop increment
			hRecord = ::GS_NextAuxObject( gCBP, ::GS_NextObject( gCBP, hRecord ), recordNode );
		}
	}
	
	fhParamRecord	= bFound ? hRecord : NULL;
	fhParamFormat	= bFound ? hFormat : NULL;
}

TXString VWParametricObj::GetParametricName() const
{
	TFormatHandler	fh( fhParamFormat );
	Str63 nameOfFormat;
	fh.GetNameOfFormat( nameOfFormat );
	return TXString( nameOfFormat );
}

MCObjectHandle VWParametricObj::GetObjectPath() const
{
	return ::GS_GetCustomObjectPath( gCBP, fhObject );
}

void VWParametricObj::SetObjectPath(MCObjectHandle hPathGroup)
{
	::GS_SetCustomObjectPath( gCBP, fhObject, hPathGroup );
}

MCObjectHandle VWParametricObj::GetObjectProfileGroup() const
{
	return ::GS_GetCustomObjectProfileGroup( gCBP, fhObject );
}

void VWParametricObj::SetObjectProfileGroup(MCObjectHandle hProfileGroup)
{
	VERIFYN( kVStanev, ::GS_SetCustomObjectProfileGroup( gCBP, fhObject, hProfileGroup ) );
}

VWRecordFormatObj VWParametricObj::GetRecordFormat() const
{
	return VWRecordFormatObj( fhParamFormat );
}

VWPoint2D VWParametricObj::GetPointObjectPos() const
{
	VWTransformMatrix	matrix;
	this->GetObjectMatrix( matrix );
	return matrix.GetOffset();
}

void VWParametricObj::SetPointObjectPos(const VWPoint2D& pt)
{
	VWTransformMatrix	matrix;
	this->GetObjectMatrix( matrix );
	matrix.SetOffset( pt );
	this->SetObjectMatrix( matrix );
}

void VWParametricObj::GetLinearObjectPos(VWPoint2D& outA, VWPoint2D& outB) const
{
	WorldPt			startPt;
	WorldPt			endPt;
	::GS_GetEndPoints( gCBP, fhObject, startPt, endPt );

	outA			= startPt;
	outB			= endPt;		
}

void VWParametricObj::SetLinearObjectPos(const VWPoint2D& a, VWPoint2D& b)
{
	::GS_SetEndPoints( gCBP, fhObject, a, b );
}

void VWParametricObj::EnableParameter(size_t paramIndex, bool bEnabled)
{
	// NOTE! 'GS_SetParameterVisibility' return 1 based value
	::GS_EnableParameter( gCBP, fhObject, (short) paramIndex+1, bEnabled );
}

void VWParametricObj::SetControlPtVisibility(size_t ctrlPtIndex, bool bVisible)
{
	// NOTE! 1 based index value
	::GS_SetCustomObjectControlPointVisibility( gCBP, fhObject, DemoteTo<short>( kVStanev, ctrlPtIndex + 1 ), bVisible );
}

void VWParametricObj::SetParameterVisibility(size_t paramIndex, bool bVisible)
{
	// NOTE! 'GS_SetParameterVisibility' return 1 based value
	::GS_SetParameterVisibility( gCBP, fhObject, (short) paramIndex+1, bVisible );
}

/*
enum EFieldStyle {
	kFieldLongInt		= 1,
	kFieldBoolean		= 2,
	kFieldReal			= 3,
	kFieldText			= 4,
	kFieldCalculation	= 5,
	kFieldHandle 		= 6,
	kFieldCoordDisp		= 7,
	kFieldPopUp			= 8,
	kFieldRadio			= 9,
	kFieldCoordLocX		= 10,	
	kFieldCoordLocY		= 11,
	kFieldStaticText	= 14
	
};
*/

size_t VWParametricObj::GetParamsCount() const
{
	VWFC_ASSERT( fhParamFormat && fhParamRecord );
	if ( fhParamFormat == NULL || fhParamRecord == NULL )
		return 0;

	TRecordHandler rh( fhParamRecord );
	return (size_t) rh.GetNumOfFields();
}

TXString VWParametricObj::GetParamName(size_t paramIndex) const
{
	VWFC_ASSERT( fhParamFormat && fhParamRecord );
	if ( fhParamFormat == NULL || fhParamRecord == NULL )
		return TXString();

	TRecordHandler rh( fhParamRecord );

	Str20	nameOfField;
	// NOTE! 'GetNameOfField' return 1 based value
	rh.GetNameOfField( DemoteTo<short>( kVStanev, paramIndex+1 ), nameOfField );
	return nameOfField;
}

TXString VWParametricObj::GetParamLocalizedName(size_t paramIndex) const
{
	TXString			paramName;

	UnivParmTabHandle	hParameterRes = (UnivParmTabHandle) ::GS_GetResource( gCBP, OST_Prm, kUnivParmResID );
	if ( hParameterRes && *hParameterRes ) {
		if ( paramIndex >= 0 && paramIndex < (size_t) (*hParameterRes)->numParms ) {
			unsigned char*	psLocalizedName	= (*hParameterRes)->parms[ paramIndex ].fLocalizedName;

			paramName		= psLocalizedName;
		}
		::GS_ReleaseResource(gCBP, (Handle) hParameterRes);
	}
	else {
		// Current parameter resource not found.  Look for old resource type ('PARM').
		paramName	= this->GetParamName( paramIndex );
	}

	return paramName;
}

size_t VWParametricObj::GetParamIndex(const TXString& paramName) const
{
	VWFC_ASSERT( fhParamFormat && fhParamRecord );
	if ( fhParamFormat == NULL || fhParamRecord == NULL )
		return (size_t) -1;	// not found

	TRecordHandler rh( fhParamRecord );
	// NOTE! 'GetFieldIndex' return 1 based value
	int		index	= rh.GetFieldIndex( paramName );
	if ( index == 0 )
		return (size_t) -1;	// not found

	return (size_t) (index - 1);
}

EFieldStyle VWParametricObj::GetParamStyle(size_t paramIndex) const
{
	VWFC_ASSERT( fhParamFormat && fhParamRecord );
	if ( fhParamFormat == NULL || fhParamRecord == NULL )
		return kFieldLongInt;	// Error default value

	TRecordHandler rh( fhParamRecord );
	// NOTE! 'GetFieldStyle' return 1 based value
	return (EFieldStyle) rh.GetFieldStyle( DemoteTo<short>( kVStanev, paramIndex+1 ) );
}

TXString VWParametricObj::GetParamValue(size_t paramIndex) const
{
	TXString	value;
	VWFC_ASSERT( fhParamFormat && fhParamRecord );
	if ( fhParamFormat == NULL || fhParamRecord == NULL )
		return value;

	TRecordHandler rh( fhParamRecord );
//	TFormatHandler fh( fhParamFormat );

	// NOTE! itemIndex is expected from the SDK as 1 based value
	short			itemIndex	= DemoteTo<short>( kVStanev, paramIndex+1 );
	EFieldStyle		style		= (EFieldStyle) rh.GetFieldStyle( itemIndex );
	TRecordItem valueItem( style );
	Boolean			ok			= rh.GetFieldObject( itemIndex, valueItem );
	VWFC_ASSERT( ok );

	if ( style == kFieldText ) {
		char*		string		= NULL;
		size_t		len			= 0;

		if ( ok ) {
			valueItem.GetFieldValue( string, len );
			value	= TXString( string, len );
		}
	}
	else {
		Str255		theString;
		if ( valueItem.GetFieldValueAsString( theString ) ) {
			value	= theString;
		}
	}

	return value;
}

void VWParametricObj::SetParamValue(size_t paramIndex, const TXString& value)
{
	VWFC_ASSERT( fhParamFormat && fhParamRecord );
	if ( fhParamFormat == NULL || fhParamRecord == NULL )
		return;

	TRecordHandler	rh( fhParamRecord );

	// NOTE! itemIndex is expected from the SDK as 1 based value
	short			itemIndex	= DemoteTo<short>( kVStanev, paramIndex+1 );
	EFieldStyle		style		= (EFieldStyle) rh.GetFieldStyle( itemIndex );
	TRecordItem		recItem( style );
	if ( VWFC_VERIFY( rh.GetFieldObject( itemIndex, recItem ) ) ) {
		if ( style == kFieldText ) {
			const char* szText		= value;
			size_t		textLen		= value.GetLength();

			if ( VWFC_VERIFY( recItem.SetFieldValue( szText, textLen ) ) ) {
				VWFC_VERIFY( rh.SetFieldObject( itemIndex, recItem ) );
			}
		}
		else {
			Str255		theString;
			value.CopyInto( theString );
			if ( VWFC_VERIFY( recItem.SetFieldValueAsString( theString ) ) ) {
				VWFC_VERIFY( rh.SetFieldObject( itemIndex, recItem ) );
			}
		}
	}
}

long VWParametricObj::GetParamLong(size_t paramIndex) const
{
	VWFC_ASSERT( fhParamFormat && fhParamRecord );
	if ( fhParamFormat == NULL || fhParamRecord == NULL )
		return 0;

	TRecordHandler rh( fhParamRecord );
//	TFormatHandler fh( fhParamFormat );

	// NOTE! itemIndex is expected from the SDK as 1 based value
	short		itemIndex	= DemoteTo<short>( kVStanev, paramIndex+1 );
	TRecordItem valueItem( rh.GetFieldStyle( itemIndex ) );
	Boolean		ok			= rh.GetFieldObject( itemIndex, valueItem );
	VWFC_ASSERT( ok );

	long		value	= 0;
	if ( ok ) {
		valueItem.GetFieldValue( value );
	}

	return value;
}

void VWParametricObj::SetParamLong(size_t paramIndex, long value)
{
	VWFC_ASSERT( fhParamFormat && fhParamRecord );
	if ( fhParamFormat == NULL || fhParamRecord == NULL )
		return;

	TRecordHandler	rh( fhParamRecord );

	// NOTE! itemIndex is expected from the SDK as 1 based value
	short			itemIndex	= DemoteTo<short>( kVStanev, paramIndex+1 );
	TRecordItem		recItem( rh.GetFieldStyle( itemIndex ) );
	if ( VWFC_VERIFY( rh.GetFieldObject( itemIndex, recItem ) ) ) {
		if ( VWFC_VERIFY( recItem.SetFieldValue( value ) ) ) {
			VWFC_VERIFY( rh.SetFieldObject( itemIndex, recItem ) );
		}
	}
}

bool VWParametricObj::GetParamBool(size_t paramIndex) const
{
	VWFC_ASSERT( fhParamFormat && fhParamRecord );
	if ( fhParamFormat == NULL || fhParamRecord == NULL )
		return 0;

	TRecordHandler rh( fhParamRecord );
//	TFormatHandler fh( fhParamFormat );

	// NOTE! itemIndex is expected from the SDK as 1 based value
	short		itemIndex	= DemoteTo<short>( kVStanev, paramIndex+1 );
	TRecordItem valueItem( rh.GetFieldStyle( itemIndex ) );
	Boolean		ok			= rh.GetFieldObject( itemIndex, valueItem );
	VWFC_ASSERT( ok );

	Boolean		value	= 0;
	if ( ok ) {
		valueItem.GetFieldValue( value );
	}

	return value ? true : false;
}

void VWParametricObj::SetParamBool(size_t paramIndex, bool value)
{
	VWFC_ASSERT( fhParamFormat && fhParamRecord );
	if ( fhParamFormat == NULL || fhParamRecord == NULL )
		return;

	TRecordHandler	rh( fhParamRecord );

	// NOTE! itemIndex is expected from the SDK as 1 based value
	short			itemIndex	= DemoteTo<short>( kVStanev, paramIndex+1 );
	TRecordItem		recItem( rh.GetFieldStyle( itemIndex ) );
	if ( VWFC_VERIFY( rh.GetFieldObject( itemIndex, recItem ) ) ) {
		Boolean		boolValue	= value;
		if ( VWFC_VERIFY( recItem.SetFieldValue( boolValue ) ) ) {
			VWFC_VERIFY( rh.SetFieldObject( itemIndex, recItem ) );
		}
	}
}

double VWParametricObj::GetParamReal(size_t paramIndex) const
{
	VWFC_ASSERT( fhParamFormat && fhParamRecord );
	if ( fhParamFormat == NULL || fhParamRecord == NULL )
		return 0;

	TRecordHandler rh( fhParamRecord );
//	TFormatHandler fh( fhParamFormat );

	// NOTE! itemIndex is expected from the SDK as 1 based value
	short		itemIndex	= DemoteTo<short>( kVStanev, paramIndex+1 );
	TRecordItem valueItem( rh.GetFieldStyle( itemIndex ) );
	Boolean		ok			= rh.GetFieldObject( itemIndex, valueItem );
	VWFC_ASSERT( ok );

	double		value	= 0;
	if ( ok ) {
		valueItem.GetFieldValue( value );
	}

	return value;
}

void VWParametricObj::SetParamReal(size_t paramIndex, double value)
{
	VWFC_ASSERT( fhParamFormat && fhParamRecord );
	if ( fhParamFormat == NULL || fhParamRecord == NULL )
		return;

	TRecordHandler	rh( fhParamRecord );

	// NOTE! itemIndex is expected from the SDK as 1 based value
	short			itemIndex	= DemoteTo<short>( kVStanev, paramIndex+1 );
	TRecordItem		recItem( rh.GetFieldStyle( itemIndex ) );
	if ( VWFC_VERIFY( rh.GetFieldObject( itemIndex, recItem ) ) ) {
		if ( VWFC_VERIFY( recItem.SetFieldValue( value ) ) ) {
			VWFC_VERIFY( rh.SetFieldObject( itemIndex, recItem ) );
		}
	}
}

TXString VWParametricObj::GetParamString(size_t paramIndex) const
{
	VWFC_ASSERT( fhParamFormat && fhParamRecord );
	if ( fhParamFormat == NULL || fhParamRecord == NULL )
		return "";

	TRecordHandler rh( fhParamRecord );
//	TFormatHandler fh( fhParamFormat );

	// NOTE! itemIndex is expected from the SDK as 1 based value
	short		itemIndex	= DemoteTo<short>( kVStanev, paramIndex+1 );
	TRecordItem valueItem( rh.GetFieldStyle( itemIndex ) );
	Boolean		ok		= rh.GetFieldObject( itemIndex, valueItem );
	VWFC_ASSERT( ok );

	TXString	value;
	char*		string		= NULL;
	size_t		len			= 0;

	if ( ok ) {
		valueItem.GetFieldValue( string, len );
		value	= TXString( string, len );
	}

	return value;
}

void VWParametricObj::SetParamString(size_t paramIndex, const TXString& value)
{
	VWFC_ASSERT( fhParamFormat && fhParamRecord );
	if ( fhParamFormat == NULL || fhParamRecord == NULL )
		return;

	TRecordHandler	rh( fhParamRecord );
//	TFormatHandler	fh( fhParamFormat );

	// NOTE! itemIndex is expected from the SDK as 1 based value
	short			itemIndex	= DemoteTo<short>( kVStanev, paramIndex+1 );
	TRecordItem		recItem( rh.GetFieldStyle( itemIndex ) );
	if ( VWFC_VERIFY( rh.GetFieldObject( itemIndex, recItem ) ) ) {
		const char* szText		= value;
		size_t		textLen		= value.GetLength();

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

double VWParametricObj::Get2DRotation180() const
{
	VWTransformMatrix	mat;
	this->GetObjectToWorldTransform( mat );

	VWPoint2D	pt	= mat.GetUVector();
	return	pt.CalcAng180(); 
}

double VWParametricObj::Get2DRotation360() const
{
	VWTransformMatrix	mat;
	this->GetObjectToWorldTransform( mat );

	VWPoint2D	pt	= mat.GetUVector();
	return	pt.CalcAng360(); 
}

bool VWParametricObj::GetParamChoices(size_t paramIndex, TXStringSTLArray& outArray) const
{
	outArray.clear();

	bool				ok				= false;
	UnivParmTabHandle	hParameterRes	= (UnivParmTabHandle) ::GS_GetResource( gCBP, OST_Prm, kUnivParmResID );
	if ( hParameterRes && *hParameterRes ) {
		if ( paramIndex >= 0 && paramIndex < (size_t) (*hParameterRes)->numParms ) {
			const UnivParmSpec&	paramDef	= (*hParameterRes)->parms[ paramIndex ];
			if ( paramDef.fType == kFieldPopUp && paramDef.fChoices > 0 ) {
				ParmChoiceTabHandle		hChoicesRes	= (ParmChoiceTabHandle) ::GS_GetResource( gCBP, OST_Chc, paramDef.fChoices );
				if ( hChoicesRes && *hChoicesRes ) {
					ok			= true;
					for(short i=0; i<(*hChoicesRes)->numChoices; i++) {
						const unsigned char*	psName	= (*hChoicesRes)->choices[i].fUniversalChoiceName;

						outArray.push_back( TXString( psName ) );
					}
				}

				::GS_ReleaseResource(gCBP, (Handle) hChoicesRes);

			}
		}
		::GS_ReleaseResource(gCBP, (Handle) hParameterRes);
	}

	return ok;
}

bool VWParametricObj::GetParamLocalizedChoices(size_t paramIndex, TXStringSTLArray& outArray) const
{
	outArray.clear();

	bool				ok				= false;
	UnivParmTabHandle	hParameterRes	= (UnivParmTabHandle) ::GS_GetResource( gCBP, OST_Prm, kUnivParmResID );
	if ( hParameterRes && *hParameterRes ) {
		if ( paramIndex >= 0 && paramIndex < (size_t) (*hParameterRes)->numParms ) {
			const UnivParmSpec&	paramDef	= (*hParameterRes)->parms[ paramIndex ];
			if ( paramDef.fType == kFieldPopUp && paramDef.fChoices > 0 ) {
				ParmChoiceTabHandle		hChoicesRes	= (ParmChoiceTabHandle) ::GS_GetResource( gCBP, OST_Chc, paramDef.fChoices );
				if ( hChoicesRes && *hChoicesRes ) {
					for(short i=0; i<(*hChoicesRes)->numChoices; i++) {
						const unsigned char*	psName	= (*hChoicesRes)->choices[i].fLocalizedChoiceName;

						outArray.push_back( TXString( psName ) );
					}
				}

				::GS_ReleaseResource(gCBP, (Handle) hChoicesRes);

			}
		}
		::GS_ReleaseResource(gCBP, (Handle) hParameterRes);
	}

	return ok;
}

/*static*/ MCObjectHandle VWParametricObj::DefineCustomObject(const char* szName, bool bShowPrefs)
{
	TXString	parametricName( szName );
	return ::GS_DefineCustomObject( gCBP, parametricName, bShowPrefs ? kCustomObjectPrefAlways : kCustomObjectPrefNever );
}

