#ifndef FIFO_H
#define FIFO_H

template <typename T>
class fifo
{
public:
        fifo( int sz = 100);
        fifo( const fifo &rhs);
        ~fifo();
    fifo& operator=( const fifo &rhs);

    int  size() const { return len; }
    bool is_empty() const { return 0 == len; }
    bool is_full() const { return cap == len; }

    void push( const T &t);
    T    pop();
    void print( std::ostream &os);

private:
    void release();
    void copy( const fifo &rhs);

    int cap;
    int top;
    int len;
    T   *v;
};

template <typename T>
fifo<T>::fifo( int sz)
{
    cap = sz;
    top = 0;
    len = 0;

    v = new T[cap];
}

template <typename T>
fifo<T>::~fifo()
{
    release();
}

template <typename T>
fifo<T>::fifo( const fifo &rhs)
{
    copy(rhs);
}

template <typename T>
fifo<T> &fifo<T>::operator=( const fifo &rhs)
{
    if ( this != &others )
    {
        release();
        copy(rhs);  
    }
    return *this;
}

template <typename T>
void fifo<T>::release()
{
    delete [] v;
}

template <typename T>
void fifo<T>::copy( const fifo &rhs)
{
    cap = rhs.cap;
    top = rhs.top;
    len = rhs.len;

    v = new T[cap];

    for ( int i = 0; i < cap; ++i)
    {
        v[i] = rhs.v[i];
    }       
}

template <typename T>
void fifo<T>::push( const T &t)
{
    if ( ! is_full() )
    {
        v[top] = t;
        top =  top==cap-1 ? 0 : top+1;
        ++len;
    }
}

template <typename T>
T fifo<T>::pop()
{
    if ( ! is_empty() )
    {
        int bottom = top-len;
        if ( bottom < 0 )
            bottom += cap;
        --len;
        return v[bottom];
    }
    else
        return T();
}

template <typename T>
void fifo<T>::print( std::ostream &os)
{
    os << std::endl; 
    os << "top = " << top << std::endl;
    os << "len = " << len << std::endl;
    os << "[ ";
    for ( int i = 0; i < cap; ++i)
        os << v[i] << " ";
    os << "]" << std::endl;
}

#endif /* FIFO_H */