//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. =========
//
// The copyright to the contents herein is the property of Charles G. Cleveland.
// The contents may be used and/or copied only with the written permission of
// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose: 
//
// $Workfile: Checksum.cpp $
// $Date: 2002/05/23 04:03:11 $
//
//-------------------------------------------------------------------------------
// $Log: Checksum.cpp,v $
// Revision 1.1  2002/05/23 04:03:11  Flayra
// - Post-crash checkin.  Restored @Backup from around 4/16.  Contains changes for last four weeks of development.
//
//===============================================================================
#include "Checksum.h"
#include "STLUtil.h"

const string kFormattedStringDelimiter = " -> ";
const string kFormattedStringEOL = "\n";

ChecksumEntry::ChecksumEntry(void)
{ 
	this->mChecksum = 0; 
}

uint32 ChecksumEntry::GetChecksum() const
{ 
	return this->mChecksum; 
}

const string& ChecksumEntry::GetDescription() const
{ 
	return this->mChecksumDescription; 
}

string ChecksumEntry::GetFormattedString() const
{ 
	string theFormattedString = this->mChecksumDescription;
	theFormattedString += kFormattedStringDelimiter;
	theFormattedString += MakeStringFromInt(this->mChecksum);
	theFormattedString += kFormattedStringEOL;

	return theFormattedString; 
}


void ChecksumEntry::SetChecksum(uint32 inChecksum)
{ 
	this->mChecksum = inChecksum; 
}

void ChecksumEntry::SetDescription(const string& inDescription)
{ 
	this->mChecksumDescription = inDescription; 
}

bool ChecksumEntry::SetFromFormattedString(const string& inFormattedString)
{
	bool theSuccess = false;

	size_t theEOL = inFormattedString.find(kFormattedStringEOL);
	if(theEOL != std::string::npos)
	{
		string theNonDelimitedString = inFormattedString.substr(0, theEOL);
		
		// Parse out delimiter
		size_t theSplit = theNonDelimitedString.find(kFormattedStringDelimiter);
		if(theSplit != std::string::npos)
		{
			// Everything before it is description
			string theDescription = theNonDelimitedString.substr(0, theSplit-1);
		
			// Everything after is checksum
			string theChecksumString = theNonDelimitedString.substr(theSplit + kFormattedStringDelimiter.size());
		
			this->mChecksumDescription = theDescription;
			this->mChecksum = MakeIntFromString(theChecksumString);
		
			theSuccess = true;
		}
	}
	
	return theSuccess;
}


bool ChecksumEntry::Compare(const ChecksumEntry& inChecksumEntry, string& theErrorString) const
{
	bool theReturnCode = false;

	if(inChecksumEntry.mChecksumDescription != this->mChecksumDescription)
	{
		sprintf(theErrorString, "ChecksumEntry::Compare failed, the tags don't match: %s != %s\n", inChecksumEntry.mChecksumDescription.c_str(), this->mChecksumDescription.c_str());
	}
	else if(inChecksumEntry.mChecksum != this->mChecksum)
	{
		sprintf(theErrorString, "ChecksumEntry::Compare failed, checksums don't match: %u != %u (%s)\n", inChecksumEntry.mChecksum, this->mChecksum, inChecksumEntry.mChecksumDescription.c_str());
	}
	else
	{
		theReturnCode = true;
	}
	
	return theReturnCode;
}

Checksum::Checksum(bool inVerboseMode)
{
	this->mIsVerboseMode = inVerboseMode;
}

void Checksum::AddChecksum(const string& inInfoString, uint32 inChecksum)
{
	ChecksumEntry theChecksumEntry;

	// Add a new tagged checksum or just add the new checksum number to our total depending on verbose mode
	if(this->mIsVerboseMode)
	{
		// Initialize new checksum entry
		theChecksumEntry.SetDescription(inInfoString);
		theChecksumEntry.SetChecksum(inChecksum);
		
		// Add our new entry
		this->mChecksumList.push_back(theChecksumEntry);
	}
	else
	{
		this->AddChecksum(inChecksum);
	}
}

void Checksum::AddFloatChecksum(const string& inInfoString, float inChecksum)
{
	// Convert float to uint32
	uint32 theUint32Checksum = (uint32)(inChecksum*1000.0f);
	this->AddChecksum(inInfoString, theUint32Checksum);
}


void Checksum::AddChecksum(uint32 inChecksum)
{
	ChecksumList::iterator		theIterator;
	ChecksumEntry				theNewEntry;

	// Make sure there is at least one checksum already, add one otherwise
	if(this->mChecksumList.size() == 0)
	{
		// Add new checksum with no description
		this->mChecksumList.push_back(theNewEntry);
	}

	// Get iterator to last checksum
	theIterator = this->mChecksumList.end() - 1;

	// Add inChecksum to the existing value
	theIterator->SetChecksum(theIterator->GetChecksum() + inChecksum);
}

