//
// tardy - a tar post-processor
// Copyright (C) 1998, 1999, 2003, 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_TAR_OUTPUT_H
#define LIBTARDY_TAR_OUTPUT_H

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

#include <libtardy/tar/header.h>

/**
  * The tar_output class is used to represent an abstract output stream
  * containing a sequence of file headers and file data.
  *
  * The expected sequence of operations is
  *                                                                     <pre>
  *                 |
  *     +-----------------------+
  *     | write_archive_begin   |
  *     +-----------------------+
  *                 |
  *                 | ,------------------<---------------.
  *                 |/                                    \
  *                 V                                      |
  *                 |                                      |
  *     +-----------------------+                          |
  *     |     write_header      |                          |
  *     +-----------------------+                          |
  *                 |                                      |
  *     +-----------------------+                          |
  *     | write_header_padding  |                          |
  *     +-----------------------+                          |
  *                 |                                      |
  *                 *------->-------.                      ^
  *                 |                \                     |
  *                 |                 |                    |
  *                 |                 | ,------<-----.     |
  *                 |                 |/              \    |
  *                 |                 V                |   |
  *                 |                 |                |   |
  *                 |     +-----------------------+    |   |
  *                 |     |      write_data       |    ^   |
  *                 |     +-----------------------+    |   |
  *                 |                 |               /    |
  *                 |                 *-------->-----'     |
  *                 |                 |                    |
  *                 |                /                     |
  *                 | ,-----<-------'                      ^
  *                 |/                                     |
  *                 V                                      |
  *                 |                                      |
  *                 |                                      |
  *     +-----------------------+                          |
  *     |  write_data_padding   |                          |
  *     +-----------------------+                          |
  *                 |                                     /
  *                 *-------------------->---------------'
  *                 |
  *     +-----------------------+
  *     |   write_archive_end   |
  *     +-----------------------+
  *                 |                                                   </pre>
  */
class tar_output
{
public:
    /**
      * The pointer type should be used for all pointers to output
      * instances, as it does reference counting a resource clean up at
      * the appropriate time.
      */
    typedef boost::shared_ptr<tar_output> pointer;

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

    /**
      * The fatal method is used to report fatal errors.  The name of
      * the output file will be prepended.
      *
      * @param fmt
      *     The format controlling the string to be printed.  See
      *     printf(3) for a description of the formats and their
      *     arguments.
      */
    void fatal(const char *fmt, ...) const ATTR_PRINTF(2, 3);

    /**
      * The write_header method is used to write file meta-data to
      * the output stream.  Must be called exactly once for each file.
      * The size in the header shall exactly match the amount of data
      * written with the write_data methods which follow.
      *
      * @param hdr
      *     The header information to be written.
      */
    virtual void write_header(const tar_header &hdr) = 0;

    /**
      * The write_header_padding is used to write padding to bring
      * the output stream to a suitable byte multiple.  Must be called
      * exactly once for each file, after the header has been written.
      */
    virtual void write_header_padding(void);

    /**
      * The write_data method is used to write file data to the output
      * stream.  The file header must be written first.  Must be called
      * zero or more times for each file.
      *
      * @param data
      *     A pointer to the array of byte data to be written.
      * @param data_size
      *     The size in bytes of the data to be written.
      *     assert(data_size > 0);
      */
    virtual void write_data(const void *data, int data_size) = 0;

    /**
      * The write_data_padding is used to write padding to bring the
      * output stream to a suitable byte multiple.  Must be called
      * exactly once for each file, after all the data has been written.
      */
    virtual void write_data_padding(void);

    /**
      * The filename method is used to retrieve the name of the output
      * file, which the archive is being written to.
      */
    virtual rcstring filename(void) const = 0;

    /**
      * The get_format_name method may be used to obtain the name of the
      * file format in use.
      *
      * Derived classes shall make every attempt to return strings that
      * can be used with the tardy -ofmt command line option.
      */
    virtual const char *get_format_name(void) const = 0;

    /**
      * The write_archive_begin method is used to begin an archive.
      * It shall be called before the first file header is written.
      */
    virtual void write_archive_begin(void);

    /**
      * The write_archive_begin method is used to end an archive.
      * It shall be called after the last file data padding has been
      * written.
      */
    virtual void write_archive_end(void);

    /**
      * The set_block_size method is used to set the output block size
      * in bytes, the multiple of which will always be written to the
      * output.  This largely controls the number of bytes of padding at
      * the end of the archive.  The default is no padding.
      *
      * @param nbytes
      *     The number of bytes per block.
      *     assert(nbytes > 0);
      *     assert((nbytes % 512) == 0);
      */
    virtual void set_block_size(long nbytes);

    /**
      * The get_maximum_name_length method may be used to obtain the
      * longest name that may be stored in the archive (as opposed to
      * the longest name actually present in the archive).
      *
      * @returns
      *     The longest filename that may be present in the archive in
      *     bytes (<i>not</i> characters).
      *     A value of zero means "effectively infinite", or more
      *     accurately "how much memory do you have?".
      */
    virtual size_t get_maximum_name_length(void) const = 0;

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

    /**
      * The calculate_mode method is used to determine an st_mode
      * equalivalent for the hdr.type and hdr.mode fields.
      *
      * @param hdr
      *     The header to extract information from.
      * @returns
      *     the appropriate mode field, combining type and permission mmode bits
      */
    int calculate_mode(const tar_header &hdr) const;

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

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

#endif // LIBTARDY_TAR_OUTPUT_H
