// Compressed file archive interface

// File_Extractor 1.0.0
#ifndef FILE_EXTRACTOR_H
#define FILE_EXTRACTOR_H

#include "blargg_common.h"
#include "Data_Reader.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 blargg_wchar_t* wname() const        { return wname_; }
	blargg_err_t stat();
	BOOST::uint64_t 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 );
	BOOST::uint64_t 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 blargg_wchar_t* wname = NULL );
	
	// Sets current file information
	void set_info( BOOST::uint64_t 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, long 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 blargg_wchar_t* wname_;
	unsigned    date_;
	unsigned    crc32_;
	BOOST::uint64_t 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, long 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
