/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: xbVariant
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<12/02/2014: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbVariant.h"
#include "xbStreamItf.h"
#include "xbDnItf.h"

#pragma warning(push)
#pragma warning(disable: 4311) //troncation de pointeur de 'wchar *' à 'xbNptr'
void	xbVariantClear( VARIANT* var )
{
	switch( var->vt )
	{
	case	VT_ARRAY:
		var->vt		= VT_EMPTY;
		HRtoE(::SafeArrayDestroy(var->parray));
		return;

	case	VT_BOOL:
	case	VT_I1:
	case	VT_UI1:
	case	VT_I2:
	case	VT_UI2:
	case	VT_INT:
	case	VT_UINT:
	case	VT_I4:
	case	VT_UI4:
	case	VT_R4:
	case	VT_R8:
	case	VT_CY:
	case	VT_DATE:
	case	VT_VOID:
	case	VT_HRESULT:
	case	VT_FILETIME:
		var->vt	= VT_EMPTY;
		return;
	case	VT_EMPTY:
		return;	
	default:
		HRtoE(::VariantClear(var));
	}
}


void	xbVariantCopy( VARIANT* dst, const VARIANT* src )
{
	if( dst==src ) return;

	switch( src->vt )
	{
	case	VT_ARRAY:
		xbVariantClear( dst );
		dst->vt		= VT_ARRAY;
		dst->parray = NULL;
		HRtoE(SafeArrayCopy( src->parray, &dst->parray ));
		return;

	case	VT_BOOL:
	case	VT_I1:
	case	VT_UI1:
	case	VT_I2:
	case	VT_UI2:
	case	VT_INT:
	case	VT_UINT:
	case	VT_I4:
	case	VT_UI4:
	case	VT_R4:
	case	VT_R8:
	case	VT_CY:
	case	VT_DATE:
	case	VT_VOID:
	case	VT_HRESULT:
	case	VT_EMPTY:
	case	VT_FILETIME:
		xbVariantClear( dst );
		dst->vt		= src->vt;
		memcpy( dst, src, sizeof(VARIANT) );
		return;
	
	default:
		HRtoE(::VariantCopy( dst, (VARIANT*)src ));
	}
}

INT xbVariantCmp(const VARIANT* pOp1, const VARIANT* pOp2,bool can_convert,LCID lcid)
{
	if( pOp1==pOp2 ) 
	{
		return 0;
	}

	// Variants not equal if types don't match
	VARIANT	var;
	var.vt = VT_EMPTY;
	if( pOp1->vt!=pOp2->vt ) 
	{
		if (!can_convert) return pOp1->vt - pOp2->vt;
		xbVariantChangeType(&var,pOp2,pOp1->vt,lcid);
		pOp2 = &var;
		xbAssert(pOp1->vt==pOp2->vt);
	}

	long ret;
	try
	{
	// Check type specific values
	switch( pOp1->vt ) 
	{
		case VT_EMPTY:
		case VT_NULL:
			ret = 0;
			break;

		case VT_I2:
			ret = V_I2(pOp1) - V_I2(pOp2);
			break;

		case VT_I4:
			ret = V_I4(pOp1) - V_I4(pOp2);
			break;

		case VT_DATE:		
		case VT_R4:
		case VT_R8:
			double r;
			if (pOp1->vt==VT_R4) r = V_R4(pOp1) - V_R4(pOp2);
			else if (pOp1->vt==VT_R8) r = V_R8(pOp1) - V_R8(pOp2);
			else r = V_DATE(pOp1) - V_DATE(pOp2);
			if (r == 0) ret = 0;
			else if (r > 0) ret = 1;
			else ret = -1;
			break;

		case VT_CY:
			ret = memcmp( &(V_CY(pOp1)), &(V_CY(pOp2)), sizeof(CY) );
			break;
		case VT_BSTR:		
//			ret = (::SysStringByteLen(V_BSTR(pOp1))==::SysStringByteLen(V_BSTR(pOp2))) && (memcmp(V_BSTR(pOp1), V_BSTR(pOp2), ::SysStringByteLen(V_BSTR(pOp1))==0);
			ret = wcscmp(V_BSTR(pOp1),V_BSTR(pOp2));
			break;
		case VT_ERROR:
			ret = V_ERROR(pOp1) - V_ERROR(pOp2);
			break;

		case VT_BOOL:		
			if ((V_BOOL(pOp1) && V_BOOL(pOp2)) || (!V_BOOL(pOp1) && !V_BOOL(pOp2))) ret = 0;
			else if (V_BOOL(pOp1)) ret = 1;
			else ret = -1;
			break;

		case VT_DECIMAL:
			ret = memcmp(&(V_DECIMAL(pOp1)), &(V_DECIMAL(pOp2)), sizeof(DECIMAL));
			break;

		case VT_UI1:		
			ret = V_UI1(pOp1) - V_UI1(pOp2);
			break;

		case VT_DISPATCH:	
			if (V_DISPATCH(pOp1) == V_DISPATCH(pOp2)) ret = 0;
			else if (V_DISPATCH(pOp1) > V_DISPATCH(pOp2)) ret = 1;
			else ret = -1;
			break;

		case VT_UNKNOWN:
			if (V_UNKNOWN(pOp1) == V_UNKNOWN(pOp2)) ret = 0;
			else if (V_UNKNOWN(pOp1) > V_UNKNOWN(pOp2)) ret = 1;
			else ret = -1;
			break;

		default:
			xbThrowEsys(E_NOTIMPL,"xbVariantCmp(const VARIANT* pOp1, const VARIANT* pOp2,bool can_convert) : unsupported VARIANT type !");
	}
	}
	catch(...)
	{
		eProtect(if (var.vt != VT_EMPTY) xbVariantClear(&var));
		throw;
	}
	if (var.vt != VT_EMPTY) xbVariantClear(&var);

	return	ret;
}

