//-------------------------------------------------------------------------------------------------
// <copyright file="LqrSMACLuaEventConversion.cpp" company="LiquidRobotics">
//  Copyright (c) Liquid Robotics Corporation, confidential and proprietary.  All rights reserved. 
// </copyright>
//
// <summary>
//		Implementation of LuaEventConversion class, which contains methos for
//		converting events and event parameters between lua and the C++/CORBA forms.
// </summary>
//
// <owner>Mike Cookson</owner>
//-------------------------------------------------------------------------------------------------

#include "LqrSMACLuaEventConversion.h"
#include "EventBase.h"
#include <set>
#include <sstream>

using namespace std;
using namespace LiquidRobotics::EventLib;

static const string keyHeader = "header";
static const string keyName = "name";
static const string keyTypeName = "typename";
static const string keyDomainName = "domainname";
static const string keyProperties = "properties";

namespace LiquidRobotics {
namespace SMACFramework {

inline int Offset(int stackOffset, int addition)
{
	int result;

	if (stackOffset < 0)
		result = stackOffset+addition;
	else
		result = stackOffset;

	return result;
}

template<typename T, typename V>
bool LuaEventConversion::ConvertArray(const T inref, V& result)
{
	if (inref)
	{
		result.resize(0);
		result.reserve(inref->size());

		for (typename T::element_type::const_iterator ci = inref->begin(); ci != inref->end(); ++ci)
			result.push_back((typename V::value_type) *ci);

		return true;
	}
	else
		return false;
}

template<typename T>
bool LuaEventConversion::ConvertStr(const T inref, string& result)
{
	if (inref)
	{
		result.assign((const char*) &inref->front(), inref->size());
		return true;
	}
	else
		return false;
}

LuaEventConversion::LuaEventConversion(LuaInstance& _L, IObjectBroker& _broker)
: L(_L)
, broker(_broker)
{
}

template<typename EXTRACTOR> struct ExtractArray
{
	ExtractArray(int stackOffset, EventBase::PropertyValueType& pvt, LuaInstance& L)
	{
		const int numEle = L.objlen(stackOffset);
		EXTRACTOR extractor(L);
		typename EXTRACTOR::HOLDER holder;

		holder.reserve(numEle);

		for (int idx = 1; idx <= numEle; ++idx)
		{
			L.rawgeti(stackOffset, idx);
			holder.push_back(extractor.Extract());
			L.pop(1);
		}

		extractor.SetResult(holder, pvt);
	}
};

template<typename EXTRACTOR> struct TargetArray
{
	TargetArray(const EventBase::PropertyValueType::BooleanVector& dfv, EventBase::PropertyValueType& pvt, LuaInstance& L)
	{
		const size_t numEle = dfv.size();
		EXTRACTOR extractor(L);
		typedef typename EXTRACTOR::HOLDER HOL;
		HOL holder;

		holder.reserve(numEle);

		for (EventBase::PropertyValueType::BooleanVector::const_iterator ci = dfv.begin(); ci != dfv.end(); ++ci)
			holder.push_back((typename HOL::value_type) !!*ci);

		extractor.SetResult(holder, pvt);
	}

	TargetArray(const EventBase::PropertyValueType::DoubleFloatVector& dfv, EventBase::PropertyValueType& pvt, LuaInstance& L)
	{
		const size_t numEle = dfv.size();
		EXTRACTOR extractor(L);
		typedef typename EXTRACTOR::HOLDER HOL;
		HOL holder;

		holder.reserve(numEle);

		for (EventBase::PropertyValueType::DoubleFloatVector::const_iterator ci = dfv.begin(); ci != dfv.end(); ++ci)
			holder.push_back((typename HOL::value_type) *ci);

		extractor.SetResult(holder, pvt);
	}
};

struct BooleanExtractor
{
	typedef EventBase::PropertyValueType::BooleanVector HOLDER;

	BooleanExtractor(LuaInstance& _L) : L(_L)									{ }
	HOLDER::value_type Extract()												{ return L.toboolean(-1); }
	void SetResult(const HOLDER& holder, EventBase::PropertyValueType& result)	{ result.SetBooleanVector(holder); }

	LuaInstance& L;
};

template<typename CT>
struct NumberExtractorT
{
	typedef CT HOLDER;
	typedef typename CT::value_type ELEMTYPE;

