#if !defined(__ARRAY__)
#define __ARRAY__

//{{NO_DEPENDENCIES}}

//
// Array Templates
//
#pragma warning(disable: 4786)

#ifndef NULL_CLASS_DEFINED
#define NULL_CLASS_DEFINED
class CNullClass {};
#endif

#define TARRAY(TYPE)        TArray<TYPE, CNullClass> // TARRAY(int) - none/none
#define TARRAYREF(TYPE)     TArrayDynamic<TYPE, TConNCon<TYPE>, TDesNDes<TYPE>, CNullClass> // TARRAYREF(CStr) - constructor/destructor
#define TARRAYPTR(TYPE)     TArrayDynamic<TYPE, TConNFZ<TYPE>, TDesNDEL<TYPE>, CNullClass> // TARRAYREF(CStr*) - new/delete
#define TARRAYPTR2(TYPE)    TArrayDynamic<TYPE, TConNFZ<TYPE>, TDesNDEL2<TYPE>, CNullClass> //  // TARRAYREF(char*) - malloc/free
#define TARRAYSIMPLE(TYPE)  TArraySimple<TYPE, CNullClass> // TARRAYSIMPLE(int), TARRAYZERO(float)

// above class definition
#define DECLARE_TARRAYREF_EX(CClass) inline void* _cdecl operator new(size_t nSize, CClass* pData) { return (void*)pData; } inline void _cdecl operator delete(void* p, CClass* pData) { }

#define DECLARE_TARRAYREF(CClass) class CClass; DECLARE_TARRAYREF_EX(CClass);

#define DECLARE_TARRAYREF_CLASS_EX(CClass, BaseClass) DECLARE_TARRAYREF(CClass) class CClass : public BaseClass { public: CClass() {} ~CClass() {} };

#define DECLARE_TARRAYREF_CLASS(Class,TYPE) DECLARE_TARRAYREF_CLASS_EX(Class, TARRAYREF(TYPE))

// in class definition
#define DECLARE_STATIC_CONSTRUCTION(CClass) inline void new##CClass() { new (this) CClass; } inline void delete##CClass() { this-> ~CClass(); }

template <class TYPE> inline void SwapValue(TYPE& a, TYPE& b)
{
    a ^= b;
    b ^= a;
    a ^= b;
}

template <class TYPE> inline void SwapPointer(TYPE*& a, TYPE*& b)
{
    TYPE* p;

    p = a;
    a = b;
    b = p;
}
/*
All the structures starting with TCon are the Template-Constructor.
TCon1XXXX means the constructor for 1 element.
TConNXXXX means the constructor for arbitrary number of elements.

All the structures starting with TDes are the Template-Destructor.
*/

template <class TYPE>
struct TCon1None
{
    // constructor: do NOTHING
    static void Construct(TYPE* pData)
    {
        ASSERT(pData);
    }
};

template <class TYPE>
struct TCon1Con
{
    // constructor: call the constructor...
    static void Construct(TYPE* pData)
    {
        ASSERT(pData);

        new (pData) TYPE;
    }
};

template <class TYPE>
struct TCon1AZ
{
    // constructor: assign zero.
    static void Construct(TYPE* pData)
    {
        ASSERT(pData);

        *pData = 0;
    }
};

template <class TYPE>
struct TCon1FZ
{
    // constructor: fill with zero
    static void Construct(TYPE* pData)
    {
        ASSERT(pData);

        memset(pData, 0, sizeof(TYPE));
    }
};

template <class TYPE>
struct TConNNone
{
    // constructor: do NOTHING
    static void Construct(TYPE* pData, int nCount)
    {
        ASSERT((nCount >= 0) && (pData || (nCount == 0)));
    }
};

template <class TYPE>
struct TConNCon : public TCon1Con<TYPE>
{
    // constructor: call the constructor
    static void Construct(TYPE* pData, int nCount)
    {
        ASSERT((nCount >= 0) && (pData || (nCount == 0)));

        while(nCount-- > 0) {
            TCon1Con<TYPE>::Construct(pData++);
        }
    }
};

