/*---------------------------------------------------------------------------------------------------------------------
- File      : constrained_circular_buffer.h                                     EPFL - LIA -  C/C++ PersonalToolkit   -
-                                                                                             Datas Structure Tools   -
- Author    : Seydoux Florian   Creation date : 16 June 1999                                                          -
- Eulogist  : -                 Approval date : -                  Version: 0.1                                       -
-                                                                                                                     -
- Descript. : Define and implement (with template) a Generic Constrained Circular Buffer.                             -
-             To minimize the allocated memory, technique with empty cell is not used here.                           -
-             Naturally, that implies a slight cost for the push/pop methods.                                         -
-                                                                                                                     -
- Gaps      : o) All (Actually, it's just an adaptor of deque (an unconstrained buffer)                               -
-                                                                                                                     -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Rev. date | Reviser               | Revise's description                                                            -
- - - - - - + - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ../../....| ........              | ...                                                                             -
---------------------------------------------------------------------------------------------------------------------*/


#ifndef CONSTRAINED_CIRCULAR_BUFFER_H
#define CONSTRAINED_CIRCULAR_BUFFER_H

#include<iterator>
#include<cassert>

#include<deque>

#ifdef _USE_NAMESPACES
    using namespace std;
#endif // _USE_NAMESPACES

#ifdef __MWERKS__   // bug ds version 3.1
    template <class T, class size_type = unsigned int, const MAXSIZE = 100>
#else
    template <class T, class size_type = unsigned int, const size_type MAXSIZE = 100>
#endif
class ConstrainedCircularBuffer: public virtual deque<T> {
public:
    inline ConstrainedCircularBuffer(const size_type maxSize = MAXSIZE): deque<T>(), max(maxSize) {}
    inline void push(const T& item)  { assert(size()<max); push_front(item); }
    inline T pop()                   { assert(!empty()); T t(back()); pop_back(); return t; }
    inline bool full() const { return (size()==max); }

private:
    size_type max;     // Nb total d'element stockable
};