long xbVariant::GetData(xbData *pData) const
{
	if (!IsData()) xbThrowEsys(E_NOTIMPL,"xbVariant::GetData() : VARIANT is not an array of bytes !");
	
	//Retrieve size of array
	int BufLen = m_var.parray->rgsabound[0].cElements;
	
	if(pData)
	{
		void *pArrayData = NULL;
		
		//Obtain safe pointer to the array
		HRtoE(SafeArrayAccessData(m_var.parray,&pArrayData));
		
		//Retreive data from variant
		pData->SetData(pArrayData,BufLen);
		
		//Unlock the variant data
		SafeArrayUnaccessData(m_var.parray);
	}
	
	return BufLen;
}

void xbVariant::SetData(const void *pData,int cByteData)
{
	Clear();
	if (cByteData <= 0)
		return;
	
	//Set the type to an array of unsigned chars (OLE SAFEARRAY)
	m_var.vt = VT_ARRAY | VT_UI1;
	
	//Set up the bounds structure
	SAFEARRAYBOUND  rgsabound[1];
	
	rgsabound[0].cElements = cByteData;
	rgsabound[0].lLbound = 0;
	
	//Create an OLE SAFEARRAY
	m_var.parray = ::SafeArrayCreate(VT_UI1,1,rgsabound);
	if (!m_var.parray)
	{
		m_var.vt = VT_EMPTY;
		xbThrowEsys("xbVariant::SetData() : array of bytes creation failure !");	
	}

	try
	{
		void *pArrayData = NULL;
		
		//Get a safe pointer to the array
		HRtoE(SafeArrayAccessData(m_var.parray,&pArrayData));
		
		//Copy data in safe array
		memcpy(pArrayData, pData, cByteData);
		
		//Unlock the variant data
		SafeArrayUnaccessData(m_var.parray);
	}
	catch(...)
	{
		eProtect(Empty());
		throw;
	}
}

xbComData::xbComData(ulong sz)
{
	m_pMem = (wchar*)::CoTaskMemAlloc(sz);
	if (!m_pMem) xbThrowEsys(E_OUTOFMEMORY,"xbComData::xbComData(ulong sz) : not enough memory");
	m_sz = sz;
}

xbComData::~xbComData()
{
	if (m_pMem) ::CoTaskMemFree(m_pMem);
}

wchar	*xbComData::Detach()
{
	if (!m_pMem) xbThrowEsys("xbComData::Detach() : no associated memory bloc");
	wchar *ptr = m_pMem;
	m_pMem = NULL;
	m_sz = 0;
	return ptr;
}

xbComData	&xbComData::Attach(wchar *ptr,ulong sz)
{
	if (m_pMem) ::CoTaskMemFree(m_pMem);
	m_pMem = ptr;
	m_sz = sz;
	return *this;
}