	NumberExtractorT(LuaInstance& _L) : L(_L)			{ }
	ELEMTYPE Extract()									{ return (ELEMTYPE) L.tonumber(-1); }

	void SetResult(const EventBase::PropertyValueType::Int8Vector& holder, EventBase::PropertyValueType& result)
	{ result.SetInt8Vector(holder); }
	void SetResult(const EventBase::PropertyValueType::UInt8Vector& holder, EventBase::PropertyValueType& result)
	{ result.SetUInt8Vector(holder); }
	void SetResult(const EventBase::PropertyValueType::Int16Vector& holder, EventBase::PropertyValueType& result)
	{ result.SetInt16Vector(holder); }
	void SetResult(const EventBase::PropertyValueType::UInt16Vector& holder, EventBase::PropertyValueType& result)
	{ result.SetUInt16Vector(holder); }
	void SetResult(const EventBase::PropertyValueType::Int32Vector& holder, EventBase::PropertyValueType& result)
	{ result.SetInt32Vector(holder); }
	void SetResult(const EventBase::PropertyValueType::UInt32Vector& holder, EventBase::PropertyValueType& result)
	{ result.SetUInt32Vector(holder); }
	void SetResult(const EventBase::PropertyValueType::SingleFloatVector& holder, EventBase::PropertyValueType& result)
	{ result.SetSingleFloatVector(holder); }
	void SetResult(const EventBase::PropertyValueType::DoubleFloatVector& holder, EventBase::PropertyValueType& result)
	{ result.SetDoubleFloatVector(holder); }

	LuaInstance& L;
};

typedef NumberExtractorT<EventBase::PropertyValueType::DoubleFloatVector> NumberExtractor;

/*struct NumberExtractor
{
	typedef EventBase::PropertyValueType::DoubleFloatVector HOLDER;

	NumberExtractor(LuaInstance& _L) : L(_L)									{ }
	HOLDER::value_type Extract()												{ return L.tonumber(-1); }
	void SetResult(const HOLDER& holder, EventBase::PropertyValueType& result) 	{ result.SetDoubleFloatVector(holder); }

	LuaInstance& L;
};*/

struct StringExtractor
{
	typedef EventBase::PropertyValueType::StringVector HOLDER;

	StringExtractor(LuaInstance& _L) : L(_L)									{ }
	HOLDER::value_type Extract()												{ return *L.tolstring(-1); }
	void SetResult(const HOLDER& holder, EventBase::PropertyValueType& result)	{ result.SetStringVector(holder); }

