/* Copyright (c) 2015  Gerald Knizia
 * 
 * This file is part of the IboView program (see: http://www.iboview.org)
 * 
 * IboView is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3.
 * 
 * IboView is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with bfint (LICENSE). If not, see http://www.gnu.org/licenses/
 * 
 * Please see IboView documentation in README.txt for:
 * -- A list of included external software and their licenses. The included
 *    external software's copyright is not touched by this agreement.
 * -- Notes on re-distribution and contributions to/further development of
 *    the IboView software
 */

#ifndef CX_PODARRAY_H
#define CX_PODARRAY_H

#include <string.h> // for memcpy
#include <stdlib.h> // for malloc/free

namespace ct {

// swap two primitive values. Here such that we need not include <algorithm> here.
template<class FType>
void swap1(FType &A, FType &B){
   FType t = A; A = B; B = t;
}


// A dynamic array of POD (``plain old data'') types that can be
// copied via a memcpy. Main point for this is that std::vector does not
// allow containing C-style arrays (e.g., double [3]), because C-style
// arrays are not assignable. Additionally, std::vector can be *very* slow
// when allocating large amounts of data, because the data is set to
// zero on resize. This class explicitly does not do that: It has RAII
// semantics, but non-explicitly touched data is just random.
//
// It is effectively a 'buffer-ptr + size' pair.
template<class FType>
struct TArray
{
   typedef FType *iterator;
   typedef FType const *const_iterator;
   typedef ::size_t size_type;
   typedef FType value_type;

   iterator begin() { return m_pData; }
   iterator end() { return m_pDataEnd; }
   const_iterator begin() const { return m_pData; }
   const_iterator end() const { return m_pDataEnd; }

   FType &front() { return *m_pData; }
   FType &back() { return *(m_pDataEnd-1); }
   FType const &front() const { return *m_pData; }
   FType const &back() const { return *(m_pDataEnd-1); }

   FType &operator[] (size_type i) { return m_pData[i]; }
   FType const &operator[] (size_type i) const  { return m_pData[i]; }

   size_type size() const { return m_pDataEnd - m_pData; }
   bool empty() const { return m_pData == m_pDataEnd; }

   // WARNING: contrary to std::vector, this function *DOES NOT*
   // initialize (or touch, for that matter) the newly created data.
   // If you want that behavior, use the other resize function.
   void resize(size_type n) {
      reserve(n);
      m_pDataEnd = m_pData + n;
   };

   void resize(size_type n, FType t) {
      size_type old_size = size();
      resize(n);
      if ( old_size < n ) {
         for ( size_type i = old_size; i < n; ++ i )
            m_pData[i] = t;
      }
   };

   void clear() {
      ::free(m_pData);
      m_pData = 0;
      m_pDataEnd = 0;
      m_pReservedEnd = 0;
   };

   // memset the entire array to 0.
   void clear_data() {
      memset(m_pData, 0, sizeof(FType)*size());
   };

   void resize_and_clear(size_type n) {
      resize(n);
      clear_data();
   }

   void push_back( FType const &t ) {
      if ( size() + 1 > static_cast<size_type>(m_pReservedEnd - m_pData) ) {
         reserve(2 * size() + 1);
      }
      m_pDataEnd[0] = t;
      ++m_pDataEnd;
   };

   void pop_back() {
      assert(!empty());
      m_pDataEnd -= 1;
   }

   void reserve(size_type n) {
      if ( static_cast<size_type>(m_pReservedEnd - m_pData) < n ) {
         FType *pNewData = static_cast<FType*>(::malloc(sizeof(FType) * n));
         size_type
            nSize = size();
         if ( nSize != 0 )
            ::memcpy(pNewData, m_pData, sizeof(FType) * nSize);
         ::free(m_pData);
         m_pData = pNewData;
         m_pDataEnd = m_pData + nSize;
         m_pReservedEnd = m_pData + n;
      }
   };


   TArray()
      : m_pData(0), m_pDataEnd(0), m_pReservedEnd(0)
   {}

   TArray(TArray const &other)
      : m_pData(0), m_pDataEnd(0), m_pReservedEnd(0)
   {
      *this = other;
   };

   // WARNING: array's content not initialized with this function!
   // (with intention!)
   explicit TArray(size_type n)
      : m_pData(0), m_pDataEnd(0), m_pReservedEnd(0)
   {
      resize(n);
   }

   TArray(size_type n, FType t)
      : m_pData(0), m_pDataEnd(0), m_pReservedEnd(0)
   {
      resize(n);
      for ( size_type i = 0; i < n; ++ i )
         m_pData[i] = t;
   }

   ~TArray() {
      ::free(m_pData);
      m_pData = 0;
   }

   void operator = (TArray const &other) {
      resize(other.size());
      memcpy(m_pData, other.m_pData, sizeof(FType) * size());
   };

   void swap(TArray &other) {
      swap1(m_pData, other.m_pData);
      swap1(m_pDataEnd, other.m_pDataEnd);
      swap1(m_pReservedEnd, other.m_pReservedEnd);
   };

   template<class FRandomIt>
   void assign(FRandomIt begin, FRandomIt end){
      resize(end - begin);
      for ( size_type i = 0; i < size(); ++ i )
         m_pData[i] = begin[i];
   }

   template<class FRandomIt>
   void insert(iterator pos, FRandomIt begin, FRandomIt end){
      // WARNING: not checked.
      size_type num = end - begin, ipos = pos - this->begin();
      resize(num + this->size());
      for ( size_type i = this->size(); i != ipos+num; -- i )
         this->m_pData[i-1] = this->m_pData[i-1-num];
      for ( size_type i = 0; i < num; ++ i )
         this->m_pData[ipos+i] = begin[i];
   }

private:
   FType
      // start of controlled array. may be 0 if no data is contained.
      *m_pData,
      // end of actual data
      *m_pDataEnd,
      // end of reserved space (i.e., of the space *this owns)
      *m_pReservedEnd;
};

} // namespace ct

namespace std {
   template<class FType>
   void swap( ct::TArray<FType> &A, ct::TArray<FType> &B ) {
      A.swap(B);
   }
}


#endif // CX_PODARRAY_H