xbComData::operator wchar*()
{
	if (!m_pMem) xbThrowEsys("xbComData::operator wchar*() : no associated memory bloc");
	return m_pMem;
}

xbComData::operator uchar*()
{
	if (!m_pMem) xbThrowEsys("xbComData::operator uchar*() : no associated memory bloc");
	return (uchar*)m_pMem;
}

ulong xbComData::SetLength(ulong sz)
{
	if (sz == m_sz) return sz;
	wchar *ptr;
	if (sz == 0)
	{
		if (m_pMem) ::CoTaskMemFree(m_pMem);
		ptr = NULL;
	}
	else
	{
		if (m_pMem) ptr= (wchar*)::CoTaskMemRealloc(m_pMem,sz);
		else ptr = (wchar*)::CoTaskMemAlloc(sz);
		if (!ptr) xbThrowEsys(E_OUTOFMEMORY,"xbComData::xbComData(ulong sz) : not enough memory");
	}
	m_pMem = ptr;
	m_sz = sz;
	return sz;
}

ulong			xbComData::GetLength() const
{
	return m_sz;
}

xbComData	&xbComData::SetString(const char *ptr)
{
	long ret = long(::mbstowcs(NULL,ptr,0));
	if (ret < 0) xbThrowEsys("xbComData::SetCString(char *ptr) : UNICODE conversion failure (1) !");
	if (ret == 0) return *this;
	SetLength(((ulong)ret+1)*sizeof(wchar));
	ret = long(::mbstowcs(m_pMem,ptr,m_sz));
	m_pMem[ret] = 0;
	if (ret < 0) xbThrowEsys("xbComData::SetCString(char *ptr) : UNICODE conversion failure (2) !");
	return *this;
}

xbComData	&xbComData::SetString(const wchar *ptr)
{
	long ret = long(wcslen(ptr));
	SetLength(((ulong)ret+1)*sizeof(wchar));
	wcscpy(m_pMem,ptr);
	return *this;
}

xbString	xbComData::GetString() const
{
	if (!m_pMem) xbThrowEsys("xbComData::GetString() : empty");
	long ret = long(::wcstombs(NULL,m_pMem,0));
	if (ret == 0) return (char*)NULL;
	if (ret < 0) xbThrowEsys("xbComData::GetString() : unicode conversion failure (length) !");
	xbData buf;
	char *ptr = (char*)buf.GetWritePtr(ret+1);
	ret = long(::wcstombs(ptr,m_pMem,ret));
	if (ret < 0) xbThrowEsys("xbComData::GetString() : unicode conversion failure (cvt) !");
	ptr[ret] = 0;
	return ptr;
}

void xbVariant::ReadFromStream(IxbStreamMode *pMode,IxbInputStream *pStream)
{
	xbVariant var;
	switch(pMode?pMode->GetFormat():xbFormatBin)
	{
	case xbFormatBin :
		{
			long type = pStream->ReadLong();
			switch(type)
			{
			case VT_NULL:
				var.m_var.vt = VT_NULL;
			case VT_EMPTY:
				break;
			case VT_UI1:		
				var = pStream->ReadUchar();
				break;
			case VT_I1:		
				var.AsChar = pStream->ReadChar();
				break;
			case (VT_UI1|VT_ARRAY):
				{
					int lg = pStream->ReadLong();
					if (lg <= 0)
						break;
					xbData data;
					data.Length = lg;
					pStream->Read((LPVOID)(LPCVOID)data,lg);
					var.SetData(data);
				}
				break;
			case VT_I2:
				var = pStream->ReadShort();
				break;
			case VT_UI2:
				var.AsUshort = pStream->ReadUshort();
				break;

			case VT_I4:
				var = pStream->ReadLong();
				break;
			case VT_UI4:
				var.AsUlong = pStream->ReadUlong();
				break;

			case VT_DATE:		
				var.m_var.vt = VT_DATE;
				pStream->Read(&V_DATE(&var.m_var),sizeof(V_DATE(&var.m_var)));
				break;
			case VT_R4:
				var.m_var.vt = VT_R4;
				pStream->Read(&V_R4(&var.m_var),sizeof(V_R4(&var.m_var)));
				break;
			case VT_R8:
				var.m_var.vt = VT_R8;
				pStream->Read(&V_R8(&var.m_var),sizeof(V_R8(&var.m_var)));
				break;
			case VT_CY:
				var.m_var.vt = VT_CY;
				pStream->Read(&V_CY(&var.m_var),sizeof(V_CY(&var.m_var)));
				break;
			case VT_BSTR:		
				{
					int lg = pStream->ReadLong();
					if (lg <= 0)
						break;
					xbBSTR str(xbAllocSys);
					pStream->Read(str.GetWritePtr(lg),lg * sizeof(wchar));
					var = str;
				}
				break;
			case VT_ERROR:
				var.m_var.vt = VT_ERROR;
				pStream->Read(&V_ERROR(&var.m_var),sizeof(V_ERROR(&var.m_var)));
				break;

			case VT_BOOL:		
				var.m_var.vt = VT_BOOL;
				pStream->Read(&V_BOOL(&var.m_var),sizeof(V_BOOL(&var.m_var)));
				break;

			case VT_DECIMAL:
				var.m_var.vt = VT_DECIMAL;
				pStream->Read(&V_DECIMAL(&var.m_var),sizeof(V_DECIMAL(&var.m_var)));
				break;

			default:
				xbThrow xbEsys(E_NOTIMPL,"xbVariant::ReadFromStream() :  type not supported");
			}
		}
		break;
	case xbFormatText :
		{
			xbStringW str;
			pStream->ReadStringW(&str,NULL,pMode);
			var = (LPCWSTR)str;
		}
		break;
	default:
		xbThrow xbEsys(E_NOTIMPL,"xbVariant::ReadFromStream() :  format not supported");
	}
	*this = var;
}

