#ifndef __COMUTILHPP__
#define __COMUTILHPP__

#pragma once

#include <tchar.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <windows.h>

template<typename type>
inline type clamp (const type& min,const type& x,const type& max)
{
  if (x < min)
    return min;
  if (x>max)
    return max;
  return x;
}
#if 0
template<typename type>
type max(const type& a,const type& b)
{
	return (((a) > (b)) ? (a) : (b));
}
template<typename type>
type min(const type& a,const type& b)
{
	return (((a) < (b)) ? (a) : (b));
}
#endif
inline bool IsAlNum(BYTE bChar)
{
#pragma warning (disable:4800)
	WORD wResult;
	BOOL bRc;

	bRc = GetStringTypeEx(LOCALE_SYSTEM_DEFAULT, CT_CTYPE1, (LPCTSTR)&bChar, 1, &wResult);
	assert(bRc);
	return (bool)(wResult & (C1_ALPHA | C1_DIGIT));
}

inline bool IsSpace(BYTE bChar)
{
#pragma warning (disable:4800)
	WORD wResult;
	BOOL bRc;

	bRc = GetStringTypeEx(LOCALE_SYSTEM_DEFAULT, CT_CTYPE1, (LPCTSTR)&bChar, 1, &wResult);
	assert(bRc);
	return (bool)(wResult & (C1_SPACE));
}

inline bool IsSpace(WORD bChar)
{
#pragma warning (disable:4800)
	WORD wResult;
	BOOL bRc;

	bRc = GetStringTypeEx(LOCALE_SYSTEM_DEFAULT, CT_CTYPE1, (LPCTSTR)&bChar, 2, &wResult);
	assert(bRc);
	return (bool)(wResult & (C1_SPACE));
}


#define _countof(array) (sizeof(array)/sizeof(array[0]))

#ifdef OLE2ANSI
	typedef LPSTR BSTR;
#else
	typedef LPWSTR BSTR;// must (semantically) match typedef in oleauto.h
#endif

struct CCom_StringData
{
	long nRefs;     // reference count
	int nDataLength;
	int nAllocLength;
	// TCHAR data[nAllocLength]

	TCHAR* data()
		{ return (TCHAR*)(this+1); }
};

bool comline_AfxIsValidString(LPCWSTR lpsz, int nLength = -1);
bool comline_AfxIsValidString(LPCSTR lpsz, int nLength = -1);
bool comline_AfxIsValidAddress(const void* lp,UINT nBytes, bool bReadWrite = true);
int comline_AfxLoadString(UINT nID, LPTSTR lpszBuf, UINT nMaxBuf,HINSTANCE inst);

class CCom_String
{
public:
// Constructors
	CCom_String();
	CCom_String(const CCom_String& stringSrc);
	CCom_String(TCHAR ch, int nRepeat = 1);
	CCom_String(LPCTSTR lpsz);
	CCom_String(LPCWSTR lpsz);
	CCom_String(LPCTSTR lpch, int nLength);
	CCom_String(const unsigned char* psz);

// Attributes & Operations
	// as an array of characters
	int GetLength() const;
	bool IsEmpty() const;
	void Empty();                       // free up the data

	TCHAR GetAt(int nIndex) const;      // 0 based
	TCHAR operator[](int nIndex) const; // same as GetAt
	void SetAt(int nIndex, TCHAR ch);
	operator LPCTSTR() const;           // as a C string

	// overloaded assignment
	const CCom_String& operator=(const CCom_String& stringSrc);
	const CCom_String& operator=(TCHAR ch);
#ifdef _UNICODE
	const CCom_String& operator=(char ch);
#endif
	const CCom_String& operator=(LPCSTR lpsz);
	const CCom_String& operator=(LPCWSTR lpsz);
	const CCom_String& operator=(const unsigned char* psz);

	// string concatenation
	const CCom_String& operator+=(const CCom_String& string);
	const CCom_String& operator+=(TCHAR ch);
#ifdef _UNICODE
	const CCom_String& operator+=(char ch);
#endif
	const CCom_String& operator+=(LPCTSTR lpsz);

	friend CCom_String  operator+(const CCom_String& string1,
			const CCom_String& string2);
	friend CCom_String  operator+(const CCom_String& string, TCHAR ch);
	friend CCom_String  operator+(TCHAR ch, const CCom_String& string);
#ifdef _UNICODE
	friend CCom_String  operator+(const CCom_String& string, char ch);
	friend CCom_String  operator+(char ch, const CCom_String& string);
#endif
	friend CCom_String  operator+(const CCom_String& string, LPCTSTR lpsz);
	friend CCom_String  operator+(LPCTSTR lpsz, const CCom_String& string);

	// string comparison
	int Compare(LPCTSTR lpsz) const;         // straight character
	int CompareNoCase(LPCTSTR lpsz) const;   // ignore case
	int Collate(LPCTSTR lpsz) const;         // NLS aware

