/// @file compressor_statistical.cpp
/// Skeleton for statistical compressors
///
/// @remarks
/// This is a base class available for use in building statistical coders
///  which use a parser+model+coder component architecture.  You can't use
///  this "as-is", you need to derive from it and fill in the constructor
///  at a minimum, to create the appropriate model/parser/coder.
///
/// @author mouser
/// @date   2003.07.29
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// application includes
#include "compressor_statistical.hpp"
// system includes
using namespace std;
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
// Normally we would create a global instance in order to register it
//  automatically with the global manager, BUT for this base class we don't
//  want to since its not a real available compressor.  But you will want to
//  create one for any derived class that is useable.
// OctaneCompressor_Statistical GloballyInstantiated_OctaneCompressor_Statistical;
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
OctaneCompressor_Statistical::OctaneCompressor_Statistical(bool registerme)
	:OctaneCompressor(registerme)
{
	// create the children objects
	parserp=NULL;
	modelerp=NULL;
	coderp=NULL;
	// in a derived class you would create real parsers,modelers, and coders here.

	// REGISTER WITH COMPRESSOR MANAGER
	//  this use a trick to make sure the global manager is ready for registration
 	//  and at the same time automatically register this instance with the global manager
 	//  note that the insurer is just temporary, and deletes when we exit the procedure
	// BUT for this base class we don't want to since its not a real available
	//  compressor.  But you will want to create one for any derived class that is useable.
	// CompressorManager_SingletonInsurer managerinsurance(this);
}

OctaneCompressor_Statistical::~OctaneCompressor_Statistical()
{
	// destructor
	// delete children objects
	if (coderp!=NULL)
		delete coderp;
	if (modelerp!=NULL)
		delete modelerp;
	if (parserp!=NULL)
		delete parserp;
}
//---------------------------------------------------------------------------




//---------------------------------------------------------------------------
// OCTANE PUBLIC API - RTTI FUNCTIONS - these are supported by all derived classes

string OctaneCompressor_Statistical::GetHelpInformation()
{
	// show help info from our parser,model,coder
	string helpinfo;

	// generic help information for children
	helpinfo += PrefixNonEmptyString("Parser Help:\n-------------------------\n",parserp->GetHelpInformation());
	helpinfo += PrefixNonEmptyString("Modeler Help:\n-------------------------\n",modelerp->GetHelpInformation());
	helpinfo += PrefixNonEmptyString("Coder Help:\n-------------------------\n",coderp->GetHelpInformation());

	// return amalgamated string
	return helpinfo;
}
//---------------------------------------------------------------------------



//---------------------------------------------------------------------------
// OCTANE PUBLIC API - AUXILIARY FUNCTIONS - these are supported by all derived classes

void OctaneCompressor_Statistical::ShowDebuggingInfo()
{
	// generic debugging info for children
	parserp->ShowDebuggingInfo();
	modelerp->ShowDebuggingInfo();
	coderp->ShowDebuggingInfo();
}

unsigned int OctaneCompressor_Statistical::GetMemoryUsed()
{
	// return the size of us and all our children
	return (unsigned int)(sizeof(this)+parserp->GetMemoryUsed()+modelerp->GetMemoryUsed()+coderp->GetMemoryUsed());
}

unsigned int OctaneCompressor_Statistical::GetDiskspaceUsed(bool fortempdecompressiononly)
{
	// return use of disk space used to save our state and state of children
	return (parserp->GetDiskspaceUsed(fortempdecompressiononly)+modelerp->GetDiskspaceUsed(fortempdecompressiononly)+coderp->GetDiskspaceUsed(fortempdecompressiononly));
}

std::string OctaneCompressor_Statistical::GetParametersInformation()
{
	// optional parameter management; return true on success
	string parameterinfo;

	// generic parameter information for children
	parameterinfo += PrefixNonEmptyString("Parser Parameters:\n-------------------------\n",parserp->GetParametersInformation());
	parameterinfo += PrefixNonEmptyString("Modeler Parameters:\n-------------------------\n",modelerp->GetParametersInformation());
	parameterinfo += PrefixNonEmptyString("Coder Parameters:\n-------------------------\n",coderp->GetParametersInformation());

	// return amalgamated string
	return parameterinfo;
}

