/// @file compressor.hpp
/// Compression/Decompression classes
///
/// @remarks
/// This is the base class from which all compressors are derived.
///
/// @author mouser
/// @date   2003.07.29
//---------------------------------------------------------------------------

/// @defgroup Compressors Compressors
///
/// The Base Compressor class implements our generic interface to all
/// compressors, and includes wrappers to measure compression/decompression
/// times and diskspace.

//---------------------------------------------------------------------------
// forward declarations
class OctaneCompressor;
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// recursive header protection
#ifndef CompressorH
#define CompressorH
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// application includes
#include "../core/octaneclass.hpp"
#include "../core/compressormanager.hpp"
#include "../bitio/bitio.hpp"
// system includes
#include <string>
//---------------------------------------------------------------------------



//---------------------------------------------------------------------------
/// The Base Compressor class implements our generic interface to all compressors,
///  and includes wrappers to measure compression/decompression times and diskspace.
///
/// @ingroup Compressors
///
class OctaneCompressor : public OctaneClass
{
public:
	/// constructor
	OctaneCompressor(bool registerme=false) {ResetStatistics();};
	/// destructor
	virtual ~OctaneCompressor() {;};
public:
	//---------------------------------------------------------------------------
	// OCTANE PUBLIC API - RTTI AND HELP FUNCTIONS - these are supported by all derived classes
	virtual std::string GetName() {return GetInstantiatedName();};
	virtual std::string GetDescription() {return "Base Compressor";};
	virtual std::string GetHelpInformation() {return "";};
	/// Return a universally unique unsigned-short-int id# for a predefined dictionary id file.
	/// This number is used to very compactly identify a preset compressor+statefile(dictionary) for use when transmission bandwidth is at a premium,
	/// such as in an online chat program with very short transmission (<<80 characters per transmission).
	/// @return 0 when this compressor+configuration has no globally-registered GUID.
	virtual unsigned short int GetCompressorStateFileGUID() {return guid;};
	//---------------------------------------------------------------------------
public:
	//---------------------------------------------------------------------------
	// COMPRESSOR PUBLIC API - FACTORY FUNCTION / MAKE AN INSTANTIATION OF DERIVED COMPRESSOR
	/// Instantiate a compressor from this class (like a factory)
	virtual OctaneCompressor *MakeCompressorInstantiation()=0;
	/// Get the assigned name of an instantiated compressor
	virtual std::string GetClassName() {return "Compressor";};
	/// Get the user-set instantiated object name for this compressor
	std::string GetInstantiatedName() {if (instantiatedname=="") return GetClassName()+std::string(".unnamed"); else return instantiatedname;};
	/// Get guid
	unsigned short int GetGuid() {return guid;};
	/// Set the user-set instantiated object name for this compressor
	void SetInstantiatedName(std::string val) {instantiatedname=val;};
	/// Set details (compressor name and guid) from file
	void SetInfo(std::string compressorname,unsigned short int compressorguid);
	//---------------------------------------------------------------------------
public:
	//---------------------------------------------------------------------------
	// COMPRESSOR PUBLIC API - PREPARATION FOR COMPRESSING/DECOMPRESSING
	/// Reset the state of the compressor to its initial state
	virtual void ResetState() {;};
	/// Synchronize state for a new stream - this will be called each time before beginning a new parsing stream
	virtual void SynchronizeStateForNewStream() {;};
	/// Prepare for parsing mode, this will be called parsing begins.
	/// @return true on success
	/// @todo is this a duplication of functionality in ResetState() and SynchronizeStateForNewStream()?
	virtual bool PrepareForCompression() {return true;};
	/// Some compressors contains parsers which begin ready to parse, without any 'training'.
	/// Here is where such a parser should be initialized.
	virtual void SetupAnyDefaultParser() {;};
	/// is the compressor ready to compress? Normally this is false until a dictionary is built or loaded.
	/// @return true on success
	virtual bool IsReadyToCompress() {return true;};
	//---------------------------------------------------------------------------
public:
	//---------------------------------------------------------------------------
	// COMPRESSOR PUBLIC API - COMPRESSING/DECOMPRESSING WRAPPERS (NOT VIRTUAL!)
	/// The Compress function is an API wrapper around a protected function which does the actual compression,
	///  in order to perform some timing/stats gathering functions.
	bool Compress(bitreader &from, bitwriter &to, bool needtowriteheader, bool savedecompressstateinfo);
	/// The Decompress function is an API wrapper around a protected function which does the actual compression,
	///  in order to perform some timing/stats gathering functions.
	/// @todo what should we do on a LoadCompressionHeader() error?
	bool Decompress(bitreader &from, bitwriter &to, bool needtoreadheader);
	///  Wrappers around the actual calls to DoProtected.., in order to perform some timing and space measurement.
	bool CreateSymbolsAndModelsUsingStream(bitreader &from);
	///  Wrappers around the actual calls to DoProtected.., in order to perform some timing and space measurement.
	///  This function also saves HEADER info which identifies the compressor class, which is read by CompressorManager
	bool Save(bitwriter &to,bool fortempdecompressiononly);
	///  Normally the compressor manager loads the header of a saved file and dynamically instantiates a compressor,
	///   but we provide this function for manually calling load on an already instantiated compressor.
	///  It reads the header, verifies it is for proper class, and then calls LoadState().
	bool Load(bitreader &from);
	///  Wrappers around the actual calls to DoProtected.., in order to perform some timing and space measurement.
	bool LoadState(bitreader &from);
	//---------------------------------------------------------------------------
protected:
	//---------------------------------------------------------------------------
	// COMPRESSOR PROTECTED DERIVED COMPRESSION/DECOMPRESSING
	/// This is the member function which does the actual compression, and this is the function that should be subclasses by derived classes.
	///  It is wrapped by the public API call Compress() which performs some measurements on compression speed and streamsize.
	/// @note this function should never be called by an outside class - the API interface should be called instead.
	/// @return true on success
	virtual bool DoProtectedCompress(bitreader &from, bitwriter &to)=0;
	/// This is the member function which does the actual decompression, and this is the function that should be subclasses by derived classes.
	///  It is wrapped by the public API call Decompress() which performs some measurements on decompression speed and streamsize.
	/// @note this function should never be called by an outside class - the API interface should be called instead.
	/// @return true on success
	virtual bool DoProtectedDecompress(bitreader &from, bitwriter &to)=0;
	//---------------------------------------------------------------------------
protected:
	//---------------------------------------------------------------------------
	// OCTANE PROTECTED DERIVED STATE LOADING AND SAVING
	/// This is the member function which does the actual training on an input in order to generate parsers/modelers/coders,
	///  and this is the function that should be subclasses by derived classes.
	///  It is wrapped by the public API call CreateSymbolsAndModelsUsingStream() which performs some measurements on speed and streamsize.
	/// @note this function should never be called by an outside class - the API interface should be called instead.
	/// @return true on success
	virtual bool DoProtectedCreateSymbolsAndModelsUsingStream(bitreader &from) {return true;};
	/// This is the member function which does the actual saving of state,
	///  and this is the function that should be subclasses by derived classes.
	///  It is wrapped by the public API call SaveState() which performs some measurements on speed and streamsize.
	/// @note this function should never be called by an outside class - the API interface should be called instead.
	/// @return true on success
	virtual bool DoProtectedSaveState(bitwriter &to,bool fortempdecompressiononly) {return true;};
	/// This is the member function which does the actual loading of state,
	///  and this is the function that should be subclasses by derived classes.
	///  It is wrapped by the public API call LoadState() which performs some measurements on speed and streamsize.
	/// @note this function should never be called by an outside class - the API interface should be called instead.
	/// @return true on success
	virtual bool DoProtectedLoadState(bitreader &from) {return true;};
	//---------------------------------------------------------------------------
public:
	//---------------------------------------------------------------------------
	// COMPRESSOR PUBLIC API - STATISTICS REPORTING
	// These methods just wrap the calls below inside some timing/stats gathering functions
	/// Reset the cumulative statistics tracking.
	void ResetStatistics();
	/// cumulative stats, can be reset with ResetCumulativeStatistics()
	unsigned int GetStatistics_Cumulative_Compression_BytesRead() {return Cumulative_Compression_BytesRead;};
	/// cumulative stats, can be reset with ResetCumulativeStatistics()
	unsigned int GetStatistics_Cumulative_Decompression_BytesRead() {return Cumulative_Decompression_BytesRead;};
	/// cumulative stats, can be reset with ResetCumulativeStatistics()
	unsigned int GetStatistics_Cumulative_Compression_BytesWritten() {return Cumulative_Compression_BytesWritten;};
	/// cumulative stats, can be reset with ResetCumulativeStatistics()
	unsigned int GetStatistics_Cumulative_Decompression_BytesWritten() {return Cumulative_Decompression_BytesWritten;};
	/// cumulative stats, can be reset with ResetCumulativeStatistics(); bit rate is in bits per bytes.
	float GetStatistics_Cumulative_Compression_Bitrate() {if (Cumulative_Compression_BytesRead==0) return 0; else return (float)8.0*(float)Cumulative_Compression_BytesWritten/(float)Cumulative_Compression_BytesRead;};
	/// cumulative stats, can be reset with ResetCumulativeStatistics(); bit rate is in bits per bytes.
	float GetStatistics_Cumulative_Decompression_Bitrate() {if (Cumulative_Decompression_BytesWritten==0) return 0; else return (float)8.0*(float)Cumulative_Decompression_BytesRead/(float)Cumulative_Decompression_BytesWritten;};
	/// cumulative stats, can be reset with ResetCumulativeStatistics(); bit ratio is % decompressedsize/compressedsize.
	float GetStatistics_Cumulative_Compression_Ratio() {if (Cumulative_Compression_BytesRead==0) return 0; else return (float)Cumulative_Compression_BytesWritten/(float)Cumulative_Compression_BytesRead;};
	/// cumulative stats, can be reset with ResetCumulativeStatistics(); bit ratio is % decompressedsize/compressedsize.
	float GetStatistics_Cumulative_Decompression_Ratio() {if (Cumulative_Decompression_BytesWritten==0) return 0; else return (float)Cumulative_Decompression_BytesRead/(float)Cumulative_Decompression_BytesWritten;};
	/// cumulative stats, can be reset with ResetCumulativeStatistics(); time is in seconds.
	float GetStatistics_Cumulative_Compression_ExecutionTime() {return Cumulative_Compression_ExecutionTime;};
	/// cumulative stats, can be reset with ResetCumulativeStatistics(); time is in seconds.
	float GetStatistics_Cumulative_Decompression_ExecutionTime() {return Cumulative_Decompression_ExecutionTime;};
	/// cumulative stats, can be reset with ResetCumulativeStatistics(); this returns the sum of compression+decompression times; time is in seconds.
	float GetStatistics_Cumulative_ExecutionTime() {return GetStatistics_Cumulative_Compression_ExecutionTime()+GetStatistics_Cumulative_Decompression_ExecutionTime();};
	/// cumulative stats, can be reset with ResetCumulativeStatistics(); number of errors since last reset.
	int GetStatistics_Cumulative_ErrorCount() {return Cumulative_ErrorCount;};
	// last operation stats
	/// returns the statistics for the last operation.
	unsigned int GetStatistics_LastOperation_Compression_BytesRead() {return LastOperation_Compression_BytesRead;};
	/// returns the statistics for the last operation.
	unsigned int GetStatistics_LastOperation_Decompression_BytesRead() {return LastOperation_Decompression_BytesRead;};
	/// returns the statistics for the last operation.
	unsigned int GetStatistics_LastOperation_Compression_BytesWritten() {return LastOperation_Compression_BytesWritten;};
	/// returns the statistics for the last operation.
	unsigned int GetStatistics_LastOperation_Decompression_BytesWritten() {return LastOperation_Decompression_BytesWritten;};
	/// returns the statistics for the last operation on state data.
	unsigned int GetStatistics_LastOperation_State_BytesRead() {return LastOperation_State_BytesRead;};
	/// returns the statistics for the last operation on state data.
	unsigned int GetStatistics_LastOperation_State_BytesWritten() {return LastOperation_State_BytesWritten;};
	/// returns the statistics for the last operation; bit rate is in bits per bytes.
	float GetStatistics_LastOperation_Compression_Bitrate() {if (LastOperation_Compression_BytesRead==0) return 0; else return (float)8.0*(float)LastOperation_Compression_BytesWritten/(float)LastOperation_Compression_BytesRead;};
	/// returns the statistics for the last operation; bit rate is in bits per bytes.
	float GetStatistics_LastOperation_Decompression_Bitrate() {if (LastOperation_Decompression_BytesWritten==0) return 0; else return (float)8.0*(float)LastOperation_Decompression_BytesRead/(float)LastOperation_Decompression_BytesWritten;};
	/// returns the statistics for the last operation; bit ratio is % decompressedsize/compressedsize.
	float GetStatistics_LastOperation_Compression_Ratio() {if (LastOperation_Compression_BytesRead==0) return 0; else return (float)LastOperation_Compression_BytesWritten/(float)LastOperation_Compression_BytesRead;};
	/// returns the statistics for the last operation; bit ratio is % decompressedsize/compressedsize.
	float GetStatistics_LastOperation_Decompression_Ratio() {if (LastOperation_Decompression_BytesWritten==0) return 0; else return (float)LastOperation_Decompression_BytesRead/(float)LastOperation_Decompression_BytesWritten;};
	/// returns the statistics for the last operation; time is in seconds.
	float GetStatistics_LastOperation_Compression_ExecutionTime() {return LastOperation_Compression_ExecutionTime;};
	/// returns the statistics for the last operation; time is in seconds.
	float GetStatistics_LastOperation_Decompression_ExecutionTime() {return LastOperation_Decompression_ExecutionTime;};
	/// returns the statistics for the last operation; this returns the sum of compression+decompression times; time is in seconds.
	float GetStatistics_LastOperation_ExecutionTime() {return GetStatistics_LastOperation_Compression_ExecutionTime()+GetStatistics_LastOperation_Decompression_ExecutionTime();};
	/// returns the statistics for the last state operation; time is in seconds.
	float GetStatistics_LastOperation_State_ExecutionTime() {return LastOperation_State_ExecutionTime;};
	/// returns number of errors during the last operation.
	/// @todo i don't think we currently reset this, so it may behave like cumulative error until we fix it.
	int GetStatistics_LastOperation_ErrorCount() {return LastOperation_ErrorCount;};
	/// kludge fixup to manually adjust counts (useful for when we read a header through a separate call)
	void Add_LastOperation_Compression_BytesRead(int val) {LastOperation_Compression_BytesRead+=val;};
	/// kludge fixup to manually adjust counts (useful for when we read a header through a separate call)
	void Add_LastOperation_Compression_BytesWritten(int val) {LastOperation_Compression_BytesWritten+=val;};
	/// kludge fixup to manually adjust counts (useful for when we read a header through a separate call)
	void Add_LastOperation_Decompression_BytesRead(int val) {LastOperation_Decompression_BytesRead+=val;};
	/// kludge fixup to manually adjust counts (useful for when we read a header through a separate call)
	void Add_LastOperation_Decompression_BytesWritten(int val) {LastOperation_Decompression_BytesWritten+=val;};
	//---------------------------------------------------------------------------
public:
	//---------------------------------------------------------------------------
	/// Save header info for a compression stream (either supershort 1 byte, or long state info)
	/// @note this is kind of ugly, maybe we can find a better solution.
	bool SaveCompressionHeader(bitwriter &to, bool savedecompressstateinfo);
	/// Load header info for a compression stream (either supershort 1 byte, or long state info)
	bool LoadCompressionHeader(bitreader &from);
	/// Save GUID field
	bool SaveGUID(bitwriter &to);
	/// Load GUID field
	/// @return the guid or -1 if its the NOGUID constant
	static int LoadGUID(bitreader &from);
	//---------------------------------------------------------------------------
protected:
	/// name of instantiated instance of this class
	std::string instantiatedname;
	/// unique short id for a compressor
	unsigned short int guid;
private:
	// statistics tracking
	/// statistics traced for compress/decompress/state operations
	unsigned int Cumulative_Compression_BytesRead;
	/// statistics traced for compress/decompress/state operations
	unsigned int Cumulative_Decompression_BytesRead;
	/// statistics traced for compress/decompress/state operations
	unsigned int Cumulative_Compression_BytesWritten;
	/// statistics traced for compress/decompress/state operations
	unsigned int Cumulative_Decompression_BytesWritten;
	/// statistics traced for compress/decompress/state operations
	float Cumulative_Compression_Bitrate;
	/// statistics traced for compress/decompress/state operations
	float Cumulative_Decompression_Bitrate;
	/// statistics traced for compress/decompress/state operations
	float Cumulative_Compression_Ratio;
	/// statistics traced for compress/decompress/state operations
	float Cumulative_Decompression_Ratio;
	/// statistics traced for compress/decompress/state operations
	float Cumulative_Compression_ExecutionTime;
	/// statistics traced for compress/decompress/state operations
	float Cumulative_Decompression_ExecutionTime;
	/// statistics traced for compress/decompress/state operations
	int Cumulative_ErrorCount;
	/// statistics traced for compress/decompress/state operations
	unsigned int LastOperation_Compression_BytesRead;
	/// statistics traced for compress/decompress/state operations
	unsigned int LastOperation_Decompression_BytesRead;
	/// statistics traced for compress/decompress/state operations
	unsigned int LastOperation_Compression_BytesWritten;
	/// statistics traced for compress/decompress/state operations
	unsigned int LastOperation_Decompression_BytesWritten;
	/// statistics traced for compress/decompress/state operations
	unsigned int LastOperation_State_BytesRead;
	/// statistics traced for compress/decompress/state operations
	unsigned int LastOperation_State_BytesWritten;
	/// statistics traced for compress/decompress/state operations
	float LastOperation_Compression_Bitrate;
	/// statistics traced for compress/decompress/state operations
	float LastOperation_Decompression_Bitrate;
	/// statistics traced for compress/decompress/state operations
	float LastOperation_Compression_Ratio;
	/// statistics traced for compress/decompress/state operations
	float LastOperation_Decompression_Ratio;
	/// statistics traced for compress/decompress/state operations
	float LastOperation_Compression_ExecutionTime;
	/// statistics traced for compress/decompress/state operations
	float LastOperation_Decompression_ExecutionTime;
	/// statistics traced for compress/decompress/state operations
	float LastOperation_State_ExecutionTime;
	/// statistics traced for compress/decompress/state operations
	int LastOperation_ErrorCount;
};
//---------------------------------------------------------------------------



//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------

