/***************************************************************************
 *                          Zanoza Modeler v2.x                            *
 *      This is unpublished propietary source code of Zanoza Software.     *
 *      The copyright notice does not evidence any actual or intended      *
 *                    publication of such source code.                     *
 *                                                                         *
 *                   Copyright (c) 2002 Zanoza Software.                   *
 *                                                                         *
 *                        All rights reserved.                             *
 ***************************************************************************
 *--------------------------------------------------------------------------
 * @author:   Oleg M.
 *--------------------------------------------------------------------------
 * @purpose:  ZModeler 2.x sequence template
 *--------------------------------------------------------------------------
 * @histroy: 23.09.2002. Created
 * @histroy: 08.02.2006. VS8 upgrade
 */
#ifndef zsequence_h_included
#define zsequence_h_included

template <class T>
class /*DLL_LINKAGE*/ ZSequence
{
private:
  T*    m_pBuffer;
  DWORD m_dwBufSize;
  long  m_nLength;
  bool  m_bRelease;
public:
  inline bool isowned() const {return m_bRelease;}
  static void copybuf(T* dest, const T* src, long items)
  {
    for (long i = 0; i < items; i++)
      dest[i] = src[i];
  }
  static T* allocbuf()
  {
    return new T[maximum()];
  }
  static T* allocbuf(DWORD dwSize)
  {
    return new T[dwSize];
  }
  static void freebuf(T* buffer)
  {
    if (buffer)
      delete[] buffer;
  }
public:
  ZSequence()
  {
    m_pBuffer   = NULL;
    m_dwBufSize = 0;
    m_nLength  = 0;
    m_bRelease  = true;
  }
  ZSequence(DWORD max)
  {
    m_pBuffer   = allocbuf(max);
    m_dwBufSize = max;
    m_nLength  = 0;
    m_bRelease  = true;
  }
  ZSequence(DWORD max, long length, T* data, bool release = false)
  {
    if (release)
    {
      m_pBuffer   = allocbuf(max);
      copybuf(m_pBuffer, data, length);
    }
    else
      m_pBuffer = data;
    m_dwBufSize = max;
    m_nLength  = length;
    m_bRelease  = release;
  }
  ZSequence(const ZSequence& seq)
  {
    m_dwBufSize = seq.maximum();
    m_nLength  = seq.length();
    m_bRelease  = seq.isowned();
    if (m_bRelease)
    {
      if (m_dwBufSize > 0)
      {
        m_pBuffer = allocbuf(m_dwBufSize);
        copybuf(m_pBuffer, seq.getBuffer(), m_nLength);
      }
      else
        m_pBuffer = NULL;
    }
    else
      m_pBuffer = seq.m_pBuffer;
  }
  ~ZSequence()
  {
    if (m_bRelease)
      freebuf(m_pBuffer);
  }

  ZSequence& operator = (const ZSequence& seq)
  {
    if (m_bRelease)
    {
      length(seq.length());
      copybuf(m_pBuffer, seq.getBuffer(), m_nLength);
    }
    else
    {
      m_dwBufSize = seq.maximum();
      m_nLength  = seq.length();
      m_bRelease  = seq.isowned();
      m_pBuffer = seq.m_pBuffer;
    }
    return *this;
  }

  // equality operator
  bool operator == (const ZSequence& seq) const
  {
    if (m_nLength != seq.length())
      return false;
    for (long i = 0; i < m_nLength; i++)
      if (m_pBuffer[i] != seq.m_pBuffer[i])
        return false;
    return true;
  }

  // inequality operator
  bool operator != (const ZSequence& seq) const
  {
    return !operator==(seq);
  }

  //-------------------------------------------------------
  // @name : share
  // shares the data with supplied sequence
  // @param const ZSequence& seq           : 
  //
  // @return ZSequence& share : 
  //-------------------------------------------------------
  ZSequence& share (const ZSequence& seq)
  {
    m_dwBufSize = seq.maximum();
    m_nLength  = seq.length();
    if (m_bRelease)
      freebuf(m_pBuffer);
    m_pBuffer = (T*)seq.getBuffer();
    m_bRelease = false; //we don't own, just share
    return *this;
  }

  inline DWORD maximum() const
  {
    return m_dwBufSize;
  }
  
  void length(long newLength)
  {
    T* pNewBuffer = NULL;
    if ((unsigned)newLength > m_dwBufSize)//realloc buffer
    {
      DWORD dwSize = (m_dwBufSize == 0) ? newLength : (newLength+1+newLength/4);
      pNewBuffer = allocbuf(dwSize);
      copybuf(pNewBuffer, m_pBuffer, m_nLength);
      if (m_bRelease && m_pBuffer)
        freebuf(m_pBuffer);
      m_pBuffer = pNewBuffer;
      m_dwBufSize = dwSize;
      m_bRelease = true;
    }
    m_nLength = newLength;
  }
  inline long length() const
  {
    return m_nLength;
  }

  T& operator[](long index)
  {
    if ((unsigned)index >= m_dwBufSize)
    {//realloc
      T* pNewBuffer = allocbuf(index+1+(index<<2));
      copybuf(pNewBuffer, m_pBuffer, m_dwBufSize);
      freebuf(m_pBuffer);
      m_pBuffer = pNewBuffer;
      m_dwBufSize = index+1+(index<<2);
      m_bRelease = true;
    }
    if (index >= m_nLength)
      m_nLength = index+1;//index is zero-based
    return m_pBuffer[index];
  }
  //const accessor
  const T& operator[](const long index) const
  {
    return m_pBuffer[index];
  }

