#ifndef _xbDATA_
#define _xbDATA_
/*<<<***************************************************************************
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: xbData
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef _xbSTRING_
#	include "xbString.h"
#endif

#define xbC_IAC	BYTE(255)

class xbData;
class xbEXPORT TEBCDICmap
{
public:
	TEBCDICmap();
	void LoadCodePage(const char *pszFileCodePage);
	void DefaultCodePage();
	void NullCodePage();

	inline BYTE	ToEBCDIC(BYTE cChar) const
	{
		xbAssert(m_Inited);

		return m_Inited?m_ASCII_EBCDIC[cChar]:cChar;
	}

	inline BYTE	ToASCII(BYTE cChar) const
	{
		xbAssert(m_Inited);

		return m_Inited?m_EBCDIC_ASCII[cChar]:cChar;
	}

	void	ToEBCDIC(const BYTE *pSrce,BYTE *pDest,int lg) const;
	void	ToASCII(const BYTE *pSrce,BYTE *pDest,int lg) const;

	xbString	ToEBCDIC(const xbString &srce) const
	{
		int lg = srce.GetLength();
		xbString dest(xbStringPad(lg));
		ToEBCDIC((const BYTE *)(const char *)srce,(BYTE *)(const char *)dest,lg);
		return dest;
	}
	xbString	ToASCII(const xbString &srce) const
	{
		int lg = srce.GetLength();
		xbString dest(xbStringPad(lg));
		ToASCII((const BYTE *)(const char *)srce,(BYTE *)(const char *)dest,lg);
		return dest;
	}

	void	DbgDump(const xbString &msg);
protected:
	void	InitASCII();
	void	InitEBCDIC();
	void	CorrectCP();

	BYTE	m_EBCDIC_ASCII[256];
	BYTE	m_ASCII_EBCDIC[256];
	bool	m_Inited;
	friend xbData;
private:
	xbDclNoNewForObject(TEBCDICmap);
	xbDclNoCopyForObject(TEBCDICmap);
};

class xbEXPORT xbData 
{
public :
	xbData ()
	{
		m_pVal = NULL;
	}
	xbData (const void *pData,  int len)
	{
		m_pVal = NULL;
		SetData(pData,len);
	}
	xbData (char chInit,  int len)
	{
		m_pVal = NULL;
		SetData(chInit,len);
	}
	xbData(const xbData &b)
	{
		m_pVal = NULL;
		if (!b.IsEmpty()) SetData(b.m_pVal,b.GetLength());
	}
	xbData &operator = (const xbData &b)
	{
		if (b.IsEmpty()) Clear();
		else SetData(b.m_pVal,b.GetLength());
		return *this;
	}
	~xbData()
	{
		eProtect(Clear());
	}

	void Attach(xbData *pDta)
	{
		Clear();
		m_pVal = pDta->m_pVal;
		pDta->m_pVal = NULL;
	}
	
	const void *Detach(xbData *pDta)//Caution: in array returned 4 first bytes is length, pDta could be NULL
	{
		const void *pRet = (const void*)((xbint*)m_pVal-1);
		if (pDta)
		{
			pDta->Clear();
			pDta->m_pVal = m_pVal;
		}
		m_pVal = NULL;
		return pRet;
	}

	xbData &SetData(const void *pData,  int len)
	{
		if (len < 0 || !pData) xbThrowEarg("xbData.SetData() failure"); 
		SetLength(len);
		if (len>0)
			memcpy (m_pVal, pData, len);
		return *this;
	}
	xbData &SetData(const char *pData,  int len)
	{
		return SetData((const void*)pData,len);
	}
	xbData &SetData(char chInit,  int len)
	{
		if (len < 0) xbThrowEarg("xbData.SetData() failure"); 
		SetLength(len);
		if (len>0)
			memset (m_pVal, chInit, len);
		return *this;
	}
	xbData &Init(char chInit,  int len)
	{
		if (len < 0) xbThrowEarg("xbData.Init() failure"); 
		SetLength(len);
		if (len>0)
			memset (m_pVal, chInit, len);
		return *this;
	}
	xbData &WriteData(int pos, const void *pData,  int len)
	{
		if (len <= 0) return *this;
		if (pos < 1) xbThrowEarg("xbData.WriteData() failure"); 
		int dtaLen = GetLength();
		if (pos + len - 1 > dtaLen) xbThrowEarg("xbData.WriteData() failure"); 
		memcpy ((BYTE*)m_pVal+pos-1, pData, len);
		return *this;
	}
	xbData &WriteData(int pos, const char *pData,  int len)
	{
		return WriteData(pos,(const void*)pData,len);
	}
	xbData &WriteData(int pos, int chInit,  int len)
	{
		if (len <= 0) return *this;
		if (pos < 1) xbThrowEarg("xbData.WriteData() failure"); 
		int dtaLen = GetLength();
		if (pos + len - 1 > dtaLen) xbThrowEarg("xbData.WriteData() failure"); 
		memset ((BYTE*)m_pVal+pos-1, chInit, len);
		return *this;
	}
	xbData &Clear()
	{
		if (!m_pVal) return *this;
		xbGlobalFree((xbint*)m_pVal-1);
		m_pVal = NULL;
		return *this;
	}
	inline void Empty()
	{
		Clear();
	}
	int		GetLength() const
	{
		return m_pVal?int(*((xbint*)m_pVal-1)):0; 
	}
	int	SetLength(int len)
	{
//		if (len < 0) xbThrowEarg("xbData.SetLength() failure"); 
		if (len <= 0) {Clear(); return 0;}
		if (m_pVal && len == int(*((xbint*)m_pVal-1))) return len;
		xbint *pVal;
		if (!m_pVal)
		{
			pVal = (xbint*)xbGlobalAlloc(sizeof(xbint)+len+1); 
		}
		else
		{
			pVal = (xbint*)xbGlobalReAlloc((xbint*)m_pVal-1,sizeof(xbint)+len+1); 
		}
		xbRdbgAssert(pVal != NULL);
		m_pVal = pVal+1;
		*pVal = len;
		((BYTE*)m_pVal)[len] = 0;
		return len;
	}
	xbPropertyRW(int,Length);

	operator const void * () const
	{
		return m_pVal;
	}
	void	*GetWritePtr(int len)
	{
		SetLength(len);
		return m_pVal;
	}
	bool	IsEmpty() const
	{
		return m_pVal == NULL;
	}
	bool	IsValid() const
	{
		return m_pVal != NULL;
	}
#ifndef xbINDICE_0BASEDCHECK
	BYTE &operator [] (int i)
	{
		if (i < 0 || i >= GetLength())
			xbThrowEargFmt("xbData.operator[](%d) failure, bad index",i); 
		return ((BYTE*)m_pVal) [i];
	}
	const BYTE operator [] (int i) const
	{
		if (i < 0 || i >= GetLength())
			xbThrowEargFmt("xbData.operator[](%d) const failure, bad index",i); 
		return ((BYTE*)m_pVal) [i];
	}
#else
private:
	BYTE &operator [] (int i);
	const BYTE &operator [] (int i) const;
public:
#endif
	BYTE &GetAt(int i)
	{
		if (i < 0 || i >= GetLength())
			xbThrowEargFmt("xbData.GetAt(%d) failure, bad index (len=%d)",i,GetLength()); 
		return ((BYTE*)m_pVal)[i];
	}
	const BYTE &GetAt(int i) const
	{
		if (i < 0 || i >= GetLength())
			xbThrowEargFmt("xbData.GetAt(%d) failure, bad index (len=%d)",i,GetLength()); 
		return ((BYTE*)m_pVal)[i];
	}
	void SetAt(int i,const BYTE &val)
	{
		if (i < 0 || i >= GetLength())
			xbThrowEargFmt("xbData.SetAt(%d,...) failure, bad index (len=%d)",i,GetLength()); 
		(((BYTE*)m_pVal)[i]) = val;
	}
	BYTE &First(int i = 0)
	{
		if (i < 0 || i >= GetLength())
			xbThrowEargFmt("xbData.First(%d) failure, bad index (len=%d)",i,GetLength()); 
		return ((BYTE*)m_pVal)[i];
	}
	const BYTE &First(int i = 0) const
	{
		if (i < 0 || i >= GetLength())
			xbThrowEargFmt("xbData.First(%d) const failure, bad index (len=%d)",i,GetLength()); 
		return ((BYTE*)m_pVal)[i];
	}
	BYTE &Last(int i = 0)
	{
		int j = GetLength() - i - 1;
		if (j < 0 || j >= GetLength())
			xbThrowEargFmt("xbData.Last(%d) failure, bad index (len=%d)",i,GetLength()); 
		return ((BYTE*)m_pVal)[j];
	}
	const BYTE &Last(int i = 0) const
	{
		int j = GetLength() - i - 1;
		if (j < 0 || j >= GetLength())
			xbThrowEargFmt("xbData.Last(%d) const failure, bad index (len=%d)",i,GetLength()); 
		return ((BYTE*)m_pVal)[j];
	}
	BYTE &unsafeGetAt(int i)
	{
		return ((BYTE*)m_pVal)[i];
	}
	const BYTE &unsafeGetAt(int i) const
	{
		return ((BYTE*)m_pVal)[i];
	}
	void unsafeSetAt(int i,const BYTE &val)
	{
		(((BYTE*)m_pVal)[i]) = val;
	}
	BYTE *Begin()
	{
		return (BYTE*)m_pVal;
	}
	BYTE *End()
	{
		if (IsEmpty()) return NULL;
		return ((BYTE*)m_pVal) + GetLength();
	}
	const BYTE *Begin() const
	{
		return (const BYTE*)m_pVal;
	}
	const BYTE *End() const
	{
		if (IsEmpty()) return NULL;
		return ((const BYTE*)m_pVal) + GetLength();
	}

	int	GetWord(int posInByte)
	{
		if (posInByte < 1 || posInByte + 1 > GetLength())
			xbThrowEargFmt("xbData.GetWord(%d) failure, bad index",posInByte); 
		BYTE *ptr = (BYTE*)m_pVal + posInByte - 1;
		return int(*ptr) * 256 + int(*(ptr+1));
	}
	xbData &Format(const char *pStr,...)
	{
		va_list		ap;
		va_start(ap,pStr);

		char		buf[512];
		int lg = xbVsnprintf(buf,sizeof(buf),pStr,ap);
		if (lg <= 0)
			xbThrowEarg("xbData.Format() failure"); 
		
		va_end(ap);

		SetData(buf,lg);
		return *this;
	}
	xbData Mid(int start,int len) const
	{
		if (start < 0 || len < 0) 
			xbThrowEargFmt("xbData::Mid(%d,%d) : bad arg",start,len);
		int dtaLen = GetLength();
		if (start >= dtaLen) 
			return xbData();

		len = min(len, dtaLen-start);
		if (len == 0) return xbData();
		
		return xbData((char*)m_pVal+start,len);
	}
	xbData Mid(int start) const
	{
		if (start < 0) 
			xbThrowEargFmt("xbData::Mid(%d) : bad arg",start);
		int dtaLen = GetLength();
		if (start >= dtaLen) 
			return xbData();

		return xbData((char*)m_pVal+start,dtaLen-start);
	}

	int	GetEBCDIClength() const;
	
	xbData &AddEBCDIC(const TEBCDICmap &map,const void *ptr,int len,int *pNbDel = NULL);
	xbData &AddEBCDIC(const TEBCDICmap &map,const xbData &data,int *pNbDel = NULL)
	{
		AddEBCDIC(map,data,data.GetLength(),pNbDel);
		return *this;
	}
	xbData &AddEBCDIC(const TEBCDICmap &map,const xbString &data,int *pNbDel = NULL)
	{
		AddEBCDIC(map,data,data.GetLength(),pNbDel);
		return *this;
	}

	xbData &AddEBCDICnc(const void *ptr,int len,int *pNbDel = NULL);
	xbData &AddEBCDICnc(const xbData &data,int *pNbDel = NULL)
	{
		AddEBCDICnc(data,data.GetLength(),pNbDel);
		return *this;
	}
	xbData &AddEBCDICnc(const xbString &data,int *pNbDel = NULL)
	{
		AddEBCDICnc(data,data.GetLength(),pNbDel);
		return *this;
	}
	xbData &AddWord(int nb)
	{
		if (nb < 0 || nb > 0xFFFF) 
			xbThrowEsys("xbData::AddWord() : word out of range"); 
		int iX1 = nb / 256;
		int iX2 = nb % 256;
		if ( iX1  ==  xbC_IAC ) 
			operator<<(xbC_IAC);
		operator<<(iX1);
		if ( iX2  ==  xbC_IAC )
			operator<<(xbC_IAC);
		operator<<(iX2);

		return *this;
	}
	//MMM: bad operator+= (must add char and not grow size)

	xbData &operator<<(int ch)
	{
		int len = GetLength();
		SetLength(len+1);
		((BYTE*)m_pVal)[len] = BYTE(ch);
		return *this;
	}

	xbData &Add(const xbData &data,int lg)
	{
		if (lg == 0) return *this;
		if (lg > data.GetLength()) xbThrowEargFmt("xbData::Add(datalg=%ld,lg=%ld) : data to write too small",data.GetLength(),lg); 
		int len = GetLength();
		SetLength(len+lg);
		memcpy((BYTE*)m_pVal + len, data, lg);
		return *this;
	}

	xbData &Add(const void *pdata,int lg)
	{
		if (lg == 0) return *this;
		int len = GetLength();
		SetLength(len+lg);
		memcpy((BYTE*)m_pVal + len, pdata, lg);
		return *this;
	}

	xbData &operator<<(const xbData &data)
	{
		if (data.IsEmpty()) return *this;
		int len = GetLength();
		SetLength(len+data.GetLength());
		memcpy((BYTE*)m_pVal + len, data, data.GetLength());
		return *this;
	}

	xbData &operator<<(const xbString &data)
	{
		if (data.IsEmpty()) return *this;
		int len = GetLength();
		SetLength(len+data.GetLength());
		memcpy((BYTE*)m_pVal + len, data, data.GetLength());
		return *this;
	}

	void Delete(int pos,int len)
	{
		if (pos < 0) 
			xbThrowEarg("xbData.Delete() failure"); 
		int dtaLen = GetLength();
		if (pos >= dtaLen) 
			return;
		if (pos + len >= dtaLen) 
			len = dtaLen - pos;
		if (/*pos == 0
			&&*/ len >= dtaLen)
		{
			Clear();
			return;
		}
		memmove((BYTE*)m_pVal + pos - 1,(BYTE*)m_pVal + pos + len - 1,
			dtaLen - len - pos + 1);
		SetLength(dtaLen - len);
	}

	int Compare(const xbData &dta) const
	{
		int lg = dta.GetLength();
		if (GetLength() != lg) return GetLength() - lg;

		return memcmp(m_pVal,dta,lg);
	}
	int PartialCompare(const xbData &dta) const
	{
		int lg = dta.GetLength();
		if (GetLength() < lg) return GetLength() - lg;

		return memcmp(m_pVal,dta,lg);
	}
	bool operator==(const xbData &dta)
	{
		return Compare(dta) == 0;
	}
	bool operator!=(const xbData &dta)
	{
		return Compare(dta) != 0;
	}

	void	ParseStr(const char *pStr);

	// find character starting at left, -1 if not found
	int IndexOf(char ch) const; //Reverse

	// find character starting at right
	int LastIndexOf(char ch) const;

	// find character starting at 0-based index and going left
	int IndexOf(char ch, int start) const;

	// find character starting at 0-based index and going right
	int LastIndexOf(char ch, int start) const;

	// find first instance of any character in passed string
	int IndexOfChars(LPCSTR lpszCharSet, int start = 0) const;

	// find last instance of any character in passed string
	int LastIndexOfChars(LPCSTR lpszCharSet, int start = xbSIZE_LIMIT) const;

	// find first instance of substring starting at zero-based index
	int IndexOf(LPCSTR lpszSub, int nStart = 0) const
	{
		return IndexOf(lpszSub,-1,nStart);
	}
	int IndexOf(const xbStringA &sSub, int nStart = 0) const
	{
		return IndexOf(sSub,sSub.Length,nStart);
	}
	int IndexOf(LPCSTR lpszSub, int cChSub, int nStart) const;

	// find first instance of substring starting at zero-based index
	int LastIndexOf(LPCSTR lpszSub, int nStart = xbSIZE_LIMIT) const
	{
		return LastIndexOf(lpszSub,-1,nStart);
	}
	int LastIndexOf(const xbStringA &sSub, int nStart = xbSIZE_LIMIT) const
	{
		return LastIndexOf(sSub,sSub.Length,nStart);
	}
	int LastIndexOf(LPCSTR lpszSub, int cChSub, int nStart) const;