void xbVariant::WriteToStream(IxbStreamMode *pMode,IxbOutputStream *pStream) const
{
	switch(pMode?pMode->GetFormat():xbFormatBin)
	{
	case xbFormatBin :
		{
			long type = m_var.vt;
			pStream->WriteLong(type);
			switch(type)
			{
			case VT_EMPTY:
			case VT_NULL:
				break;
			case VT_I1:		
				pStream->Write(&V_I1(&m_var),sizeof(V_I1(&m_var)));
				break;
			case VT_UI1:		
				pStream->Write(&V_UI1(&m_var),sizeof(V_UI1(&m_var)));
				break;
			case (VT_UI1|VT_ARRAY):
				{
					xbData data;
					GetData(&data);
					int lg = data.Length;
					pStream->WriteLong(lg);
					if (lg <= 0)
						break;
					pStream->Write(data,lg);
				}
				break;
			case VT_I2:
				pStream->WriteShort(V_I2(&m_var));
				break;
			case VT_UI2:
				pStream->WriteUshort(V_UI2(&m_var));
				break;

			case VT_I4:
				pStream->WriteLong(V_I4(&m_var));
				break;
			case VT_UI4:
				pStream->WriteUlong(V_UI4(&m_var));
				break;

			case VT_DATE:		
				pStream->Write(&V_DATE(&m_var),sizeof(V_DATE(&m_var)));
				break;
			case VT_R4:
				pStream->Write(&V_R4(&m_var),sizeof(V_R4(&m_var)));
				break;
			case VT_R8:
				pStream->Write(&V_R8(&m_var),sizeof(V_R8(&m_var)));
				break;
			case VT_CY:
				pStream->Write(&V_CY(&m_var),sizeof(V_CY(&m_var)));
				break;
			case VT_DECIMAL:
				pStream->Write(&V_DECIMAL(&m_var),sizeof(V_DECIMAL(&m_var)));
				break;
			case VT_BSTR:		
				{
					int lg = (m_var.bstrVal == NULL)? 0 : SysStringByteLen(m_var.bstrVal);
					pStream->WriteLong(lg);
					if (lg <= 0)
						break;
					pStream->Write(m_var.bstrVal,lg);
				}
				break;
			case VT_ERROR:
				pStream->Write(&V_ERROR(&m_var),sizeof(V_ERROR(&m_var)));
				break;

			case VT_BOOL:		
				pStream->Write(&V_BOOL(&m_var),sizeof(V_BOOL(&m_var)));
				break;

			default:
				xbThrow xbEsys(E_NOTIMPL,"xbVariant::WriteToStream() :  type not supported");
			}
		}
		break;
	case xbFormatText :
		pStream->WriteStringW(AsStringW,false,pMode);
		break;
	default:
		xbThrow xbEsys("xbVariant::WriteToStream() :  format not supported");
	}
}