template <class TYPE>
struct TConNAZ : public TCon1AZ<TYPE>
{
    // constructor: assign zero
    static void Construct(TYPE* pData, int nCount)
    {
        ASSERT((nCount >= 0) && (pData || (nCount == 0)));

        while(nCount-- > 0) {
            TCon1AZ<TYPE>::Construct(pData++);
        }
    }
};

template <class TYPE>
struct TConNFZ
{
    // constructor: fill with zero
    static void Construct(TYPE* pData, int nCount)
    {
        ASSERT((nCount >= 0) && (pData || (nCount == 0)));

        if (nCount > 0) {
            memset(pData, 0, sizeof(TYPE) * nCount);
        }
    }
};

template <class TYPE>
struct TDes1None
{
    // destructor: do NOTHING
    static void Destruct(TYPE* pData)
    {
        ASSERT(pData);
    }
};

template <class TYPE>
struct TDes1Des
{
    // destructor: call the destructor
    static void Destruct(TYPE* pData)
    {
        ASSERT(pData);

        pData-> ~TYPE();
    }
};

template <class TYPE>
struct TDes1AZ
{
    // destructor: assign zero
    static void Destruct(TYPE* pData)
    {
        ASSERT(pData);

        *pData = 0;
    }
};

template <class TYPE>
struct TDes1FZ
{
    // destructor: fill with zero
    static void Destruct(TYPE* pData)
    {
        ASSERT(pData);

        memset(pData, 0, sizeof(TYPE));
    }
};

template <class TYPE>
struct TDesNNone
{
    // destructor: do NOTHING
    static void Destruct(TYPE* pData, int nCount)
    {
        ASSERT((nCount >= 0) && (pData || (nCount == 0)));
    }
};

template <class TYPE>
struct TDesNDes : public TDes1Des<TYPE>
{
    // destructor: call the destructor
    static void Destruct(TYPE* pData, int nCount)
    {
        ASSERT((nCount >= 0) && (pData || (nCount == 0)));

        while(nCount-- > 0) {
            TDes1Des<TYPE>::Destruct(pData++);
        }
    }
};

template <class TYPE>
struct TDesNAZ : public TDes1AZ<TYPE>
{
    // destructor: assign zero
    static void Destruct(TYPE* pData, int nCount)
    {
        ASSERT((nCount >= 0) && (pData || (nCount == 0)));

        while(nCount-- > 0) {
            TDes1AZ<TYPE>::Destruct(pData++);
        }
    }
};

template <class TYPE>
struct TDesNFZ
{
    // destructor: fill with zero
    static void Destruct(TYPE* pData, int nCount)
    {
        ASSERT((nCount >= 0) && (pData || (nCount == 0)));

        if (nCount > 0) {
            memset(pData, 0, sizeof(TYPE) * nCount);
        }
    }
};

template <class TYPE>
struct TDesNDEL
{
    // destructor: fill with zero
    static void Destruct(TYPE* pData, int nCount)
    {
        ASSERT((nCount >= 0) && (pData || (nCount == 0)));

        while(nCount-- > 0) {
            TYPE t = *pData++;
            if (t)
                delete t;
        }
    }
};

template <class TYPE>
struct TDesNDEL2
{
    // destructor: fill with zero
    static void Destruct(TYPE* pData, int nCount)
    {
        ASSERT((nCount >= 0) && (pData || (nCount == 0)));

        while(nCount-- > 0) {
            TYPE t = *pData++;
            if (t)
                delete [] t;
        }
    }
};

