//-----------------------------------------------------------
// Copyright Christian Arnault LAL-Orsay CNRS
// arnault@lal.in2p3.fr
// See the complete license in cmt_license.txt "http://www.cecill.info". 
//-----------------------------------------------------------

#ifndef __cmt_vector_h__
#define __cmt_vector_h__

#include <stdlib.h>

template <class T> class cmt_vector
{
public:
  typedef T   element_type;
  typedef T*  element_ptr;
  typedef T** frame_ptr;

  cmt_vector ()
    {
      _data = 0;
      _frames = 0;
      _size = 0;
    }

  cmt_vector (const cmt_vector& other)
    {
      _data = 0;
      _frames = 0;
      _size = 0;

      cmt_vector<T>& me = *this;

      extend (other._size);
      for (int i = 0; i < _size; i++)
        {
          me.element_at (i) = other.element_at (i);
        }
    }

  cmt_vector (int n)
    {
      _data = 0;
      _frames = 0;
      _size = 0;

      extend (n);
    }

  ~cmt_vector ()
    {
      if (_data != 0)
        {
          for (int i = 0; i < _frames; i++)
            {
              delete[] _data[i];
              _data[i] = 0;
            }
#ifdef CMT_USE_NEW_DELETE
          delete[] _data;
#else
          free (_data);
#endif
        }
      _data = 0;
      _frames = 0;
      _size = 0;
    }

  void push_back (const T& object)
    {
      extend (1);
      element_at (_size - 1) = object;
    }

  T& add ()
    {
      resize (size() + 1);
      return (back ());
    }

  void pop_back ()
    {
      if (_size > 0) _size--;
    }

  void erase (int index)
    {
      if ((_data == 0) ||
          (index < 0) ||
          (index >= _size))
        {
          return;
        }

      for (int i = index; i < (_size - 1); i++)
        {
          element_at (i) = element_at (i + 1);
        }

      _size--;
    }

  cmt_vector& operator = (const cmt_vector& other)
    {
      clear ();

      cmt_vector<T>& me = *this;

      extend (other._size);
      for (int i = 0; i < _size; i++)
        {
          element_at (i) = other.element_at (i);
        }

      return (me);
    }

  T& operator [] (int index) const
    {
      if ((_data == 0) ||
          (index < 0) ||
          (index >= _size))
        {
          static T object;
          return (object);
        }
      else
        {
          return (element_at (index));
        }
    }

  T& operator [] (int index)
    {
      if ((_data == 0) ||
          (index < 0) ||
          (index >= _size))
        {
          static T object;
          return (object);
        }
      else
        {
          return (element_at (index));
        }
    }

  T& back () const
    {
      if ((_data == 0) ||
          (_size == 0))
        {
          static T object;
          return (object);
        }
      else
        {
          return (element_at (_size - 1));
        }
    }

  T& back ()
    {
      if ((_data == 0) ||
          (_size == 0))
        {
          static T object;
          return (object);
        }
      else
        {
          return (element_at (_size - 1));
        }
    }

  void resize (int new_size)
    {
      if (new_size < 0) return;

      extend (new_size - _size);
      _size = new_size;
    }

  int size () const
    {
      return (_size);
    }

  void clear ()
    {
      _size = 0;
    }

  frame_ptr get_frame () const
      {
        return (_data);
      }

  int get_frame_number () const
      {
        return (_frames);
      }

  int get_frame_size () const
      {
        return (frame_size);
      }

private:

  enum {frame_size = 4};

  T& element_at (int index)
    {
      int frame = index / frame_size;
      return (_data[frame][index % frame_size]);
    }

  T& element_at (int index) const
    {
      int frame = index / frame_size;
      return (_data[frame][index % frame_size]);
    }

  int frames (int n)
    {
      return ((n == 0) ? 0 : ((n - 1) / frame_size) + 1);
    }

  void extend (int n)
    {
      if (n <= 0) return;

      _size += n;

      int f = frames (_size);
      if (f > _frames)
        {
          if (_data == 0)
            {

#ifdef CMT_USE_NEW_DELETE
              _data = new element_ptr [f];
#else
              _data = (frame_ptr) malloc (f * sizeof (element_ptr));
#endif

            }
          else
            {

#ifdef CMT_USE_NEW_DELETE
              frame_ptr new_data;

              new_data = new element_ptr [f];
              for (int i = 0; i < _frames; i++)
                {
                  new_data[i] = _data[i];
                }
              delete[] _data;
              _data = new_data;
#else
              _data = (frame_ptr) realloc (_data, f * sizeof (element_ptr));
#endif

            }

          for (int i = _frames; i < f; i++)
            {
              _data[i] = new T[frame_size];
            }

          _frames = f;
        }
    }

  frame_ptr _data;
  int _frames;
  int _size;
};

#endif
