/* vim: set ft=c: 
 * (C) Frank-Rene Schaefer */
#ifndef  __QUEX_INCLUDE_GUARD__BUFFER__LOADER__BYTE_LOADER
#define  __QUEX_INCLUDE_GUARD__BUFFER__LOADER__BYTE_LOADER

#include <quex/code_base/definitions>
#include <quex/code_base/MemoryManager>

QUEX_NAMESPACE_MAIN_OPEN

typedef struct QUEX_SETTING_USER_CLASS_DECLARATION_EPILOG QUEX_NAME(ByteLoader_tag) {
    QUEX_TYPE_STREAM_POSITION (*tell)(struct QUEX_NAME(ByteLoader_tag)* me);
    void                      (*seek)(struct QUEX_NAME(ByteLoader_tag)* me, 
                                      QUEX_TYPE_STREAM_POSITION Pos);
    size_t                    (*load)(struct QUEX_NAME(ByteLoader_tag)*, 
                                      void*, const size_t,
                                      bool*  end_of_stream_f);
    void                      (*delete_self)(struct QUEX_NAME(ByteLoader_tag)*);

    /* .compare_handle(A, B):
     *
     * Quex checks whether A and B are of the same class (comparing the
     * function pointer '.load'). The derived class can safely cast both
     * to its pointer type.
     *
     * Upon reset, the the new input handle might be the same as the old one.
     * Such a usage is against the design of the reset functions! To detect
     * these situations, the byte loader provides '.compare_handle()'.
     *
     * In case of doubt, return always 'false'. The only disadvantage is that
     * if the user makes the aforementioned error, he will not get a nice hint
     * upon crash.                                                           
     *
     * ByteLoader_FILE, ByteLoader_stream, and ByteLoader_POSIX implement the
     * function propperly.                                                   */
    bool  (*compare_handle)(const struct QUEX_NAME(ByteLoader_tag)* A, 
                            const struct QUEX_NAME(ByteLoader_tag)* B);

    /* .on_nothing(...)
     * 
     * When '.load' cannot provide anymore data, customized actions may be 
     * performed. 
     *
     * -- If '.on_nothing' is not defined, '.load' returns zero and the
     *    caller must assume that the stream terminated. 
     * -- Else, the user defined '.no_nothing()' function is called. If it
     *    returns 'true', '.load' tries again to load data. Else, '.load()'
     *    returns with zero. Then, the caller, again, must assume that the
     *    end of stream has been reached.                                    */
    bool  (*on_nothing)(struct QUEX_NAME(ByteLoader_tag)*, size_t TryN, size_t RequestN);

    E_Ownership  handle_ownership;
    E_Ownership  ownership;

    /* Upon construction, the stream handle may be setup to a particular 
     * position in the stream. This is going to be the reference position.   
     * The consideration of offsets is handled in this base class' functions.
     * The derived class does not need to know about an initial offset.      */
    QUEX_TYPE_STREAM_POSITION initial_position;

    /* It is crucial for 'seeking' in the stream whether the stream is in 
     * binary mode or not. If not, then character/byte number is not const.  */
    bool                      binary_mode_f;      /* In doubt, say 'false'.  */

    struct {
        QUEX_TYPE_STREAM_POSITION (*tell)(struct QUEX_NAME(ByteLoader_tag)*);
        void                      (*seek)(struct QUEX_NAME(ByteLoader_tag)*, 
                                          QUEX_TYPE_STREAM_POSITION);
        size_t                    (*load)(struct QUEX_NAME(ByteLoader_tag)*, void*, const size_t, 
                                          bool*);
    } derived;
} QUEX_NAME(ByteLoader);

QUEX_INLINE void  QUEX_NAME(ByteLoader_construct)(QUEX_NAME(ByteLoader)*                me, 
                                       QUEX_TYPE_STREAM_POSITION  (*tell)(QUEX_NAME(ByteLoader)* me),
                                       void                       (*seek)(QUEX_NAME(ByteLoader)* me, QUEX_TYPE_STREAM_POSITION Pos),
                                       size_t                     (*load)(QUEX_NAME(ByteLoader)*, void*, const size_t, bool*),
                                       void                       (*delete_self)(QUEX_NAME(ByteLoader)*),
                                       bool                       (*compare_handle)(const QUEX_NAME(ByteLoader)*, 
                                                                                    const QUEX_NAME(ByteLoader)*));
QUEX_INLINE void  QUEX_NAME(ByteLoader_seek_disable)(QUEX_NAME(ByteLoader)* me);
QUEX_INLINE bool  QUEX_NAME(ByteLoader_seek_is_enabled)(QUEX_NAME(ByteLoader)* me);
QUEX_INLINE bool  QUEX_NAME(ByteLoader_is_equivalent)(const QUEX_NAME(ByteLoader)* A, 
                                                      const QUEX_NAME(ByteLoader)* B);
QUEX_INLINE void  QUEX_NAME(ByteLoader_delete)(QUEX_NAME(ByteLoader)** me);

QUEX_NAMESPACE_MAIN_CLOSE

#include <quex/code_base/buffer/loader/ByteLoader_FILE>
#include <quex/code_base/buffer/loader/ByteLoader_stream>
#ifdef QUEX_OPTION_POSIX
#   include <quex/code_base/buffer/loader/ByteLoader_POSIX>    /* (tm) */
#endif
#if 0
#   include <quex/code_base/buffer/loader/ByteLoader_FreeRTOS> /* (tm) */
#   include <quex/code_base/buffer/loader/ByteLoader_PalmOS>   /* (tm) */
#endif

#endif /*  __QUEX_INCLUDE_GUARD__BUFFER__LOADER__BYTE_LOADER */