void OctaneCompressor_Statistical::SetDefaultParameters()
{
	// set default parameters for children
	parserp->SetDefaultParameters();
	modelerp->SetDefaultParameters();
	coderp->SetDefaultParameters();
}

bool OctaneCompressor_Statistical::SetParameter(const std::string &parametername,const std::string &parametervalue)
{
	/// @note this is a little weird; there are two ways we could do this, we could use a prefix to indicate
	///  whether they want to set a parameter in parser/modeler/coder, or we could simply ask each in turn if they own the variable
	bool bretv;
	
	bretv=parserp->SetParameter(parametername,parametervalue);
	if (!bretv)
		bretv=modelerp->SetParameter(parametername,parametervalue);
	if (!bretv)
		bretv=coderp->SetParameter(parametername,parametervalue);
	return bretv;
}

bool OctaneCompressor_Statistical::DoProtectedSaveState(bitwriter &to,bool fortempdecompressiononly)
{
	// save state; returns true on success
	bool bretv;
	bretv=parserp->SaveState(to,fortempdecompressiononly);
	if (bretv)
		bretv=modelerp->SaveState(to,fortempdecompressiononly);
	if (bretv)
		bretv=coderp->SaveState(to,fortempdecompressiononly);
	return bretv;
}

bool OctaneCompressor_Statistical::DoProtectedLoadState(bitreader &from)
{
	// save state; returns true on success
	bool bretv;
	bretv=parserp->LoadState(from);
	if (bretv)
		bretv=modelerp->LoadState(from);
	if (bretv)
		{
		bretv=coderp->LoadState(from);
		/// now tell the coder that the modeler (the weights on symbols) have changed
		coderp->ReceiveNotification_ModelChange_AllSymbolWeights(modelerp);
		}
	return bretv;
}
//---------------------------------------------------------------------------



//---------------------------------------------------------------------------
// COMPRESSOR PUBLIC API - PREPARATION FOR COMPRESSING/DECOMPRESSING
void OctaneCompressor_Statistical::ResetState()
{
	// reset the state of the parser with any default starting symbols, can be called multiple times; returns true on success
	parserp->ResetState();
	modelerp->ResetState();
	coderp->ResetState();
}


void OctaneCompressor_Statistical::SetupAnyDefaultParser()
{
	// if a parser starts out ready to parse, we might need to build a model/coder from it
	if (parserp->IsReadyToParse())
		{
		modelerp->CreateModelUsingParser(parserp);
		if (modelerp->IsReadyToModel())
			{
			/// now tell the coder that all the weights on symbols have changed
			coderp->ReceiveNotification_ModelChange_AllSymbolWeights(modelerp);
			}
		}
}


bool OctaneCompressor_Statistical::DoProtectedCreateSymbolsAndModelsUsingStream(bitreader &from)
{
	// optionally (pre)process an input stream to update/create a symbol set from it; returns true on success
	bool bretv;
	// build parser symbol set using stream
	// HOWEVER this can be a bit tricky, because we have 2 structures (parser and modeler) which may want to process from stream
	//  so we need to rewind it after the parser gets done with it.
	
	// record starting position of from stream (probably 0)
	size_t fromstartpos=from.tell_bit();
	
	// give it to parser
	bretv = parserp->CreateSymbolSetUsingStream(from);
	// now build the model using the symbols created by the parser, and again on the stream from
	if (bretv)
		{
		// reset from stream to start
		from.seek_bit(fromstartpos);
		// now have model process it
		bretv = modelerp->CreateModelUsingStream(parserp,from);
		if (bretv)
			{
			/// now tell the coder that all the weights on symbols have changed
			coderp->ReceiveNotification_ModelChange_AllSymbolWeights(modelerp);
			}
		}

	// return success
	return bretv;
}

bool OctaneCompressor_Statistical::PrepareForCompression()
{
	// prepare for parsing mode, must be called after zero or more Updating calls, and before parsing begins; returns true on success
	bool bretv;
	
	// first prepare the parser (this should ensure we have a valid symbol set)
	bretv=parserp->PrepareForParsing();
	// now prepare the model (give it parser info in case it wants it)
	if (bretv)
		modelerp->PrepareForModeling(parserp);
	// now build coder based on modeler
	if (bretv)
		coderp->PrepareForCoding(modelerp);

	// return success
	return bretv;
}