	LuaInstance& L;
};

void LuaEventConversion::ConvertArrayTable(int stackOffset, PVT& result)
{
	LuaInstance::StackStateHolder holder(L);

	//ValidateArrayTable(stackOffset);

	if (!L.objlen(stackOffset))
	{
		PVT::DoubleFloatVector v;
		result.SetDoubleFloatVector(v);
	}
	else
	{
		L.rawgeti(stackOffset, 1);
		const int typeNum = L.type(-1);
		L.pop(1);

		switch(typeNum)
		{
		case LUA_TBOOLEAN:	ExtractArray<BooleanExtractor>(stackOffset, result, L);	break;
		case LUA_TNUMBER:	ExtractArray<NumberExtractor>(stackOffset, result, L);	break;
		case LUA_TSTRING:	ExtractArray<StringExtractor>(stackOffset, result, L);	break;
		}
	}
}

IEventBase::Ptr LuaEventConversion::GetEvent(int stackOffset)
{
	// validates the structure of the event and throws if there is an error
	//ValidateEventStructure(stackOffset);
	IEventBase::Ptr result;

	// declaring this ensures that the lua stack will be restored on exit
	LuaInstance::StackStateHolder holder(L);

	// gets the event name from the table
	LuaInstance::StringPtr sptr;
	L.getfield(stackOffset, keyHeader);

	if (!L.istable(-1))	return result;

	L.getfield(-1, keyName);
	sptr = L.tolstring(-1);
	if (!sptr)	return result;
	const string eventName = *sptr;
	L.pop(1);

	// gets the event type (type name and domain name) from the table
	L.getfield(-1, keyTypeName);
	sptr = L.tolstring(-1);
	string eventTypeName;
	if (sptr)	eventTypeName = *sptr;
	L.pop(1);
	L.getfield(-1, keyDomainName);
	sptr = L.tolstring(-1);
	string eventDomainName;
	if (sptr)	eventDomainName = *sptr;
	L.pop(2);

	const EventType etype(eventTypeName, eventDomainName);

	// create the base event
	result.reset(new EventBase(eventName, etype, broker));

	// loop through the properties and create each item
	L.getfield(stackOffset, keyProperties);

	if (L.istable(-1))
	{
		L.pushnil();	// nil is used for the first key

		while(L.next(-2))
		{
			LuaInstance::StringPtr key = L.tostring(-2);
			PVTPtr pvtp = GetPropertyValue(-1);

			L.pop(1);	// leaves key on stack for use by "next"

			if (key && pvtp)
				result->SetProperty(*key, *pvtp);
		}
	}

	return result;
}

void LuaEventConversion::PushEvent(IEventBase& evt, const char* varName)
{
	L.newtable();					// outer table for the event

	// header = { name = event name, typename = event type name, domainname = event domain name }
	L.pushlstring(keyHeader);
	L.newtable();
	SetNamedStringField(keyName, evt.GetEventName());
	const EventType& etype = evt.GetEventType();
	SetNamedStringField(keyTypeName, etype.GetTypeName());
	SetNamedStringField(keyDomainName, etype.GetDomainName());
	L.settable(-3);

	L.pushlstring(keyProperties);
	L.newtable();

	IEventBase::PropertyNameArrayPtr properties = evt.GetPropertyNames();

	for (IEventBase::PropertyNameArrayType::const_iterator ci = properties->begin(); ci != properties->end(); ++ci)
	{
		LuaInstance::StackStateHolder loopHolder(L);
		bool ok = false;

		try
		{
			const IEventBase::PropertyValueType& pvt = evt.GetProperty(*ci, true);

			L.pushlstring(*ci);
			PushPropertyValue(pvt);
			ok = true;
		}
		catch(...)
		{
		}

		if (ok)
			L.settable(-3);
	}

	L.settable(-3);

	SetOptionalName(varName);
}

template<typename T>
void LuaEventConversion::PushArray(typename T::VECT& array, const char* varName)
{
	T t(L);
	unsigned idx = 0;

	L.newtable();

	for (typename T::VECT::const_iterator ci = array.begin(); ci != array.end(); ++ci)
	{
		t.push(*ci);
		L.rawseti(-2, ++idx);
	}

	SetOptionalName(varName);

}

struct PushArrayBoolean
{
	typedef vector<bool> VECT;
	PushArrayBoolean(LuaInstance& _L) : L(_L) { }
	void push(const VECT::value_type& val) { L.pushboolean(val); }
	LuaInstance& L;
};

struct PushArrayNumber
{
	typedef vector<LuaInstance::Number> VECT;
	PushArrayNumber(LuaInstance& _L) : L(_L) { }
	void push(const VECT::value_type& val) { L.pushnumber(val); }
	LuaInstance& L;
};

struct PushArrayString
{
	typedef vector<string> VECT;
	PushArrayString(LuaInstance& _L) : L(_L) { }
	void push(const VECT::value_type& val) { L.pushlstring(val); }
	LuaInstance& L;
};

void LuaEventConversion::PushPropertyValue(const PVT& pvt)
{
	enum { TYPE_BOOL, TYPE_NUM, TYPE_STR, TYPE_STRA, TYPE_NUMA, TYPE_BOOLA, TYPE_NIL } rType = TYPE_NIL;
	string str;
	Number num = 0;
	IEventBase::PropertyValueType::UInt8VectorPtr uivp;
	StringArray strA;
	NumberArray numA;
	BoolArray boolA;
	PVT::StorageType storageType = pvt.GetStorageType();

	switch(pvt.GetStorageType())
	{
	case PVT::ST_BOOLEAN:				rType = TYPE_BOOL; num = (Number) pvt.GetBoolean(); 					break;
	case PVT::ST_SINGLE_FLOAT:			rType = TYPE_NUM; num = (Number) pvt.GetSingleFloat();					break;
	case PVT::ST_DOUBLE_FLOAT:			rType = TYPE_NUM; num = (Number) pvt.GetDoubleFloat();					break;
	case PVT::ST_INT16:					rType = TYPE_NUM; num = (Number) pvt.GetInt16(); 						break;
	case PVT::ST_INT32:					rType = TYPE_NUM; num = (Number) pvt.GetInt32(); 						break;
	case PVT::ST_INT8:					rType = TYPE_NUM; num = (Number) pvt.GetInt8(); 						break;
	case PVT::ST_UINT16:				rType = TYPE_NUM; num = (Number) pvt.GetUInt16(); 						break;
	case PVT::ST_UINT32:				rType = TYPE_NUM; num = (Number) pvt.GetUInt32(); 						break;
	case PVT::ST_UINT8:					rType = TYPE_NUM; num = (Number) pvt.GetUInt8(); 						break;
	case PVT::ST_STRING:				rType = TYPE_STR; str = pvt.GetString();								break;
	case PVT::ST_BOOLEAN_VECTOR:		if (ConvertArray(pvt.GetBooleanVector(), boolA)) rType = TYPE_BOOLA;	break;
	case PVT::ST_UINT8_VECTOR:			if (ConvertArray(pvt.GetUInt8Vector(), numA)) rType = TYPE_NUMA;		break;
	case PVT::ST_INT8_VECTOR:			if (ConvertArray(pvt.GetInt8Vector(), numA)) rType = TYPE_NUMA;			break;
	case PVT::ST_UINT16_VECTOR:			if (ConvertArray(pvt.GetUInt16Vector(), numA)) rType = TYPE_NUMA;		break;
	case PVT::ST_INT16_VECTOR:			if (ConvertArray(pvt.GetInt16Vector(), numA)) rType = TYPE_NUMA;		break;
	case PVT::ST_UINT32_VECTOR:			if (ConvertArray(pvt.GetUInt32Vector(), numA)) rType = TYPE_NUMA;		break;
	case PVT::ST_INT32_VECTOR:			if (ConvertArray(pvt.GetInt32Vector(), numA)) rType = TYPE_NUMA;		break;
	case PVT::ST_SINGLE_FLOAT_VECTOR:	if (ConvertArray(pvt.GetSingleFloatVector(), numA)) rType = TYPE_NUMA;	break;
	case PVT::ST_DOUBLE_FLOAT_VECTOR:	if (ConvertArray(pvt.GetDoubleFloatVector(), numA)) rType = TYPE_NUMA;	break;
	case PVT::ST_STRING_VECTOR:			if (ConvertArray(pvt.GetStringVector(), strA)) rType = TYPE_STRA;		break;

	case PVT::ST_NULL:
	case PVT::ST_UNSUPPORTED:
	case PVT::ST_VECTOR:
	case PVT::ST_TYPE_MASK:

		break;
	}

	L.newtable();
	L.pushnumber(1);

	switch(rType)
	{
	case TYPE_BOOL:

		L.pushboolean(!!num);
		break;
	case TYPE_NUM:
		L.pushnumber(num);
		break;
	case TYPE_STR:
		L.pushlstring(str);
		break;
	case TYPE_STRA:
		PushArray<PushArrayString>(strA);
		break;
	case TYPE_NUMA:
		PushArray<PushArrayNumber>(numA);
		break;
	case TYPE_BOOLA:
		PushArray<PushArrayBoolean>(boolA);
		break;
	case TYPE_NIL:
	default:
		L.pushnil();
		storageType = PVT::ST_NULL;
		break;
	}

	L.settable(-3);
	L.pushnumber(2);
	L.pushlstring(PVT::ToString(storageType));
	L.settable(-3);
}

void LuaEventConversion::PushProperties(IEventBase& evt, const char* varName)
{
	IEventBase::PropertyNameArrayPtr names = evt.GetPropertyNames();
	const int beginIndex = L.gettop();

	L.newtable();

	if (names)
		for (IEventBase::PropertyNameArrayType::const_iterator ci = names->begin(); ci != names->end(); ++ci)
		{
			const IEventBase::PropertyNameType propName = *ci;
			enum { TYPE_BOOL, TYPE_NUM, TYPE_STR, TYPE_STRA, TYPE_NUMA, TYPE_BOOLA, TYPE_NIL } rType = TYPE_NIL;
			string str;
			Number num = 0;
			IEventBase::PropertyValueType::UInt8VectorPtr uivp;
			StringArray strA;
			NumberArray numA;
			BoolArray boolA;

			try
			{
				IEventBase::PropertyValueType propVal = evt.GetProperty(propName);

				switch(propVal.GetStorageType())
				{
				case PVT::ST_BOOLEAN:				rType = TYPE_BOOL; num = (Number) propVal.GetBoolean(); 				break;
				case PVT::ST_SINGLE_FLOAT:			rType = TYPE_NUM; num = (Number) propVal.GetSingleFloat();				break;
				case PVT::ST_DOUBLE_FLOAT:			rType = TYPE_NUM; num = (Number) propVal.GetDoubleFloat();				break;
				case PVT::ST_INT16:					rType = TYPE_NUM; num = (Number) propVal.GetInt16(); 					break;
				case PVT::ST_INT32:					rType = TYPE_NUM; num = (Number) propVal.GetInt32(); 					break;
				case PVT::ST_INT8:					rType = TYPE_NUM; num = (Number) propVal.GetInt8(); 					break;
				case PVT::ST_UINT16:				rType = TYPE_NUM; num = (Number) propVal.GetUInt16(); 					break;
				case PVT::ST_UINT32:				rType = TYPE_NUM; num = (Number) propVal.GetUInt32(); 					break;
				case PVT::ST_UINT8:					rType = TYPE_NUM; num = (Number) propVal.GetUInt8(); 					break;
				case PVT::ST_STRING:				rType = TYPE_NUM; str = propVal.GetString();							break;
				case PVT::ST_BOOLEAN_VECTOR:		if (ConvertArray(propVal.GetBooleanVector(), boolA)) rType = TYPE_BOOLA; break;
				case PVT::ST_UINT8_VECTOR:			if (ConvertStr(propVal.GetUInt8Vector(), str)) rType = TYPE_STR;		break;
				case PVT::ST_INT8_VECTOR:			if (ConvertArray(propVal.GetInt8Vector(), numA)) rType = TYPE_NUMA;		break;
				case PVT::ST_UINT16_VECTOR:			if (ConvertArray(propVal.GetUInt16Vector(), numA)) rType = TYPE_NUMA;	break;
				case PVT::ST_INT16_VECTOR:			if (ConvertArray(propVal.GetInt16Vector(), numA)) rType = TYPE_NUMA;		break;
				case PVT::ST_UINT32_VECTOR:			if (ConvertArray(propVal.GetUInt32Vector(), numA)) rType = TYPE_NUMA;	break;
				case PVT::ST_INT32_VECTOR:			if (ConvertArray(propVal.GetInt32Vector(), numA)) rType = TYPE_NUMA;		break;
				case PVT::ST_SINGLE_FLOAT_VECTOR:	if (ConvertArray(propVal.GetSingleFloatVector(), numA)) rType = TYPE_NUMA; break;
				case PVT::ST_DOUBLE_FLOAT_VECTOR:	if (ConvertArray(propVal.GetDoubleFloatVector(), numA)) rType = TYPE_NUMA; break;
				case PVT::ST_STRING_VECTOR:			if (ConvertArray(propVal.GetStringVector(), strA)) rType = TYPE_STRA;	break;

				case PVT::ST_NULL:
				case PVT::ST_UNSUPPORTED:
				case PVT::ST_VECTOR:
				case PVT::ST_TYPE_MASK:

					break;
				}

				L.pushlstring(propName);

				switch(rType)
				{
				case TYPE_BOOL:	L.pushboolean(!!num);					break;
				case TYPE_NUM:	L.pushnumber(num);						break;
				case TYPE_STR:	L.pushlstring(str);						break;
				case TYPE_STRA:	PushArray<PushArrayString>(strA);		break;
				case TYPE_NUMA:	PushArray<PushArrayNumber>(numA);		break;
				case TYPE_BOOLA:PushArray<PushArrayBoolean>(boolA);		break;

				case TYPE_NIL:
				default:		L.pushnil();							break;
				}

				L.settable(-3);
			}
			catch(...)
			{
				L.settop(beginIndex);
				throw;
			}
		}

	SetOptionalName(varName);
}

void LuaEventConversion::SetNamedStringField(const string& fieldName, string strVal, int stackOffset)
{
	L.pushlstring(fieldName);
	L.pushlstring(strVal);
	L.settable(stackOffset);
}

void LuaEventConversion::SetNamedNumericField(const string& fieldName, double numVal, int stackOffset)
{
	L.pushlstring(fieldName);
	L.pushnumber(numVal);
	L.settable(stackOffset);
}

void LuaEventConversion::SetNamedPropertiesField(const string& fieldName, IEventBase& evt, int stackOffset)
{
	const int beginIndex = L.gettop();

	try
	{
		L.pushlstring(fieldName);
		PushProperties(evt);
		L.settable(stackOffset);
	}
	catch(...)
	{
		L.settop(beginIndex);
		throw;
	}

}

void LuaEventConversion::SetOptionalName(const char* varName)
{
	if (varName && *varName)
		L.setglobal(varName);
}

inline void CheckedGetField(LuaInstance& L, int stackOffset, const string& fieldName)
{
	const int beginIndex = L.gettop();

	L.getfield(stackOffset, fieldName);

	if (L.gettop() == beginIndex)
	{
		stringstream msg;
		msg << "CheckedGetField(" << fieldName << ") did not return anything";
		string m = msg.str();
		throw runtime_error(m);
	}
}

LuaEventConversion::PVTPtr LuaEventConversion::GetBasicPropertyValue(int stackOffset)
{
	LuaInstance::StackStateHolder holder(L);
	PVTPtr result;
	result.reset(new PVT(broker));

	if (L.isnumber(stackOffset))
		result->SetDoubleFloat(L.tonumber(stackOffset));

	else if (L.isstring(stackOffset))
		result->SetString(*L.tolstring(stackOffset));

	else if (L.isboolean(stackOffset))
		result->SetBoolean(L.toboolean(stackOffset));

	else if (L.isnil(stackOffset))
		; // nil by default

	else if (L.istable(stackOffset))
		ConvertArrayTable(stackOffset, *result);

	else
	{
		stringstream msg;
		msg << "GetBasicPropertyValue: invalid type '" << L._typename(L.type(stackOffset));
		string m = msg.str();
		throw runtime_error(m);
	}

	return result;
}

LuaEventConversion::PVTPtr LuaEventConversion::GetCoercedPropertyValue(int stackOffset, PVT::StorageType targetType)
{
	LuaInstance::StackStateHolder holder(L);
	PVTPtr result;
	result.reset(new PVT(broker));

	if (L.isnumber(stackOffset))
	{
		const double val = L.tonumber(stackOffset);
		stringstream msg;
		string m;

		switch(targetType)
		{
		case PVT::ST_UINT8:			result->SetUInt8((PVT::UInt8) val);					break;
		case PVT::ST_INT8:			result->SetInt8((PVT::Int8) val);					break;
		case PVT::ST_UINT16:		result->SetUInt16((PVT::UInt16) val);				break;
		case PVT::ST_INT16:			result->SetInt16((PVT::Int16) val);					break;
		case PVT::ST_UINT32:		result->SetUInt32((PVT::UInt32) val);				break;
		case PVT::ST_INT32:			result->SetInt32((PVT::Int32) val);					break;
		case PVT::ST_SINGLE_FLOAT:	result->SetSingleFloat((PVT::SingleFloat) val);		break;
		case PVT::ST_DOUBLE_FLOAT:	result->SetDoubleFloat((PVT::DoubleFloat) val);		break;
		case PVT::ST_STRING:		msg << val; m = msg.str(); result->SetString(m);	break;
		case PVT::ST_BOOLEAN:		result->SetBoolean((PVT::Boolean) !!val);			break;

		default:

			msg << "GetCoercedPropertyValue: cannot represent a number as " << PVT::ToString(targetType);
			m = msg.str();
			throw runtime_error(m);
		}
	}

	else if (L.isstring(stackOffset))
	{
		const string val = *L.tolstring(stackOffset);

		switch(targetType)
		{
		case PVT::ST_STRING:

			result->SetString(val);
			break;

		case PVT::ST_UINT8_VECTOR:
			{
				const PVT::UInt8* p = (const PVT::UInt8*) val.data();
				PVT::UInt8Vector v(p, p+val.length());

				result->SetUInt8Vector(v);
				break;
			}

		case PVT::ST_INT8_VECTOR:
			{
				const PVT::Int8* p = (const PVT::Int8*) val.data();
				PVT::Int8Vector v(p, p+val.length());

				result->SetInt8Vector(v);
				break;
			}

		default:
			{
				stringstream msg;
				msg << "GetCoercedPropertyValue: cannot represent a string as " << PVT::ToString(targetType);
				string m = msg.str();
				throw runtime_error(m);
			}

		}
	}

	else if (L.isboolean(stackOffset))
	{
		const bool val = L.toboolean(stackOffset);
		const double n = !!val;
		stringstream msg;
		string m;

		switch(targetType)
		{
		case PVT::ST_UINT8:			result->SetUInt8((PVT::UInt8) n);					break;
		case PVT::ST_INT8:			result->SetInt8((PVT::Int8) n);						break;
		case PVT::ST_UINT16:		result->SetUInt16((PVT::UInt16) n);					break;
		case PVT::ST_INT16:			result->SetInt16((PVT::Int16) n);					break;
		case PVT::ST_UINT32:		result->SetUInt32((PVT::UInt32) n);					break;
		case PVT::ST_INT32:			result->SetInt32((PVT::Int32) n);					break;
		case PVT::ST_SINGLE_FLOAT:	result->SetSingleFloat((PVT::SingleFloat) n);		break;
		case PVT::ST_DOUBLE_FLOAT:	result->SetDoubleFloat((PVT::DoubleFloat) n);		break;
		case PVT::ST_STRING:		msg << n; m = msg.str(); result->SetString(m);		break;
		case PVT::ST_BOOLEAN:		result->SetBoolean(val);							break;

		default:

			msg << "GetCoercedPropertyValue: cannot represent a boolean as " << PVT::ToString(targetType);
			m = msg.str();
			throw runtime_error(m);
		}
	}

	else if (L.isnil(stackOffset))
	{
		if (targetType != PVT::ST_NULL)
		{
			stringstream msg;
			msg << "GetCoercedPropertyValue: cannot represent nil as " << PVT::ToString(targetType);
			string m = msg.str();
			throw runtime_error(m);
		}
	}

	else if (L.istable(stackOffset))
	{
		PVTPtr temp;

		temp.reset(new PVT(broker));
		ConvertArrayTable(stackOffset, *temp);

		const PVT::StorageType srcType = temp->GetStorageType();

		if (targetType == srcType)
			result = temp;

		else if ((targetType == PVT::ST_STRING) && (srcType == PVT::ST_UINT8_VECTOR))
		{
			PVT::UInt8VectorPtr s = temp->GetUInt8Vector();
			PVT::String t((const char*) &s->front(), s->size());
			result->SetString(t);
		}
		else if ((targetType == PVT::ST_STRING) && (srcType == PVT::ST_INT8_VECTOR))
		{
			PVT::Int8VectorPtr s = temp->GetInt8Vector();
			PVT::String t((const char*) &s->front(), s->size());
			result->SetString(t);
		}
		else if (srcType == PVT::ST_DOUBLE_FLOAT_VECTOR)
		{
			PVT::DoubleFloatVectorPtr s = temp->GetDoubleFloatVector();

			switch(targetType)
			{
			case PVT::ST_UINT8_VECTOR:			TargetArray<NumberExtractorT<PVT::UInt8Vector> >(*s, *result, L);		break;
			case PVT::ST_INT8_VECTOR:			TargetArray<NumberExtractorT<PVT::Int8Vector> >(*s, *result, L);		break;
			case PVT::ST_UINT16_VECTOR:			TargetArray<NumberExtractorT<PVT::UInt16Vector> >(*s, *result, L);		break;
			case PVT::ST_INT16_VECTOR:			TargetArray<NumberExtractorT<PVT::Int16Vector> >(*s, *result, L);		break;
			case PVT::ST_UINT32_VECTOR:			TargetArray<NumberExtractorT<PVT::UInt32Vector> >(*s, *result, L);		break;
			case PVT::ST_INT32_VECTOR:			TargetArray<NumberExtractorT<PVT::Int32Vector> >(*s, *result, L);		break;
			case PVT::ST_SINGLE_FLOAT_VECTOR:	TargetArray<NumberExtractorT<PVT::SingleFloatVector> >(*s, *result, L);	break;
			//case PVT::ST_DOUBLE_FLOAT_VECTOR:	result = temp;	break;
			case PVT::ST_BOOLEAN_VECTOR:		TargetArray<BooleanExtractor>(*s, *result, L);							break;

			default:
			{
				stringstream msg;
				msg << "GetCoercedPropertyValue: cannot convert " << PVT::ToString(srcType) << " to " << PVT::ToString(targetType);
				string m;
				throw runtime_error(m);
			}

			}
		}
		else if (srcType == PVT::ST_BOOLEAN_VECTOR)
		{
			PVT::BooleanVectorPtr s = temp->GetBooleanVector();

			switch(targetType)
			{
			case PVT::ST_UINT8_VECTOR:			TargetArray<NumberExtractorT<PVT::UInt8Vector> >(*s, *result, L);		break;
			case PVT::ST_INT8_VECTOR:			TargetArray<NumberExtractorT<PVT::Int8Vector> >(*s, *result, L);		break;
			case PVT::ST_UINT16_VECTOR:			TargetArray<NumberExtractorT<PVT::UInt16Vector> >(*s, *result, L);		break;
			case PVT::ST_INT16_VECTOR:			TargetArray<NumberExtractorT<PVT::Int16Vector> >(*s, *result, L);		break;
			case PVT::ST_UINT32_VECTOR:			TargetArray<NumberExtractorT<PVT::UInt32Vector> >(*s, *result, L);		break;
			case PVT::ST_INT32_VECTOR:			TargetArray<NumberExtractorT<PVT::Int32Vector> >(*s, *result, L);		break;
			case PVT::ST_SINGLE_FLOAT_VECTOR:	TargetArray<NumberExtractorT<PVT::SingleFloatVector> >(*s, *result, L);	break;
			case PVT::ST_DOUBLE_FLOAT_VECTOR:	TargetArray<NumberExtractorT<PVT::DoubleFloatVector> >(*s, *result, L);	break;
			//case PVT::ST_BOOLEAN_VECTOR:		result = temp;													break;

			default:
			{
				stringstream msg;
				msg << "GetCoercedPropertyValue: cannot convert " << PVT::ToString(srcType) << " to " << PVT::ToString(targetType);
				string m;
				throw runtime_error(m);
			}

			}
		}
		else
		{
			stringstream msg;
			msg << "GetCoercedPropertyValue: cannot convert " << PVT::ToString(srcType) << " to " << PVT::ToString(targetType);
			string m;
			throw runtime_error(m);
		}
	}
	else
	{
		stringstream msg;
		msg << "GetCoercedPropertyValue: invalid type '" << L._typename(L.type(stackOffset)) << "'";
		string m = msg.str();
		throw runtime_error(m);
	}

	return result;
}

// conversion cases:
//
//		number
//		string
//		boolean
//		nil
//		{ number }
//		{ string }
//		{ boolean }
//		{ nil }
//		{ string, numeric- or boolean-vector type }
//		{ number, numeric-type or string or boolean }
//		{ nil, "null" }
//		{ { string, string, ... } }  -- string vector
//		{ { number, number, ... } }  -- number vector
//		{ { number, number, ... }, numeric- or boolean-vector or string type }

LuaEventConversion::PVTPtr LuaEventConversion::GetPropertyValue(int stackOffset)
{
	LuaInstance::StackStateHolder holder(L);
	PVTPtr result;

	if (L.istable(stackOffset))
	{
		const size_t tableSize = L.objlen(stackOffset);

		if (tableSize == 1)
		{
			LuaInstance::StackStateHolder holder0(L);
			L.pushnumber(1);
			L.gettable(Offset(stackOffset,-1));

			result = GetBasicPropertyValue(-1);
		}
		else if (!tableSize)
		{
			PVT::DoubleFloatVector v;

			result.reset(new PVT(broker));
			result->SetDoubleFloatVector(v);
		}
		else if (tableSize == 2)
		{
			LuaInstance::StackStateHolder holder0(L);
			L.pushnumber(2);
			L.gettable(Offset(stackOffset,-1));

			LuaInstance::StringPtr sp = L.tostring(-1);

			if (!sp)
			{
				stringstream msg;
				msg << "GetPropertyValue: unable to convert second element of table to string";
				string m = msg.str();
				throw runtime_error(m);
			}
			else
			{
				PVT::StorageType storageType;

				try
				{
					const string coerceTypeString = *sp;
					PVT::FromString(coerceTypeString, storageType);
				}
				catch(exception& ex)
				{
					stringstream msg;
					msg << "GetPropertyValue: invalid type name (" << ex.what() << ")";
					string m = msg.str();
					throw runtime_error(m);
				}

				L.pop(1);
				L.pushnumber(1);
				L.gettable(Offset(stackOffset,-1));
				result = GetCoercedPropertyValue(-1, storageType);
			}
		}
		else
		{
			stringstream msg;
			msg << "GetPropertyValue: invalid table size of " << tableSize;
			string m = msg.str();
			throw runtime_error(m);
		}
	}
	else
		result = GetBasicPropertyValue(stackOffset);

	return result;
}


}
}