protected:
	void	*m_pVal;
private:
	xbDclNoNewForObject(xbData);
};

/*inline const xbData xbEXPORT Mid (const xbData &str , int start, int length)
{
	return str.Mid(start,length);
}

inline const xbData xbEXPORT Mid (const xbData &str , int start)
{
	return str.Mid(start);
}
*/
//int			xbEXPORT InStr (int start , const xbData &str , const xbData &strToFind, int cmp=0);
//int			xbEXPORT InStr (int start , const xbData &str , BYTE chToFind, int cmp=0);

#ifdef _DEBUG
#	define xbDbgDataTrace(msg,data) xbDataTrace(msg,data)
#else
#	define xbDbgDataTrace(msg,data)
#endif
extern void xbEXPORT xbDataTrace(const char *pszMessage, const xbData &sData);

template<class t_obj> class xbTdata:public xbData
{
public:
	xbTdata()
	{
	}
	xbTdata(const void *pData,  int len)
		:xbData(pData,len)
	{
	}
	xbTdata(const t_obj *pData,  int len)
		:xbData((const void*)pData,len)
	{
	}
	xbTdata(char chInit,  int len)
		:xbData(chInit,len)
	{
	}
	xbTdata(const xbData &b)
		:xbData(b)
	{
	}
	xbTdata &operator=(const xbData &b)
	{
		xbData::operator=(b);
		return *this;
	}
	xbTdata &operator=(const xbTdata<t_obj> &b)
	{
		xbData::operator=(b);
		return *this;
	}

	operator const t_obj*() const
	{
		return (const t_obj*)m_pVal;
	}
	operator t_obj*()
	{
		return (t_obj*)m_pVal;
	}
	t_obj	*GetWritePtr(int len)
	{
		SetLength(len);
		return (t_obj*)m_pVal;
	}
	void Set(const t_obj *pData,int len)
	{
		SetData(pData,len);
	}
	t_obj *operator->() const
	{
		if (!m_pVal) 
			xbThrowEsys("t_obj *xbTdata::operator->() const, data is empty !");
		return (t_obj*)m_pVal;
	}
	t_obj* Ptr() const
	{
		return (t_obj*)m_pVal;
	}
};

#endif