bool OctaneCompressor_Statistical::IsReadyToCompress()
{
	// is the compressor ready to compress? Normally this is false until a dictionary is built or loaded.
	bool bretv1,bretv2,bretv3;
	
	if (!(bretv1=parserp->IsReadyToParse()))
		cout << "the parser is not ready."<<endl;
	if (!(bretv2=modelerp->IsReadyToModel()))
		cout << "the modeler is not ready."<<endl;
	if (!(bretv3=coderp->IsReadyToCode()))
		cout << "the coder is not ready."<<endl;
	return (bretv1 && bretv2 && bretv3);
}

void OctaneCompressor_Statistical::SynchronizeStateForNewStream()
{
	// synchronize state for a new stream - this *MUST* be called before beginning a new parsing stream
	// we just synchronize our components
	parserp->SynchronizeStateForNewStream();
	modelerp->SynchronizeStateForNewStream();
	coderp->SynchronizeStateForNewStream();
}
//---------------------------------------------------------------------------



//---------------------------------------------------------------------------
// COMPRESSOR PUBLIC API - COMPRESSING/DECOMPRESSING

bool OctaneCompressor_Statistical::DoProtectedCompress(bitreader &from, bitwriter &to)
{
	// compress all of from and send to to
	// ATTN: the only thing slightly troubling about this otherwise elegant routine, is the
	//  fact that there is no way for the components to change the symbol set during compression
	bool bretv=true;
	int symbolnumber;

	// we *must* inform our components that we are about to compress or decompress a new stream
	SynchronizeStateForNewStream();

	// loop parsing a symbol from parser until we hit end (note the last symbol will be an End-Of-Stream)
	while (parserp->ParseNextSymbolFromInput(from,symbolnumber))
		{
		// debug show some info?
		//   cout << "$READ SYMBOL "<<symbolnumber<<": '"<<parserp->LookupSymbolText(symbolnumber)<<"'."<<endl;
		// code the symbol given current model and codes
		if (!(bretv=coderp->WriteSymbolBits(symbolnumber,to)))
			break;
		// now update the model based on this symbol (pass coderp so it can inform it if probabilities change).
		// note that for four zero-order model this doesn't do anything, but we call it for completeness.
		if (!(bretv=modelerp->UpdateModelAfterReceivingSymbol(symbolnumber,coderp)))
			break;
		}

	// when we finish coding a stream we want to align to byte boundary in the output
	//  this will happen automatically if we close the output stream now, but we do it manually in case we want to pack multiple
	//  input streams into the same output.
	coderp->AlignOutputStreamToByteBoundry(to);

	// return success
	return bretv;
}

bool OctaneCompressor_Statistical::DoProtectedDecompress(bitreader &from, bitwriter &to)
{
	// decompress all of from and send to to
	// ATTN: the only thing slightly troubling about this otherwise elegant routine, is the
	//  fact that there is no way for the components to change the symbol set during compression
	bool bretv=true;
	int symbolnumber;
	bool isendofstreamsymbol;

	// we *must* inform our components that we are about to compress or decompress a new stream
	SynchronizeStateForNewStream();
	
	// loop decoding a symbol from coder until we hit end
	while (coderp->DecodeSymbolFromInput(symbolnumber,from))
		{
		// debug show some info?
		//    cout << "$DECODED SYMBOL "<<symbolnumber<<": '"<<parserp->LookupSymbolText(symbolnumber)<<"'."<<endl;
		// deParse the symbol given current model and symbol set
		if (!(bretv=parserp->WriteSymbolText(to,symbolnumber,isendofstreamsymbol)))
			break;
		// did we just parse an EOS(end-of-stream) symbol? if so we need to align input to byte boundary (normally its end of file)
		if (isendofstreamsymbol)
			{
			// when we finish coding a stream we want to align to byte boundary in the output
			//  this will happen automatically if we close the output stream now, but we do it manually in case we want to pack multiple
			//  input streams into the same output. note that this we will almost always be leaving this loop after this symbol.
			coderp->AlignInputStreamToByteBoundry(from);
			// cout << "$DEBUG aligning input."<<endl;
			}
		// now update the model based on this symbol (pass coderp so it can inform it if probabilities change).
		// note that for four zero-order model this doesn't do anything, but we call it for completeness.
		if (!(bretv=modelerp->UpdateModelAfterReceivingSymbol(symbolnumber,coderp)))
			break;
		}

	// return success
	return bretv;
}
//---------------------------------------------------------------------------