void xbEXPORT xbVariantChangeType(VARIANT *pDest,const VARIANT *pSrce,VARTYPE vt,LCID lcid)
{ 
//	if (lcid == 0)
//		lcid = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); //= 1033
	HRtoE(::VariantChangeTypeEx(pDest, (VARIANT*)pSrce,lcid, 0, vt));
}


int	xbBSTR::GetToken(int start,const wchar *pSeps,xbBSTR *pStr,xbBSTR *pStrStaying,wchar *pChSep,
					   const wchar *pStringDelimiter,
					   const wchar *pDelimiterStart,const wchar *pDelimiterEnd,bool bSpcNet) const
{
	if (IsEmpty()) 
		return xbTokenError_End;

//	if (!bSpcNet)
//		bSpcNet = wcschr(pSeps,' ') != NULL; 

	const wchar *pStart = *this;
	int			lg = Length;

	if (start <= 0)
		xbThrow xbEsys(E_INVALIDARG,"xbBSTR::GetToken(start="+xbString(start)+") : bad start position");

	if (start >= lg) 
		return xbTokenError_End;
	if (start > 0)
		pStart += start;

	const wchar *pStringDelimiterEnd = NULL;
	if (pStringDelimiter)
		pStringDelimiterEnd = pStringDelimiter + wcslen(pStringDelimiter);

	const wchar *pDelimiterStartEnd = NULL;
	if (pDelimiterStart)
	{
		pDelimiterStartEnd = pDelimiterStart + wcslen(pDelimiterStart);
		if (!pDelimiterEnd)
			pDelimiterEnd = pDelimiterStart;
		else
		{
			if (wcslen(pDelimiterStart) != wcslen(pDelimiterEnd))
				xbThrow xbEsys(E_INVALIDARG,"xbBSTR::GetToken() : delimiters start("+xbString(pDelimiterStart)+") length != delimiters end ("+xbString(pDelimiterEnd)+") length");
		}
	}
	if (bSpcNet)
	{
		while(start <= lg
			&& xbIsSpaceW(*pStart))
		{
			pStart++;
			start++;
		}
	}
	const wchar *ptr = pStart;
	int			pos = start;

	const wchar *pSepsEnd = pSeps + wcslen(pSeps);

	int level = 0;
	wchar chInStr = 0;

	xbArray<wchar,false,false>	arrLevel;

	while(pos < lg)
	{
		if (chInStr != 0)
		{
			if (*ptr == L'\\')
			{
				if (pos >= lg - 1)
					return xbTokenError_StringNotTerminated; //string not terminated
				ptr+=2;
				pos+=2;
				continue;
			}
			if (*ptr == chInStr)
				chInStr = 0;
			ptr++;
			pos++;
			continue;
		}
		if (pStringDelimiter)
		{
			const wchar *pDel = pStringDelimiter;
			while(pDel < pStringDelimiterEnd)
			{
				if (*ptr == *pDel)
				{
					chInStr = *pDel;
					break;
				}
				pDel++;
			}
			if (chInStr != 0)
			{
				ptr++;
				pos++;
				continue;
			}
		}
		if (level > 0)
		{
			if (*ptr == arrLevel.GetAt(level-1))
			{
				level--;
				ptr++;
				pos++;
				continue;
			}
		}
		if (pDelimiterStart)
		{
			const wchar *pDel = pDelimiterStart;
			const wchar *pDelEnd = pDelimiterEnd;
			while(pDel < pDelimiterStartEnd)
			{
				if (*ptr == *pDel)
					break;
				pDel++;
				pDelEnd++;
			}
			if (pDel < pDelimiterStartEnd)
			{
				level++;
				if (level > arrLevel.Length)
					arrLevel.Length = level;
				arrLevel.SetAt(level-1,*pDelEnd);
				ptr++;
				pos++;
				continue;
			}
		}
		if (level > 0)
		{
			ptr++;
			pos++;
			continue;
		}
		const wchar *pSep = pSeps;
		while(pSep < pSepsEnd)
		{
			if ((*ptr == *pSep) || (*pSep == ' ' && xbIsSpaceW(*ptr)))
			{
				int retPos = pos;
				const wchar *retPtr = ptr;
				if (*pSep == ' ')
				{
					while(retPos < lg && xbIsSpaceW(*(retPtr+1)))
					{
						retPtr++;
						retPos++;
					}
					if (retPos < lg - 1)
					{
						const wchar *pOthSep = wcschr(pSeps,*(retPtr+1));
						if (pOthSep != NULL)
						{
							pSep = pOthSep;
							retPtr++;
							retPos++;
						}
					}
				}
				else if (bSpcNet)
				{
					while(pos > start && xbIsSpaceW(*(ptr-1)))
					{
						pos--;
						ptr--;
					}
				}
				//Bugs corrigés sur 5.0.3.69 / 3/3/03 pChSep was bad
				if (pChSep) //
					*pChSep = *retPtr;
				xbBSTR str(xbAllocLib),strStaying(xbAllocLib);
				if (pStr)
				{
					if (pos - start > 0) 
						str.Set(pStart,pos - start);
				}
				if (pStrStaying)
				{
					if (lg - retPos - 1> 0) 
					{
						strStaying.Set(retPtr+1,lg - retPos - 1);
						if (bSpcNet)
							strStaying.Trim();
					}
				}
				if (pStr) 
					*pStr = str;
				if (pStrStaying) 
					*pStrStaying = strStaying;
				return retPos+1;
			}
			pSep++;
		}
		ptr++;
		pos++;
	}
	if (chInStr != 0)
		return xbTokenError_StringNotTerminated; //string not terminated
	if (level > 0)
		return xbTokenError_BlockNotTerminated; //delimited block not terminated
	if (pStr)
	{
		xbBSTR str(pStart,lg - start + 1);
		if (bSpcNet)
			str.Trim();
		*pStr = str;
	}
	if (pStrStaying)
		pStrStaying->Empty();
	if (pChSep)
		*pChSep = 0;
	return lg;
}