	// simple sub-string extraction
	CCom_String Mid(int nFirst, int nCount) const;
	CCom_String Mid(int nFirst) const;
	CCom_String Left(int nCount) const;
	CCom_String Right(int nCount) const;

	CCom_String SpanIncluding(LPCTSTR lpszCharSet) const;
	CCom_String SpanExcluding(LPCTSTR lpszCharSet) const;

	// upper/lower/reverse conversion
	void MakeUpper();
	void MakeLower();
	void MakeReverse();

	// trimming whitespace (either side)
	void TrimRight();
	void TrimLeft();

	// searching (return starting index, or -1 if not found)
	// look for a single character match
	int Find(TCHAR ch) const;               // like "C" strchr
	int ReverseFind(TCHAR ch) const;
	int FindOneOf(LPCTSTR lpszCharSet) const;

	// advanced manipulation
	int Replace(TCHAR chOld, TCHAR chNew);
	int Replace(LPCTSTR lpszOld, LPCTSTR lpszNew);
	int Remove(TCHAR chRemove);
	int Insert(int nIndex, TCHAR ch);
	int Insert(int nIndex, LPCTSTR pstr);
	int Delete(int nIndex, int nCount = 1);

	// look for a specific sub-string
	int Find(LPCTSTR lpszSub) const;        // like "C" strstr

	// simple formatting
	void _cdecl Format(LPCTSTR lpszFormat, ...);
	void _cdecl Format(UINT nFormatID,HINSTANCE inst, ...);
	void _cdecl FormatHex (char *mem,int count);
	void FormatV(LPCTSTR lpszFormat, va_list argList);

#ifndef _MAC
	// formatting for localization (uses FormatMessage API)
	void _cdecl FormatMessage(LPCTSTR lpszFormat, ...);
	void _cdecl FormatMessage(UINT nFormatID,HINSTANCE inst, ...);
#endif

	// Windows support
	bool LoadString(UINT nID,HINSTANCE inst=NULL);          // load from string resource
										// 255 chars max
#ifndef _UNICODE
	// ANSI <-> OEM support (convert string in place)
	void AnsiToOem();
	void OemToAnsi();
#endif

#ifndef _AFX_NO_BSTR_SUPPORT
	// OLE BSTR support (use for OLE automation)
	BSTR AllocSysString() const;
	BSTR SetSysString(BSTR* pbstr) const;
#endif

	// Access to string implementation buffer as "C" character array
	LPTSTR GetBuffer(int nMinBufLength);
	void ReleaseBuffer(int nNewLength = -1);
	LPTSTR GetBufferSetLength(int nNewLength);
	void FreeExtra();

	// Use LockBuffer/UnlockBuffer to turn refcounting off
	LPTSTR LockBuffer();
	void UnlockBuffer();

// Implementation
public:
	~CCom_String();
	int GetAllocLength() const;

protected:
	LPTSTR m_pchData;   // pointer to ref counted string data

	// implementation helpers
	CCom_StringData* GetData() const;
	void Init();
	void AllocCopy(CCom_String& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const;
	void AllocBuffer(int nLen);
	void AssignCopy(int nSrcLen, LPCTSTR lpszSrcData);
	void ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data);
	void ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData);
	void CopyBeforeWrite();
	void AllocBeforeWrite(int nLen);
	void Release();
	static void PASCAL Release(CCom_StringData* pData);
	static int PASCAL SafeStrlen(LPCTSTR lpsz);
};

// Compare helpers
bool  operator==(const CCom_String& s1, const CCom_String& s2);
bool  operator==(const CCom_String& s1, LPCTSTR s2);
bool  operator==(LPCTSTR s1, const CCom_String& s2);
bool  operator!=(const CCom_String& s1, const CCom_String& s2);
bool  operator!=(const CCom_String& s1, LPCTSTR s2);
bool  operator!=(LPCTSTR s1, const CCom_String& s2);
bool  operator<(const CCom_String& s1, const CCom_String& s2);
bool  operator<(const CCom_String& s1, LPCTSTR s2);
bool  operator<(LPCTSTR s1, const CCom_String& s2);
bool  operator>(const CCom_String& s1, const CCom_String& s2);
bool  operator>(const CCom_String& s1, LPCTSTR s2);
bool  operator>(LPCTSTR s1, const CCom_String& s2);
bool  operator<=(const CCom_String& s1, const CCom_String& s2);
bool  operator<=(const CCom_String& s1, LPCTSTR s2);
bool  operator<=(LPCTSTR s1, const CCom_String& s2);
bool  operator>=(const CCom_String& s1, const CCom_String& s2);
bool  operator>=(const CCom_String& s1, LPCTSTR s2);
bool  operator>=(LPCTSTR s1, const CCom_String& s2);

// conversion helpers
int _cdecl _wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count);
int _cdecl _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count);

// Globals
extern TCHAR comline_afxChNil;
const CCom_String&  comline_AfxGetEmptyString();
#define comline_afxEmptyString comline_AfxGetEmptyString()