template <class TYPE, class BaseClass>
class TArray : public BaseClass
{
/*
This class is the classical array implementation.
*/
public:
    typedef TYPE                    Type;
public: // This may should be protected or private, but...
    TYPE* m_pData; // Data Type Pointer - classic data array pointer
    int m_nSize; // Exact Array Size - size of array being used
protected:
    int m_nGrowBy; // Number used to determine the allocate unit.
private:
    int m_nSizeMax; // Allocated Array Size - It could be modified by SetGrowBy()... - size of array allocated (memory allocation)
public: // Construction & Desturction
    TArray()
    {
        // as is...
        m_pData = NULL;
        m_nSize = m_nSizeMax = 0;
        m_nGrowBy = 100;
    }
    ~TArray()
    {
        // as is...
        Reset();
    }
    void RemoveAll()
    {
        // remove all the elements, and free all the memory used.
        // but no destruction of object-itself.
        // IMPORTANT: don't call this method, when constructor/destructor should be called.
        if (m_pData) {
            ASSERT(m_nSizeMax > 0);

            free(m_pData);
            m_pData = NULL;
            m_nSize = m_nSizeMax = 0;
        } else {
            ASSERT((m_nSize == 0) && (m_nSizeMax == 0));
        }
    }
    void Reset()
    {
        // same with RemoveAll
        RemoveAll();
    }
    void Swap(TArray<TYPE, BaseClass>& that)
    {
        SwapPointer(m_pData, that.m_pData);
        SwapValue(m_nSize, that.m_nSize);
        SwapValue(m_nGrowBy, that.m_nGrowBy);
        SwapValue(m_nSizeMax, that.m_nSizeMax);
    }
    BOOL IsEqual(const TArray<TYPE, BaseClass>& that) const
    {
        int nSize = m_nSize;

        if (nSize == that.m_nSize) {
            if (nSize > 0) {
                if (memcmp(m_pData, that.m_pData, nSize * sizeof(TYPE)) == 0) {
                    return TRUE;
                }
            } else {
                return TRUE;
            }
        }

        return FALSE;
    }
	BOOL operator == (const TArray<TYPE, BaseClass>& that) const
    {
        return IsEqual(that);
    }
	BOOL operator != (const TArray<TYPE, BaseClass>& that) const
    {
        return !IsEqual(that);
    }
    void SetAllValue(const TYPE one)
    {
        int i, nSize = m_nSize;
        TYPE* pDest = m_pData;

        for(i = 0; i < nSize; i++) {
            *pDest = one;
            pDest++;
        }
    }
    void SetAll(const TYPE& one)
    {
        int i, nSize = m_nSize;
        TYPE* pDest = m_pData;

        for(i = 0; i < nSize; i++) {
            *pDest = one;
            pDest++;
        }
    }
    int SetGrowBy(int nGrowBy)
    {
        ASSERT(nGrowBy >= 0);

        // swap m_nGrowBy with nGrowBy, and return nGrowBy(old value)...
        SwapValue(m_nGrowBy, nGrowBy);

        return nGrowBy;
    }
    int GetSize() const
    {
        // as is...
        return m_nSize;
    }
    void SetSize(int nSize)
    {
        ASSERT(nSize >= 0);
        int nDiff = nSize - m_nSize;
        if (nDiff != 0) {
            if (nDiff < 0) {
                // shrink
                Del(m_nSize + nDiff, -nDiff);
            } else {
                // grow
                Add(nDiff);
            }
        }
    }
    void ResetSize(int nSize)
    {
        Reset();
        SetSize(nSize);
    }
    TYPE At(int nIndex) const               // return data
    {
        // used to retrive values(as like char, int, long...)
        // usually this method used retrieve small(accuping 1 or 2 CPU register) objects

        ASSERT((0 <= nIndex) && (nIndex < m_nSize));

        return m_pData[nIndex];
    }
    TYPE& GetAt(int nIndex) const           // return reference to data
    {
        // used to retrive structures(as like POINT, RECT, MSG...)
        // usually this method used retrieve large(accuping 2 or more CPU registers) objects

        ASSERT(0 <= nIndex); // (nIndex < m_nSize) - used as pointer?

        return m_pData[nIndex];
    }
    TYPE& operator [] (int nIndex) const    // return reference to data
    {
        // same with "TYPE& GetAt(int nIndex) const"

        ASSERT(0 <= nIndex); // (nIndex < m_nSize) - used as pointer?

        return m_pData[nIndex];
    }
	int Find(TYPE data) const
	{
		int i, nSize = m_nSize;
		TYPE* pData = m_pData;

		for(i = 0; i < nSize; i++) {
			if (*pData == data)
				return i;
			pData++;
		}

		return -1;
	}
    TYPE& Add(int nRoom = 1)                // return reference to the first slot added
    {
        // add nRoom element(s), and return its starting memory reference...

        ASSERT(nRoom > 0);

        TYPE* pData = m_pData;

        nRoom += m_nSize;
        if (nRoom > m_nSizeMax) {           // insufficient slot. grow array
            int nSizeMax = nRoom + m_nGrowBy;
            if (pData) {
                pData = (TYPE*)realloc(pData, sizeof(TYPE) * nSizeMax);
            } else {
                pData = (TYPE*)malloc(sizeof(TYPE) * nSizeMax);
            }
#if 0
            if (!pData)
                return *(TYPE*)NULL;
#endif
            m_pData = pData;
            m_nSizeMax = nSizeMax;
        }

        pData = &m_pData[m_nSize];          // point to the first slot added
        m_nSize = nRoom;

        return *pData;
    }
    TYPE& Ins(int nIndex, int nRoom = 1)    // return reference to the first slot inserted
    {
        // insert nRoom element(s) before nIndex, and return its starting memory reference...
        ASSERT(nRoom > 0);
        ASSERT((0 <= nIndex) && (nIndex <= m_nSize));

        int nSize = m_nSize - nIndex;       // number of array to move
        TYPE* pData = &Add(nRoom);

        if (nSize) {
            int nCount = nSize * sizeof(TYPE);

            TYPE* pDataSrc = (TYPE*)((char*)pData - nCount);
            memmove(&pDataSrc[nRoom], pDataSrc, nCount);

            return *pDataSrc;
        }
        return *pData;
    }
    void FreeExtra()
    {
        // used to free extra memories(make m_nSizeMax same with m_nSize)...
        if (m_pData) {
            if (m_nSize) {
                // realloc to smaller size: always successive. 
                m_pData = (TYPE*)realloc(m_pData, sizeof(TYPE) * m_nSize);
            } else {
                free(m_pData);
                m_pData = NULL;
            }
            m_nSizeMax = m_nSize;
        } else {
            ASSERT(m_nSize == 0); // (m_nSizeMax == 0)
        }
    }
    void Del(int nIndex, int nCount = 1)
    {
        // delete nCount element(s) where nIndex...
        ASSERT(nCount > 0);
        ASSERT((0 <= nIndex) && (nIndex <= m_nSize));

        int nSize = m_nSize;

        if (nIndex < nSize) {
            ASSERT(nIndex + nCount <= nSize);

            TYPE* pDataDst = &m_pData[nIndex];

            nIndex += nCount;
            int nMove = nSize - nIndex;
            if (nMove) {
                memcpy(pDataDst, &pDataDst[nCount], nMove * sizeof(TYPE));
            }
            m_nSize = nSize - nCount;
        } else {
            TRACE("TArray::Del() : deletion at the out of bound\n");
            ASSERT(nIndex == nSize);
        }
    }
	int DelFind(TYPE data)
	{
		int nIndex = Find(data);

		if (0 <= nIndex) {
			Del(nIndex);
		}

		return nIndex;
	}
    void Append(const TArray<TYPE, BaseClass>& that)
    {
        // append array...
        int nSize = that.m_nSize;

        if (nSize > 0) {
            memcpy(&Add(nSize), that.m_pData, nSize * sizeof(TYPE));
        }
    }
    void Assign(const TArray<TYPE, BaseClass>& that)
    {
        // append array...
        int nSize = that.m_nSize;
        SetSize(nSize);

        if (nSize > 0) {
            memcpy(m_pData, that.m_pData, nSize * sizeof(TYPE));
        }
    }
    TArray<TYPE, BaseClass>& operator = (const TArray<TYPE, BaseClass>& that)
    {
        Assign(that);
        
        return *this;
    }
	void operator *= (const TArray<TYPE, BaseClass>& that)
	{
		int nSize = m_nSize;
		ASSERT(nSize > 0);
		ASSERT(nSize == that.GetSize());

		TYPE* pStart = m_pData;
		TYPE* pEnd = &pStart[nSize];

		const TYPE* pSrc = that.m_pData;

		while(pStart < pEnd) {
			*pStart *= *pSrc;
			pSrc++;
			pStart++;
		}
	}
	void operator /= (const TArray<TYPE, BaseClass>& that)
	{
		int nSize = m_nSize;
		ASSERT(nSize > 0);
		ASSERT(nSize == that.GetSize());

		TYPE* pStart = m_pData;
		TYPE* pEnd = &pStart[nSize];

		const TYPE* pSrc = that.m_pData;

		while(pStart < pEnd) {
			*pStart /= *pSrc;
			pSrc++;
			pStart++;
		}
	}
	void operator += (const TArray<TYPE, BaseClass>& that)
	{
		int nSize = m_nSize;
		ASSERT(nSize > 0);
		ASSERT(nSize == that.GetSize());

		TYPE* pStart = m_pData;
		TYPE* pEnd = &pStart[nSize];

		const TYPE* pSrc = that.m_pData;

		while(pStart < pEnd) {
			*pStart += *pSrc;
			pSrc++;
			pStart++;
		}
	}
	void operator -= (const TArray<TYPE, BaseClass>& that)
	{
		int nSize = m_nSize;
		ASSERT(nSize > 0);
		ASSERT(nSize == that.GetSize());

		TYPE* pStart = m_pData;
		TYPE* pEnd = &pStart[nSize];

		const TYPE* pSrc = that.m_pData;

		while(pStart < pEnd) {
			*pStart -= *pSrc;
			pSrc++;
			pStart++;
		}
	}
	void operator *= (TYPE tValue)
	{
		int nSize = m_nSize;
		ASSERT(nSize > 0);

		TYPE* pStart = m_pData;
		TYPE* pEnd = &pStart[nSize];

		while(pStart < pEnd) {
			*pStart *= tValue;
			pStart++;
		}
	}
	void operator /= (TYPE tValue)
	{
		ASSERT(tValue != 0);

		int nSize = m_nSize;
		ASSERT(nSize > 0);

		TYPE* pStart = m_pData;
		TYPE* pEnd = &pStart[nSize];

		while(pStart < pEnd) {
			*pStart /= tValue;
			pStart++;
		}
	}
	void operator += (TYPE tValue)
	{
		int nSize = m_nSize;
		ASSERT(nSize > 0);

		TYPE* pStart = m_pData;
		TYPE* pEnd = &pStart[nSize];

		while(pStart < pEnd) {
			*pStart += tValue;
			pStart++;
		}
	}
	void operator -= (TYPE tValue)
	{
		int nSize = m_nSize;
		ASSERT(nSize > 0);

		TYPE* pStart = m_pData;
		TYPE* pEnd = &pStart[nSize];

		while(pStart < pEnd) {
			*pStart -= tValue;
			pStart++;
		}
	}
};

