// Compressed file archive interface

// File_Extractor 1.0.0
#ifndef FILE_EXTRACTOR_H
#define FILE_EXTRACTOR_H

#include "Data_Reader.h"
#include "blargg_common.h"
#include "fex.h"

struct fex_t : private Data_Reader {
        public:
        virtual ~fex_t();

        // Open/close

        // Opens archive from custom data source. Keeps pointer until close().
        blargg_err_t open(File_Reader *input, const char *path = NULL);

        // Takes ownership of File_Reader* passed to open(), so that close()
        // will delete it.
        void own_file()
        {
                own_file_ = reader_;
        }

        // See fex.h
        blargg_err_t open(const char path[]);
        fex_type_t type() const
        {
                return type_;
        }
        void close();

        // Scanning

        // See fex.h
        bool done() const
        {
                return done_;
        }
        blargg_err_t next();
        blargg_err_t rewind();
        fex_pos_t tell_arc() const;
        blargg_err_t seek_arc(fex_pos_t);

        // Info

        // See fex.h
        const char *name() const
        {
                return name_;
        }
        const wchar_t *wname() const
        {
                return wname_;
        }
        blargg_err_t stat();
        int size() const
        {
                assert(stat_called);
                return size_;
        }
        unsigned int dos_date() const
        {
                return date_;
        }
        unsigned int crc32() const
        {
                return crc32_;
        }

        // Extraction

        // Data_Reader to current file's data, so standard Data_Reader interface can
        // be used, rather than having to treat archives specially. stat() must have
        // been called.
        Data_Reader &reader()
        {
                assert(stat_called);
                return *this;
        }

        // See fex.h
        blargg_err_t data(const void **data_out);
        int tell() const
        {
                return size_ - remain();
        }

        // Derived interface
        protected:
        // Sets type of object
        fex_t(fex_type_t);

        // Path to archive file, or "" if none supplied
        const char *arc_path() const
        {
                return path_.begin();
        }

        // Opens archive file if it's not already. If unbuffered is true, opens file
        // without any buffering.
        blargg_err_t open_arc_file(bool unbuffered = false);

        // Archive file
        File_Reader &arc() const
        {
                return *reader_;
        }

        // Sets current file name
        void set_name(const char name[], const wchar_t *wname = NULL);

        // Sets current file information
        void set_info(int size, unsigned date = 0, unsigned crc = 0);

        // User overrides

        // Overrides must do indicated task. Non-pure functions have reasonable default
        // implementation. Overrides should avoid calling public functions like
        // next() and rewind().

        // Open archive using file_path(). OK to delay actual file opening until later.
        // Default just calls open_arc_file(), then open_v().
        virtual blargg_err_t open_path_v();

        // Open archive using file() for source data. If unsupported, return error.
        virtual blargg_err_t open_v() BLARGG_PURE(;)

            // Go to next file in archive and call set_name() and optionally set_info()
            virtual blargg_err_t next_v() BLARGG_PURE(;)

            // Go back to first file in archive
            virtual blargg_err_t rewind_v() BLARGG_PURE(;)

            // Close archive. Called even if open_path_v() or open_v() return unsuccessfully.
            virtual void close_v() BLARGG_PURE(;)

            // Clear any fields related to current file
            virtual void clear_file_v()
        {
        }

        // Call set_info() if not already called by next_v()
        virtual blargg_err_t stat_v()
        {
                return blargg_ok;
        }

        // Return value that allows later return to this file. Result must be >= 0.
        virtual fex_pos_t tell_arc_v() const;

        // Return to previously saved position
        virtual blargg_err_t seek_arc_v(fex_pos_t);

        // One or both of the following must be overridden

        // Provide pointer to data for current file in archive
        virtual blargg_err_t data_v(const void **out);

        // Extract next n bytes
        virtual blargg_err_t extract_v(void *out, int n);

        // Implementation
        public:
        BLARGG_DISABLE_NOTHROW

        private:
        fex_type_t const type_;

        // Archive file
        blargg_vector<char> path_;
        File_Reader *reader_;
        File_Reader *own_file_;
        bool opened_;

        // Position in archive
        fex_pos_t tell_; // only used by default implementation of tell/seek
        bool done_;

        // Info for current file in archive
        const char *name_;
        const wchar_t *wname_;
        unsigned date_;
        unsigned crc32_;
        int size_;
        bool stat_called;

        // Current file contents
        void const *data_ptr_; // NULL if not read into memory
        blargg_vector<char> own_data_;

        bool opened() const
        {
                return opened_;
        }
        void clear_file();
        void close_();
        blargg_err_t set_path(const char *path);
        blargg_err_t rewind_file();
        blargg_err_t next_();

        // Data_Reader overrides
        // TODO: override skip_v?
        virtual blargg_err_t read_v(void *out, int n);
};

struct fex_type_t_ {
        const char *extension;
        File_Extractor *(*new_fex)();
        const char *name;
        blargg_err_t (*init)(); // Called by fex_init(). Can be NULL.
};

extern const fex_type_t_ fex_7z_type[1], fex_gz_type[1], fex_rar_type[1], fex_zip_type[1],
    fex_bin_type[1];

inline blargg_err_t File_Extractor::open_v()
{
        return blargg_ok;
}
inline blargg_err_t File_Extractor::next_v()
{
        return blargg_ok;
}
inline blargg_err_t File_Extractor::rewind_v()
{
        return blargg_ok;
}
inline void File_Extractor::close_v()
{
}

// Default to Std_File_Reader for archive access
#ifndef FEX_FILE_READER
#define FEX_FILE_READER Std_File_Reader
#elif defined(FEX_FILE_READER_INCLUDE)
#include FEX_FILE_READER_INCLUDE
#endif

#endif
