//
// tardy - a tar post-processor
// Copyright (C) 1998, 1999, 2004, 2008, 2009, 2011 Peter Miller
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//

#ifndef LIBTARDY_FILE_INPUT_H
#define LIBTARDY_FILE_INPUT_H

#include <libtardy/config.h>
#include <boost/shared_ptr.hpp>

#include <libtardy/rcstring.h>

/**
  * The file_input class represents an abstract base class for input
  * data sources.  It shall be derived from before it can be used.
  */
class file_input
{
public:
    typedef boost::shared_ptr<file_input> pointer;

    /**
      * The destructor.
      */
    virtual ~file_input();

    /**
      * The factory method is used to create a new file_input instance,
      * based on the file name.
      *
      * @param filename
      *     The name of the file to open for writing.  If the NULL
      *     pointer is upplied, or the empty string is upplied, or the
      *     special name "-" is upplied, it will read from the standard
      *     input; otherwise, it will read from the named file.
      */
    static pointer factory(const char *filename);

    /**
      * The fatal method is used to report fatal errors occurring when
      * operating on this output stream.  The program name and the file
      * name are prepended.  It exits with an exit status of one.  It
      * does not return.
      *
      * @param fmt
      *     The message format to print.  See printf(3) for a
      *     description of the format and its arguments.
      */
    void fatal(const char *fmt, ...) const ATTR_PRINTF(2, 3);

    /**
      * The read method is used to read data from the file.  It only
      * returns if there is no error.
      *
      * @param data
      *     The location to write the data read from the file.
      * @param data_size
      *     The maximum number of bytes read into the buffer from the file.
      * @returns
      *     The number of bytes read into the buffer, or 0 for end of file.
      */
    int read(void *data, size_t data_size);

    /**
      * The peek method is used to read data from the file, without
      * moving the file read position.  It only returns if there is no
      * error, and the exact number of bytes were available.
      *
      * @param data
      *     The location to write the data read from the file.
      * @param data_size
      *     The number of bytes read into the buffer from the file.
      */
    int peek(void *data, size_t data_size);

    /**
      * The filename method is used to determine the name of the file.
      */
    virtual rcstring filename(void) const = 0;

    /**
      * The unread method is used to "push back" unused input.
      *
      * @param data
      *     The datat to be returned.
      * @param data_size
      *     The amount of data to be returned, in bytes.
      *
      * @note
      *     While you are at liberty to "return" bytes that weren't
      *     actually read (although why you would lie to yourself like
      *     that is a mystery) you shall not return more bytes than were
      *     read, otherwise the value reported by the #get_position
      *     method will be incorrect.
      */
    void unread(const void *data, size_t data_size);

    /**
      * The skip method is used to discard input data.
      *
      * @param nbytes
      *     The number of bytes of input data to discard.  It will be an error
      *     if the input does nlt contain at least this many bytes.
      */
    void skip(size_t nbytes);

    /**
      * The get_position method may be used to obtain the current file
      * position, in bytes from the start of the file.  Any "unread"
      * bytes will be taken into account.
      */
    unsigned long get_position(void) const;

protected:
    /**
      * The default constructor.
      * Only derived classes may use this constructor.
      */
    file_input();

    /**
      * The read_inner method is supplied by derived classes, to
      * implement that actual machinery of reading data.
      */
    virtual int read_inner(void *data, size_t data_size) = 0;

    /**
      * The get_position_inner method may be used to obtain the current
      * file position, in bytes from the start of the file.
      */
    virtual unsigned long get_position_inner(void) const = 0;

private:
    /**
      * The read_ahead_buffer instance variable is used to remember the
      * dynamically allocated array for any read-ahead data that may
      * have been unread (or peek()ed) previously.
      */
    char *read_ahead_buffer;

    /**
      * The read_ahead_size instance variable is used to remember
      * the size in bytes of the dynamically allocated array for any
      * read-ahead data.
      */
    size_t read_ahead_size;

    /**
      * The read_ahead_pos instance variable is used to remember how
      * much of the read-ahead buffer has been used.  The used data lies
      * between read_ahead_pos and read_ahead_size; that is, the array
      * is used from back to front.
      */
    size_t read_ahead_pos;

    /**
      * The valid method is used to determine whether or not the read
      * ahead buffer is in a valid state.  Used in combination with
      * assert().
      */
    bool valid(void) const;

    bool
    read_ahead_empty(void)
        const
    {
        return (read_ahead_pos == read_ahead_size);
    }

    /**
      * The copy constructor.  Do not use.
      *
      * @param rhs
      *     The right hand side of the initialization.
      */
    file_input(const file_input &rhs);

    /**
      * The assignment operator.  Do not use.
      *
      * @param rhs
      *     The right hand side of the assignment.
      */
    file_input &operator=(const file_input &rhs);
};

#endif // LIBTARDY_FILE_INPUT_H