template <class TYPE, class CON, class DES, class BaseClass>
class TArrayDynamic : public TArray<TYPE, BaseClass>
{
/*
This class is used to call constructor or destructor when enlarged or reduced.
CON: constructor template(TConNNone, TConNCon, TConNAZ, TConNFZ)
DES: destructor template(TDesNNone, TDesNCon, TDesNAZ, TDesNFZ)
*/
public:
    ~TArrayDynamic()
    {
        Reset();
    }
    void ConstructAll()
    {
        CON::Construct(m_pData, m_nSize);
    }
    void DestructAll()
    {
        DES::Destruct(m_pData, m_nSize);
    }
    void Reset()
    {
        // call the specified destructor(see DES)
        DES::Destruct(m_pData, m_nSize);
        TArray<TYPE, BaseClass>::Reset();
    }
    BOOL IsEqual(const TArrayDynamic<TYPE, CON, DES, BaseClass>& that) const
    {
        int nSize = m_nSize;

        if (nSize == that.m_nSize) {
            if (nSize > 0) {
                const TYPE* pDest = m_pData;
                const TYPE* pSrc = that.m_pData;
                for(int i = 0; i < nSize; i++) {
                    if (!(*pDest == *pSrc)) {
                        return FALSE;
                    }
                    pDest++;
                    pSrc++;
                }
            }
            return TRUE;
        }

        return FALSE;
    }
	BOOL operator == (const TArrayDynamic<TYPE, CON, DES, BaseClass>& that) const
    {
        return IsEqual(that);
    }
	BOOL operator != (const TArrayDynamic<TYPE, CON, DES, BaseClass>& that) const
    {
        return !IsEqual(that);
    }
    void SetSize(int nSize)
    {
        ASSERT(nSize >= 0);
        int nDiff = nSize - m_nSize;
        if (nDiff != 0) {
            if (nDiff < 0) {
                // shrink
                Del(m_nSize + nDiff, -nDiff);
            } else {
                // grow
                Add(nDiff);
            }
        }
    }
    void ResetSize(int nSize)
    {
        Reset();
        SetSize(nSize);
    }
    TYPE& Add(int nRoom = 1)
    {
        ASSERT(nRoom >= 0);

        TYPE* pData;

        if (nRoom) {
            pData = &TArray<TYPE, BaseClass>::Add(nRoom);
            CON::Construct(pData, nRoom);
        } else {
            pData = NULL;
        }

        return *pData;
    }
    TYPE& Ins(int nIndex, int nRoom = 1)
    {
        ASSERT((0 <= nIndex) && (nIndex <= m_nSize) && (nRoom >= 0));

        TYPE* pData;

        if (nRoom) {
            pData = &TArray<TYPE, BaseClass>::Ins(nIndex, nRoom);
            CON::Construct(pData, nRoom);
        } else {
            pData = NULL;
        }

        return *pData;
    }
    void Del(int nIndex, int nCount = 1)
    {
        ASSERT(nCount > 0);
        ASSERT((0 <= nIndex) && (nIndex < m_nSize));

        TYPE* pData = &m_pData[nIndex];

        DES::Destruct(pData, nCount);
        TArray<TYPE, BaseClass>::Del(nIndex, nCount);
    }
	int DelFind(TYPE data)
	{
		int nIndex = Find(data);

		if (0 <= nIndex) {
			Del(nIndex);
		}

		return nIndex;
	}
    void Append(const TArrayDynamic<TYPE, CON, DES, BaseClass>& that)
    {
        // append array...
        int nSize = that.m_nSize;
        TYPE* pDest = &Add(nSize);
        const TYPE* pSrc = that.m_pData;

        for(int i = 0; i < nSize; i++) {
            *pDest = *pSrc;
            pDest++;
            pSrc++;
        }
    }
    void Assign(const TArrayDynamic<TYPE, CON, DES, BaseClass>& that)
    {
        // append array...
        int nSize = that.m_nSize;
        SetSize(nSize);

        TYPE* pDest = m_pData;
        const TYPE* pSrc = that.m_pData;

        for(int i = 0; i < nSize; i++) {
            *pDest = *pSrc;
            pDest++;
            pSrc++;
        }
    }
    TArrayDynamic<TYPE, CON, DES, BaseClass>& operator = (const TArrayDynamic<TYPE, CON, DES, BaseClass>& that)
    {
        Assign(that);
        
        return *this;
    }
};