/*

class ConstrainedCircularBuffer {
      class iterator;
      class const_iterator;
      friend class iterator;
      friend class const_iterator;

private:     
      size_range   capacity;     // Nb total d'element stockable
      size_range   count;        // Nb d'element actuellement en stock
      size_range   pushIndex;    // Pointe la premiere cellule vide (= prochain pop)
      size_range   popIndex;     // Pointe 1 avant la premiere cellule pleine
      T*           collection;

public:

    inline ~ConstrainedCircularBuffer() {
        delete[] collection;
    }

    inline ConstrainedCircularBuffer()
    : capacity(MAXSIZE),
      count(0),
      pushIndex(0),
      popIndex(capacity-1),
      collection(new T[capacity])
    { }

    inline ConstrainedCircularBuffer(const SizeRange maxSize)
    : capacity(maxSize),
      count(0),
      pushIndex(0),
      popIndex(capacity-1),
      collection(new T[capacity])
    { }

    inline ConstrainedCircularBuffer(const SizeRange maxSize, const T& initialValue)
    : capacity(maxSize),
      count(maxSize),
      pushIndex(0),
      popIndex(capacity-1),
      collection(new T[capacity])
    {
        for (SizeRange i = 0; i < capacity; ++i)
            collection[i] = initialValue;
    }

    inline ConstrainedCircularBuffer(const SizeRange maxSize, const SizeRange nbinit, const T& initialValue)
    : capacity(maxSize),
      count(nbinit),
      pushIndex(0),
      popindex(capacity-1),
      collection(new T[capacity])
    {
        for (SizeRange i = 0; i < nbInit; ++i)
            collection[i] = initialValue;
    }
    
    inline ConstrainedCircularBuffer(const ConstrainedCircularBuffer& mirror) // semi deep copy
    : capacity(mirror.capacity),
      count(mirror.count),
      pushIndex(mirror.pushIndex),
      popIndex(mirror.popIndex),
      collection(new T[capacity])
    {
        memcpy(collection, mirror.collection, sizeof(T)*count); // Rev. 08/03/1999
//      for (SizeRange i = 0; i < capacity; ++i) collection[i] = mirror.collection[i];
    }
  
    inline iterator begin()                                 { return iterator(); } --
    inline iterator end()                                   { return iterator(0,); } --
    inline const_iterator begin() const                     { return const_iterator(collection); }
    inline const_iterator end() const                       { return const_iterator(collection+count); }

    inline void clear()                                     { pushIndex = (popIndex+1) % capacity; count = 0; }
    inline SizeRange size() const                           { return count; }
    inline SizeRange maxLength() const                      { return capacity; }
    inline bool empty() const                               { return (count == 0); }

    inline void push(const T& item)                         { push_front(t); }
    inline T& pop()                                         { return pop_back(); }
    
    inline void push_front(const T& item) {
        assert(count<capacity);
        collection[push] = item;
        push = (push+1) % capacity;
        ++count;
    }

    inline void push_back(const T& item) {
        assert(count<capacity);
        collection[pop] = item;
        pop = (pop + capacity - 1) % capacity;
        ++count;
    }

    inline T& pop_back() {
        assert(count>0);
        pop = (pop+1) % capacity;
        --count;
        return collection[pop];
    }

    inline T& pop_front() {
        assert(count>0);
        push = (push + capacity - 1) % capacity;
        --count;
        return collection[push];
    }
    
    inline T& back() const {
        assert(count>0);
        return collection[(pop+1) % capacity];
    }
    inline T& front() const {
        assert(count>0);
        return collection[(push+capacity-1) % capacity];
    }
    
    inline T* at(const SizeRange index)                     { return ((count>index) ? &(collection[index]) : NULL); }
    inline const T* at(const SizeRange index) const         { return ((count>index) ? &(collection[index]) : NULL); }
    inline T& operator[](const SizeRange index)             { assert(count>index); return collection[index]; }
    inline const T& operator[](const SizeRange index) const { assert(count>index); return collection[index]; }

    inline void resize(const SizeRange length)  { 
        delete[] collection;
        count = 0;
        capacity = length;
        collection = new T[capacity+1];
    }

    inline void resize(const SizeRange length, const T& initialValue)  { 
        delete[] collection;
        count = length;
        capacity = length;
        collection = new T[capacity+1];
        for (SizeRange i = 0; i < capacity; ++i) collection[i] = initialValue;
    }
    
    inline ConstrainedCircularBuffer& operator=(const ConstrainedCircularBuffer& mirror) { // semi deep copy
        if (&mirror != this) {
            if (capacity != mirror.capacity) {
                delete[] collection;
                capacity = mirror.capacity;
                collection = new T[capacity+1];
            }
            count = mirror.count;
            memcpy(collection, mirror.collection, sizeof(T)*count); // Rev. 08/03/1999
//            for (SizeRange i = 0; i < capacity; ++i)      // On copie aussi les éléments 'non présent' ?!?
//              collection[i] = mirror.collection[i];       // Il existe surement des primitives plus efficaces.
           }
        return *this;
    }

    inline ConstrainedCircularBuffer& operator=(const T& value) {        // uniforme assignement
        for (SizeRange i = 0; i < capacity; ++i)
            collection[i] = value;
        return *this;
    }
    

    // Définition partielle d'un random access iterator.
    class iterator { // : public random_access_iterator<T> ou iterator_tag ... any !
        friend class const_iterator;
        T* p;
    public:
        inline iterator(): p(NULL)                                { }
        inline iterator(T*const x): p(x)                          { }
        inline iterator(const iterator& x): p(x.p)                { }
        inline operator const_iterator() const                    { return const_iterator(p); }
        inline T& operator*() const                               { return *p; }
        inline T* operator->() const                              { return p;  }
        inline T& operator[] (const SizeRange i) const            { return *(*this + i); }
        inline iterator& operator++()                             { ++p; return *this; }
        inline iterator  operator++(int)                          { return iterator(p++); }
        inline iterator& operator--()                             { --p; return *this; }
        inline iterator  operator--(int)                          { return iterator(p--); }
        inline iterator& operator+=(const SizeRange i)            { p += i; return *this; }
        inline iterator& operator-=(const SizeRange i)            { p -= i; return *this; }
        inline iterator  operator+(const SizeRange i) const       { return iterator(p+i); }
        inline iterator  operator-(const SizeRange i) const       { return iterator(p-i); }
        inline SizeRange operator-(const iterator& x) const       { return static_cast<SizeRange>(p - x.p); }
        inline SizeRange operator-(const const_iterator& x) const { return static_cast<SizeRange>(p - x.p); }
        inline bool operator==(const iterator& x) const           { return p == x.p; }
        inline bool operator==(const const_iterator& x) const     { return p == x.p; }
        inline bool operator!=(const iterator& y) const           { return !(*this == y); }
        inline bool operator!=(const const_iterator& y) const     { return !(*this == y); }
        inline bool operator< (const iterator& x) const           { return p < x.p; }
        inline bool operator< (const const_iterator& x) const     { return p < x.p; }
        inline bool operator> (const iterator& y) const           { return  (y < *this); }
        inline bool operator> (const const_iterator& y) const     { return  (y < *this); }
        inline bool operator<=(const iterator& y) const           { return !(y < *this); }
        inline bool operator<=(const const_iterator& y) const     { return !(y < *this); }
        inline bool operator>=(const iterator& y) const           { return !(*this < y); }
        inline bool operator>=(const const_iterator& y) const     { return !(*this < y); }
    };
    
    class const_iterator {
        friend class iterator;
        const T* p;
    public:
        inline const_iterator(): p(NULL)                          { }
        inline const_iterator(const T*const x): p(x)              { }
        inline const_iterator(const const_iterator& x): p(x.p)    { }
        inline const T& operator*() const                         { return *p; }
        inline const T* operator->() const                        { return p; }
        inline const T& operator[] (const SizeRange i) const      { return *(*this + i); }
        inline const_iterator& operator++()                       { ++p; return *this; }
        inline const_iterator  operator++(int)                    { return const_iterator(p++); }
        inline const_iterator& operator--()                       { --p; return *this; }
        inline const_iterator  operator--(int)                    { return const_iterator(p--); }
        inline const_iterator& operator+=(const SizeRange i)      { p += i; return *this; }
        inline const_iterator& operator-=(const SizeRange i)      { p -= i; return *this; }
        inline const_iterator  operator+(const SizeRange i) const { return const_iterator(p+i); }
        inline const_iterator  operator-(const SizeRange i) const { return const_iterator(p-i); }
        inline SizeRange operator-(const iterator& x) const       { return static_cast<SizeRange>(p - x.p);  }
        inline SizeRange operator-(const const_iterator& x) const { return static_cast<SizeRange>(p - x.p);  }
        inline bool operator==(const iterator& x) const           { return p == x.p; }
        inline bool operator==(const const_iterator& x) const     { return p == x.p; }
        inline bool operator!=(const iterator& y) const           { return !(*this == y); }
        inline bool operator!=(const const_iterator& y) const     { return !(*this == y); }
        inline bool operator< (const iterator& x) const           { return p < x.p; }
        inline bool operator< (const const_iterator& x) const     { return p < x.p; }
        inline bool operator> (const iterator& y) const           { return  (y < *this); }
        inline bool operator> (const const_iterator& y) const     { return  (y < *this); }
        inline bool operator<=(const iterator& y) const           { return !(y < *this); }
        inline bool operator<=(const const_iterator& y) const     { return !(y < *this); }
        inline bool operator>=(const iterator& y) const           { return !(*this < y); }
        inline bool operator>=(const const_iterator& y) const     { return !(*this < y); }
    };
};
*/

#endif // CONSTRAINED_CIRCULAR_BUFFER