int xbBSTR::GetTokenString(int start,const wchar *pStringDelimiter, xbBSTR *pStr,
		wchar *pchDelimiter,bool *pbMixedDelimiter) const
{
	if (pStr) 
		pStr->Empty();
	if (pchDelimiter)
		*pchDelimiter = 0;
	if (pbMixedDelimiter)
		*pbMixedDelimiter = false;

	if (IsEmpty()) 
		return xbTokenError_End;

	const wchar *ptr = *this;
	int			lg = Length;

	if (start <= 0)
		xbThrow xbEsys(E_INVALIDARG,"xbBSTR::GetTokenString(start="+xbString(start)+") : bad start position");
	if (start >= lg) 
		return xbTokenError_End;

	if (start > 0)
		ptr += start;

	const wchar *pStringDelimiterEnd = pStringDelimiter + wcslen(pStringDelimiter);

	int			pos = start;

	wchar chInStr = 0,chDel = 0;
	const wchar *pStart = NULL;
	start = 0;

	while(pos < lg)
	{
		if (chInStr != 0)
		{
			if (*ptr == L'\\')
			{
				if (pos >= lg - 1)
					return xbTokenError_StringNotTerminated; //string not terminated
				ptr+=2;
				pos+=2;
				continue;
			}
			if (*ptr == chInStr)
			{
				if (pStr)
					pStr->Add(pStart,pos - start);
				pStart = NULL;
				start = 0;
				chInStr = 0;
			}
			ptr++;
			pos++;
			continue;
		}
		if (pStringDelimiter)
		{
			const wchar *pDel = pStringDelimiter;
			while(pDel < pStringDelimiterEnd)
			{
				if (*ptr == *pDel)
				{
					chInStr = *pDel;
					if (chDel == 0) chDel = chInStr;
					else if (pbMixedDelimiter && chDel != chInStr)
						*pbMixedDelimiter = true;
					pStart = ptr + 1;
					start = pos + 1;
					break;
				}
				pDel++;
			}
			if (chInStr != 0)
			{
				ptr++;
				pos++;
				continue;
			}
		}
		if (!xbIsSpaceW(*ptr))
			break;
		ptr++;
		pos++;
	}
	if (chInStr != 0)
		return xbTokenError_StringNotTerminated; //string not terminated
	if (pchDelimiter)
		*pchDelimiter = chDel;
	return pos;
}

