// Copyright (C) 2004  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#undef DLIB_LZ77_BUFFER_KERNEl_ABSTRACT_
#ifdef DLIB_LZ77_BUFFER_KERNEl_ABSTRACT_

#include "../algs.h"

namespace dlib
{

    class lz77_buffer 
    {
        /*!
            INITIAL VALUE
                get_history_buffer_limit() == defined by constructor arguments
                get_lookahead_buffer_limit() == defined by constructor arguments
                get_history_buffer_size() == 0
                get_lookahead_buffer_size() == 0


            WHAT THIS OBJECT REPRESENTS
                This object represents a pair of buffers (history and lookahead buffers) 
                used during lz77 style compression.

                It's main function is to search the history buffer for long strings which
                match the contents (or a part of the contents) of the lookahead buffer.
                

            HISTORY AND LOOKAHEAD BUFFERS
                The buffers have the following structure:
                | history buffer | lookahead buffer |  <-- contents of buffers
                |  ...9876543210 | 0123456789...    |  <-- index numbers

                So this means that history_buffer(0) == 'r', history_buffer(1) == 'e'
                and so on.  And lookahead_buffer(0) == 'l', lookahead_buffer(1) == 'o'
                and so on.


                What shift_buffers() does in english:
                    This function just means that the buffers have their contents shifted
                    left by N elements and that elements shifted out of the lookahead buffer 
                    go into the history buffer.   An example will make it clearer.

                    Suppose that we have the following buffers before we apply shift_buffers()
                        history_buffer() == "hey" and
                        lookahead_buffer() == "lookahead buffer"
                    And in the same format as the above diagram it would be
                        | hey | lookahead buffer |  <-- contents of buffers
                        | 210 | 0123456789...    |  <-- index numbers

                    Applying shift_buffers(4) will give
                        lookahead_buffer() == "ahead buffer"
                        history_buffer() == "heylook" or "eylook" or "ylook" or "look"

                    You might be wondering why the history_buffer can resize itself in 
                    such a nondeterministic way.  It is just to allow a lot of freedom in the 
                    implementations of this object.                                  
        !*/

    public:

        lz77_buffer (
            unsigned long total_limit,
            unsigned long lookahead_limit            
        );
        /*!
            requires
                - 6 < total_limit < 32
                - 15 < lookahead_limit <= 2^(total_limit-2)
            ensures                
                - #*this is properly initialized
                - #get_history_buffer_limit() == 2^total_limit  - lookahead_limit
                - #get_lookahead_buffer_limit() == lookahead_limit
            throws
                - std::bad_alloc
        !*/

        virtual ~lz77_buffer (
        );
        /*!
            ensures
                - any resources associated with *this have been released
        !*/

        void clear(
        );
        /*!
            ensures
                - #*this has its initial value
            throws
                - std::bad_alloc
                    if this exception is thrown then #*this is unusable 
                    until clear() is called and succeeds
        !*/

        void shift_buffers (
            unsigned long N
        );
        /*!
            requires
                - N <= get_lookahead_buffer_size()
            ensures
                - #get_lookahead_buffer_size() == get_lookahead_buffer_size() - N
                - #get_history_buffer_size() >= N
                - #get_history_buffer_size() <= get_history_buffer_size()+N
                - #get_history_buffer_size() <= get_history_buffer_limit()
                - for all i where 0 <= i < N:
                    #history_buffer(N-1-i) == lookahead_buffer(i)
                - for all i where 0 <= i < #get_history_buffer_size()-N:
                    #history_buffer(N+i) == history_buffer(i)
                - for all i where 0 <= i < #get_lookahead_buffer_size()
                    #lookahead_buffer(i) == lookahead_buffer(N+i)            
        !*/

        void add (
            unsigned char symbol
        );
        /*!
            ensures
                - if (get_lookahead_buffer_size() == get_lookahead_buffer_limit()) then
                    - performs shift_buffers(1)
                    - #lookahead_buffer(get_lookahead_buffer_limit()-1) == symbol
                    - #get_lookahead_buffer_size() == get_lookahead_buffer_size()
                - else
                    - #lookahead_buffer(get_lookahead_buffer_size()) == symbol
                    - #get_lookahead_buffer_size() == get_lookahead_buffer_size() + 1                                    
            throws
                - std::bad_alloc
                    if this exception is thrown then #*this is unusable 
                    until clear() is called and succeeds
        !*/

        void find_match (
            unsigned long& index,
            unsigned long& length,
            unsigned long min_match_length
        );
        /*!
            ensures
                - if (#length != 0) then
                    - #length >= min_match_length
                    - for all i where 0 <= i < #length:
                      history_buffer(#index-i) == lookahead_buffer(i)
                    - performs shift_buffers(#length)
            throws
                - std::bad_alloc
                    if this exception is thrown then #*this is unusable 
                    until clear() is called and succeeds
        !*/

        unsigned long get_history_buffer_limit (
        ) const;
        /*!
            ensures
                - returns the max number of symbols that can fit in the history buffer
        !*/

        unsigned long get_lookahead_buffer_limit (
        ) const;
        /*!
            ensures
                - returns the max number of symbols that can fit in the lookahead buffer
        !*/

        unsigned long get_history_buffer_size (
        ) const;
        /*!
            ensures
                - returns the number of symbols currently in the history buffer
        !*/

        unsigned long get_lookahead_buffer_size (
        ) const;
        /*!
            ensures
                - returns the number of symbols currently in the lookahead buffer
        !*/

        unsigned char lookahead_buffer (
            unsigned long index
        ) const;
        /*!
            requires
                - index < get_lookahead_buffer_size()
            ensures
                - returns the symbol in the lookahead buffer at location index
        !*/

        unsigned char history_buffer (
            unsigned long index
        ) const;
        /*!
            requires
                - index < get_history_buffer_size()
            ensures
                - returns the symbol in the history buffer at location index
        !*/


    private:

        // restricted functions
        lz77_buffer(lz77_buffer&);        // copy constructor
        lz77_buffer& operator=(lz77_buffer&);    // assignment operator

    };      
}

#endif // DLIB_LZ77_BUFFER_KERNEl_ABSTRACT_