bool Checksum::Compare(const Checksum& inChecksum, StringList& outErrors) const
{
	ChecksumList::const_iterator	theIncomingIter = inChecksum.mChecksumList.begin();
	ChecksumList::const_iterator	theSourceIter = this->mChecksumList.begin();

	string							theErrorString;

	int32							theChecksumCount = 0;

	bool							theReturnCode = true;

	// Only try compare if both checksums are in the same mode 
	if(this->GetIsVerboseMode() == inChecksum.GetIsVerboseMode())
	{
		if(this->mChecksumList.size() == inChecksum.mChecksumList.size())
		{
			for( ; theSourceIter != this->mChecksumList.end(); theSourceIter++, theIncomingIter++)
			{
				if(!(theSourceIter->Compare(*theIncomingIter, theErrorString)))
				{
					outErrors.push_back(theErrorString);
					theReturnCode = false;
				}
				theChecksumCount++;
			}
		}
		else
		{
			sprintf(theErrorString, "Checksum::Compare(): Checksum sizes don't match.  Source size is %d and other size is %d.\n", this->mChecksumList.size(), inChecksum.mChecksumList.size());
			outErrors.push_back(theErrorString);
			theReturnCode = false;
		}
	}
	else
	{
		sprintf(theErrorString, "Checksum::Compare(): One checksum is in verbose mode, the other isn't.\n");
		outErrors.push_back(theErrorString);
		theReturnCode = false;
	}

	return theReturnCode;
}

bool Checksum::GetIsVerboseMode(void) const
{
	return this->mIsVerboseMode;
}

uint32 Checksum::GetTotalChecksum(void) const
{
	ChecksumList::const_iterator	theSourceIter = this->mChecksumList.begin();
	uint32							theTotalChecksum = 0;
	
	for( ; theSourceIter != this->mChecksumList.end(); theSourceIter++)
	{
		theTotalChecksum += theSourceIter->mChecksum;
	}
	
	return theTotalChecksum;
}

void Checksum::PrintReport(void) const
{
	//	ChecksumList::const_iterator	theSourceIter = this->mChecksumList.begin();
	//
	//	::ESReport("Checksum::PrintReport(): begin.\n");
	//	for( ; theSourceIter != this->mChecksumList.end(); theSourceIter++)
	//	{
	//		::ESReport("  %s, %u\r\n", (const char*)(theSourceIter->mChecksumDescription), theSourceIter->mChecksum);
	//	}
	//	::ESReport("Checksum::PrintReport(): complete.\n");
}

bool Checksum::ReadFromFile(const char* inFilename) 
{
	bool theSuccess = false;

	this->mChecksumList.clear();
	
	ifstream theStream(inFilename);
	if(theStream.is_open())
	{
		string theStartString;
		theStream >> theStartString;

		// Read in num checksums
		int theNumChecksums = 0;
		theStream >> theNumChecksums;
		
		// Read in pairs of checksums
		for(int i = 0; i < theNumChecksums; i++)
		{
			string theFormattedString;
			theStream >> theFormattedString;

			ChecksumEntry theNewEntry;
			theNewEntry.SetFromFormattedString(theFormattedString);
			this->mChecksumList.push_back(theNewEntry);
		}
		
		string theEndString;
		theStream >> theEndString;
		
		theStream.close();
	}

	return theSuccess;
}

bool Checksum::SaveToFile(const char* inFilename) const
{
	bool theSuccess = false;

	// open file for writing
	ofstream theStream(inFilename);
	if(theStream.is_open())
	{
		string theStartString("CHECKSUM-REPORT:\r\n");
		theStream << theStartString;

		int theNumChecksums = (int)this->mChecksumList.size();
		theStream << theNumChecksums;

		// dump everything to it
		for(ChecksumList::const_iterator theIter = this->mChecksumList.begin(); theIter != this->mChecksumList.end(); theIter++)
		{
			string theFormattedDescription = theIter->GetFormattedString();
			theStream << theFormattedDescription;
		}
		
		string theEndString("END.\r\n");
		theStream << theEndString;

		theStream.close();

		theSuccess = true;
	}

//	FILE* theFile = fopen(inFilename, "wt");
//	if(theFile)
//	{
//		string theStartString("Checksum report:\r\n");
//		fwrite(theStartString.c_str(), theStartString.length(), 1, theFile);
//		
//		// dump everything to it
//		for(ChecksumList::const_iterator theIter = this->mChecksumList.begin(); theIter != this->mChecksumList.end(); theIter++)
//		{
//			const string& theFormattedString = theIter->GetFormattedString();
//			fwrite(theFormattedString.c_str(), theFormattedString.length(), 1, theFile);
//			fwrite("\r\n", 2, 1, theFile);
//		}
//
//		string theEndString("Checksum report complete.\r\n");		
//		fwrite(theEndString.c_str(), theEndString.length(), 1, theFile);
//		
//		// close it
//		int theCloseRC = fclose(theFile);
//		if(theCloseRC == 0)
//		{
//			theSuccess = true;
//		}
//	}

	//	ChecksumList::const_iterator	theSourceIter = this->mChecksumList.begin();
	//
	//	::ESReport("Checksum::PrintReport(): begin.\n");
	//	for( ; theSourceIter != this->mChecksumList.end(); theSourceIter++)
	//	{
	//		::ESReport("  %s, %u\r\n", (const char*)(theSourceIter->mChecksumDescription), theSourceIter->mChecksum);
	//	}
	//	::ESReport("Checksum::PrintReport(): complete.\n");
	
	return theSuccess;
}