void xbBSTR::trim(const wchar *pTrimChars, bool bAutoOwn, bool bLeft, bool bRight)
{
	long lgOrig = GetLength();
	long lg = lgOrig;
	const wchar *ptr = m_str;

	if (bLeft)
	{
		while(lg > 0 && ::wcschr(pTrimChars,*ptr))
		{
			lg--;
			ptr++;
		}
	}

	if (bRight)
	{
		const wchar *pend = ptr + lg - 1;
		while(lg > 0 && (pTrimChars && ::wcschr(pTrimChars,*pend)))
		{
			lg--;
			pend--;
		}
	}
	if (lg == lgOrig)
		return;

	if (bAutoOwn && !(m_bstrMode & xbBSTR_TOFREE))
	{
		m_str = Copy();
		m_bstrMode |= xbBSTR_TOFREE;
	}

	if (ptr != m_str)
	{
		long pos = long((xbNptr(ptr) - xbNptr(m_str))/sizeof(wchar));
		memmove(m_str,m_str+pos,lg*sizeof(wchar));
	}

	SetLength(lg, false);
}

xbStringW xbVariant::GetAsXmlStringW() const
{
	VARTYPE vt = GetType();
	if (vt == VT_EMPTY)
		return L"empty:";

	if (vt == VT_BSTR)
	{
		xbStringW val(AsStringW);
		if (!AsXmlStringWtypeNameGet(val,NULL,NULL))
			return val;
		return L"string:"+val;
	}
	if (vt == VT_UI4)
		return L"ulong:0x"+::xbStrFromUlongW(AsUlong,16);
	if (vt == VT_I4)
		return L"long:"+::xbStrFromLongW(AsUlong);
	if (vt == VT_R8)
		return L"double:"+::xbStrFromDoubleW(AsDouble);
	if (vt == VT_BOOL)
		return L"bool:"+::xbStrFromBoolW(AsBool);
	if (vt == (VT_UI1|VT_ARRAY))
	{
		xbData data;
		GetData(&data);
		return L"data:"+::xbBase64encodeW(data.Begin(),data.Length);
	}
	xbThrowEsys("xbVariant::GetAsXmlStringW: variant type not supported");
	return xbStringW();
}

void xbVariant::SetAsXmlStringW(const xbStringW &str)
{
	xbStringW stype,sval;
	if (!AsXmlStringWtypeNameGet(str,&stype,&sval))
	{
		AsStringW = str;
		return;
	}
	stype.Lower();
	if (stype == L"empty")
	{
		xbRdbgAssert(sval.IsEmpty());
		Empty();
		return;
	}
	if (stype == L"long")
	{
		AsLong = ::xbStrToLongW(sval,0);
		return;
	}
	if (stype == L"ulong")
	{
		AsUlong = ::xbStrToUlongW(sval,0);
		return;
	}
	if (stype == L"double")
	{
		AsDouble = ::xbStrToDoubleW(sval);
		return;
	}
	if (stype == L"bool")
	{
		AsBool = ::xbStrToBoolW(sval);
		return;
	}
	if (stype == L"data")
	{
		xbStringA data(::xbBase64decodeW(sval));
		SetData(data.Ptr(),data.Length);
		return;
	}

	xbThrowEsysFmt("xbVariant::SetAsXmlStringW: variant type(%s) not supported",xbString(stype).Ptr());
}

LPCWSTR xbVariant::AsXmlStringWtypeToName(VARTYPE vtype)
{
	switch(vtype)
	{
	case VT_EMPTY:
		return L"empty";
	case VT_BSTR:
		return L"string";
	case VT_I4:
		return L"long";
	case VT_UI4:
		return L"ulong";
	case VT_R8:
		return L"double";
	case VT_BOOL:
		return L"bool";
	case VT_UI1|VT_ARRAY:
		return L"data";
	}
	return NULL;
}
bool xbVariant::AsXmlStringWtypeNameGet(const xbStringW &str,xbStringW *pTypeStr,xbStringW *pVal)
{
	const wchar *ptr = str;//.Ptr();
	if (!ptr)
		return false;
	//type syntax could evoluate to type[], ...
	int cTyp = 0;
	while(cTyp < 30
		&& ((*ptr >= L'a' && *ptr <= L'z')
			|| (*ptr >= L'A' && *ptr <= L'Z')
			|| (*ptr >= L'0' && *ptr <= L'9')
			|| *ptr == L'[' || *ptr == L']'))
	{
		ptr++;
		cTyp++;
	}
	if (cTyp<=1 || *ptr != L':')
		return false;
	if (pTypeStr)
		pTypeStr->Set(str,cTyp);
	if (pVal)
		pVal->Set(ptr+1);
	return true;
}
#pragma warning(pop)