// CCom_String
inline CCom_StringData* CCom_String::GetData() const
	{ assert(m_pchData != NULL); return ((CCom_StringData*)m_pchData)-1; }
inline void CCom_String::Init()
	{ m_pchData = comline_afxEmptyString.m_pchData; }
inline CCom_String::CCom_String(const unsigned char* lpsz)
	{ Init(); *this = (LPCSTR)lpsz; }
inline const CCom_String& CCom_String::operator=(const unsigned char* lpsz)
	{ *this = (LPCSTR)lpsz; return *this; }
#ifdef _UNICODE
inline const CCom_String& CCom_String::operator+=(char ch)
	{ *this += (TCHAR)ch; return *this; }
inline const CCom_String& CCom_String::operator=(char ch)
	{ *this = (TCHAR)ch; return *this; }
inline CCom_String  operator+(const CCom_String& string, char ch)
	{ return string + (TCHAR)ch; }
inline CCom_String  operator+(char ch, const CCom_String& string)
	{ return (TCHAR)ch + string; }
#endif

inline int CCom_String::GetLength() const
	{ return GetData()->nDataLength; }
inline int CCom_String::GetAllocLength() const
	{ return GetData()->nAllocLength; }
inline bool CCom_String::IsEmpty() const
	{ return GetData()->nDataLength == 0; }
inline CCom_String::operator LPCTSTR() const
	{ return m_pchData; }
inline int PASCAL CCom_String::SafeStrlen(LPCTSTR lpsz)
	{ return (lpsz == NULL) ? 0 : lstrlen(lpsz); }

// CCom_String support (windows specific)
inline int CCom_String::Compare(LPCTSTR lpsz) const
	{ return _tcscmp(m_pchData, lpsz); }    // MBCS/Unicode aware
inline int CCom_String::CompareNoCase(LPCTSTR lpsz) const
	{ return _tcsicmp(m_pchData, lpsz); }   // MBCS/Unicode aware
// CCom_String::Collate is often slower than Compare but is MBSC/Unicode
//  aware as well as locale-sensitive with respect to sort order.
inline int CCom_String::Collate(LPCTSTR lpsz) const
	{ return _tcscoll(m_pchData, lpsz); }   // locale sensitive

inline TCHAR CCom_String::GetAt(int nIndex) const
{
	assert(nIndex >= 0);
	assert(nIndex < GetData()->nDataLength);
	return m_pchData[nIndex];
}
inline TCHAR CCom_String::operator[](int nIndex) const
{
	// same as GetAt
	assert(nIndex >= 0);
	assert(nIndex < GetData()->nDataLength);
	return m_pchData[nIndex];
}
inline bool  operator==(const CCom_String& s1, const CCom_String& s2)
	{ return s1.Compare(s2) == 0; }
inline bool  operator==(const CCom_String& s1, LPCTSTR s2)
	{ return s1.Compare(s2) == 0; }
inline bool  operator==(LPCTSTR s1, const CCom_String& s2)
	{ return s2.Compare(s1) == 0; }
inline bool  operator!=(const CCom_String& s1, const CCom_String& s2)
	{ return s1.Compare(s2) != 0; }
inline bool  operator!=(const CCom_String& s1, LPCTSTR s2)
	{ return s1.Compare(s2) != 0; }
inline bool  operator!=(LPCTSTR s1, const CCom_String& s2)
	{ return s2.Compare(s1) != 0; }
inline bool  operator<(const CCom_String& s1, const CCom_String& s2)
	{ return s1.Compare(s2) < 0; }
inline bool  operator<(const CCom_String& s1, LPCTSTR s2)
	{ return s1.Compare(s2) < 0; }
inline bool  operator<(LPCTSTR s1, const CCom_String& s2)
	{ return s2.Compare(s1) > 0; }
inline bool  operator>(const CCom_String& s1, const CCom_String& s2)
	{ return s1.Compare(s2) > 0; }
inline bool  operator>(const CCom_String& s1, LPCTSTR s2)
	{ return s1.Compare(s2) > 0; }
inline bool  operator>(LPCTSTR s1, const CCom_String& s2)
	{ return s2.Compare(s1) < 0; }
inline bool  operator<=(const CCom_String& s1, const CCom_String& s2)
	{ return s1.Compare(s2) <= 0; }
inline bool  operator<=(const CCom_String& s1, LPCTSTR s2)
	{ return s1.Compare(s2) <= 0; }
inline bool  operator<=(LPCTSTR s1, const CCom_String& s2)
	{ return s2.Compare(s1) >= 0; }
inline bool  operator>=(const CCom_String& s1, const CCom_String& s2)
	{ return s1.Compare(s2) >= 0; }
inline bool  operator>=(const CCom_String& s1, LPCTSTR s2)
	{ return s1.Compare(s2) >= 0; }
inline bool  operator>=(LPCTSTR s1, const CCom_String& s2)
	{ return s2.Compare(s1) <= 0; }

#endif