  void replace(DWORD max, long length, T* data, bool release = false)
  {
    //variouse optimization can be applied here
    T* pOldBuf = m_pBuffer;
    m_dwBufSize = max;
    m_nLength  = length;
    if (release)
    {
      m_pBuffer = allocbuf(max);
      copybuf(m_pBuffer, data, length);
    }
    else
      m_pBuffer = data;
    if (m_bRelease && pOldBuf)
      freebuf(pOldBuf);
    m_bRelease = release;
  }

  T* getBuffer(bool orphan = false)
  {
    if (orphan && !m_bRelease)
      return NULL;//can't provide such a buffer
    if (orphan)
      m_bRelease = false;
    return m_pBuffer;
  }

  const T* getBuffer() const
  {
    return m_pBuffer;
  }

  //added:
  void append(long length, T* data)
  {
    T* pBuf = m_pBuffer;
    if ((long)(m_dwBufSize - m_nLength) < length)
    {//alloc new;
      long newLen = m_nLength + length;
      DWORD dwSize = (newLen+1+newLen/4);
      pBuf = allocbuf(dwSize);
      //copy data;
      copybuf(pBuf, m_pBuffer, m_nLength);
      copybuf((T*)((BYTE*)pBuf + sizeof(T)*m_nLength), data, length);
      if (m_bRelease && m_pBuffer)
        freebuf(m_pBuffer);
      m_pBuffer = pBuf;
      m_dwBufSize = dwSize;
      m_bRelease = true;
    }
    else
      copybuf((T*)((BYTE*)m_pBuffer + sizeof(T)*m_nLength), data, length);
    m_nLength += length;
  }

  //add single item at the end of the buffer
  void add(T item)
  {
    if (0 == (m_dwBufSize - m_nLength))
    {//alloc new;
      T* pBuf = allocbuf(m_nLength+1+m_nLength/4);
      //copy data;
      copybuf(pBuf, m_pBuffer, m_nLength);
      pBuf[m_nLength] = item;
      if (m_bRelease && m_pBuffer)
        freebuf(m_pBuffer);
      m_pBuffer = pBuf;
      m_dwBufSize = m_nLength+1+m_nLength/4;
      m_bRelease = true;
    }
    else
      m_pBuffer[m_nLength] = item;
    m_nLength++;
  }

  //inserts single item at specified position
  //pos is zero-based; call with pos == m_nLength
  //equal to add(item);
  void insert(T item, long pos) //:TRICKY: don't change 'T item' to 'T& item'!!!
  {
    if (pos > m_nLength)
      pos = m_nLength;
    if (0 == (m_dwBufSize - m_nLength))
    {//alloc new;
      T* pBuf = allocbuf(m_nLength+1+m_nLength/4);
      //copy data;
      copybuf(pBuf, m_pBuffer, pos);
      pBuf[pos] = item;
      copybuf((T*)((BYTE*)pBuf + sizeof(T)*(pos+1)), (T*)((BYTE*)m_pBuffer + sizeof(T)*pos), m_nLength-pos);
      if (m_bRelease && m_pBuffer)
        freebuf(m_pBuffer);
      m_pBuffer = pBuf;
      m_dwBufSize = m_nLength+1+m_nLength/4;
      m_bRelease = true;
    }
    else
    {
      for (long i = m_nLength; i > pos; i--)
        m_pBuffer[i] = m_pBuffer[i-1];
      m_pBuffer[pos] = item;
    }
    m_nLength++;
  }

  //erases single item at specified position.
  void erase(long pos)
  {
    if (pos < 0)
      return;
    else
      if (pos >= m_nLength)
        return;
    copybuf((T*)&m_pBuffer[pos], (T*)&m_pBuffer[pos+1], m_nLength-pos-1);
    m_nLength--;
  }

  //clears the sequence.
  void clear()
  {
    if (m_bRelease && m_pBuffer)
    {
      freebuf(m_pBuffer);
      m_pBuffer = NULL;
      m_dwBufSize = 0;
    }
    m_nLength = 0;
  }

  //tells whether sequence contains supplied element
  bool contains(const T item) const
  {
    for (long i = 0; i < m_nLength; i++)
      if (m_pBuffer[i] == item)
        return true;
    return false;
  }

  //position of supplied item in array. -1 if not found
  long indexOf(const T item) const
  {
    for (long i = 0; i < m_nLength; i++)
      if (m_pBuffer[i] == item)
        return i;
    return -1;
  }
};

//---------------------------------------------------
// several standard sequences:
typedef ZSequence<bool>   tBoolSeq;
typedef ZSequence<BYTE>   tBYTESeq;
typedef ZSequence<long>   tLongSeq;
typedef ZSequence<short>  tShortSeq;
typedef ZSequence<float>  tFloatSeq;
typedef ZSequence<DWORD>  tULongSeq;
typedef ZSequence<WORD>   tUShortSeq;
#ifndef ZM_NO_MFC
typedef ZSequence<CWnd*>  tCWndSeq;
#endif

#endif //zsequence_h_included