template <class TYPE, class BaseClass>
class TArraySimple : public TArrayDynamic<TYPE, TConNFZ<TYPE>, TDesNNone<TYPE>, BaseClass>
{
public:
    BOOL IsEqual(const TArraySimple<TYPE, BaseClass>& that) const
    {
        return TArray<TYPE, BaseClass>::IsEqual(that);
    }
	BOOL operator == (const TArraySimple<TYPE, BaseClass>& that) const
    {
        return TArray<TYPE, BaseClass>::IsEqual(that);
    }
	BOOL operator != (const TArraySimple<TYPE, BaseClass>& that) const
    {
        return !TArray<TYPE, BaseClass>::IsEqual(that);
    }
    void Append(const TArraySimple<TYPE, BaseClass>& that)
    {
        // append array...
        int nSize = that.m_nSize;
        if (nSize > 0) {
            memcpy(&TArray<TYPE, BaseClass>::Add(nSize), that.m_pData, nSize * sizeof(TYPE));
        }
    }
    void Assign(const TArraySimple<TYPE, BaseClass>& that)
    {
        // append array...
        int nSize = that.m_nSize;
        TArray<TYPE, BaseClass>::SetSize(nSize);

        if (nSize > 0) {
            memcpy(m_pData, that.m_pData, nSize * sizeof(TYPE));
        }
    }
    TArraySimple<TYPE, BaseClass>& operator = (const TArraySimple<TYPE, BaseClass>& that)
    {
        Assign(that);

        return *this;
    }
};

#endif // !defined(__ARRAY__)
