
#include "SessionFileManager.h"
#include "Debugger.h"
#include <stdint.h>
#include <QFile>

struct SessionFileHeader {
	char		Signature[8];
	quint32		Version;
	quint32		Reserved;
	char		File[512];
	quint8		MD5[16];
};

struct SessionFileBlock {
	char		SubSignature[8];
	quint32		Version;
	quint32		BlockSize;
	char		Data[1];	// size is actually == BlockSize
};


struct SessionFileFooter {
	char		Signature[8];
	quint32		Version;
	quint32		Reserved;
};

//------------------------------------------------------------------------------
// Name: SessionFileManager()
// Desc: 
//------------------------------------------------------------------------------
SessionFileManager::SessionFileManager() {
}

//------------------------------------------------------------------------------
// Name: saveSessionData(const QString &filename, const QString &executable)
// Desc: 
//------------------------------------------------------------------------------
void SessionFileManager::saveSessionData(const QString &filename, const QString &executable) {

	edb::v1::log("saving session file %s for %s", qPrintable(filename), qPrintable(executable));
	if(!executable.isEmpty()) {

		QFile file(filename);
		if(file.open(QIODevice::WriteOnly)) {
			SessionFileHeader header;
	
			std::memcpy(header.Signature, "EDB SESS", 8);
			header.Version	= edb::v1::intVersion();
			header.Reserved		= 0;
			std::strncpy(header.File, qPrintable(executable), sizeof(header.File));
			header.File[sizeof(header.File) - 1] = '\0';

			const QByteArray md5 = edb::v1::getFileMD5(executable);
			std::memcpy(header.MD5, md5.data(), md5.size());

			SessionFileFooter footer;
			std::memcpy(footer.Signature, "EDB SESS", 8);
			footer.Version	= edb::v1::intVersion();
			footer.Reserved		= 0;

			file.write(reinterpret_cast<const char *>(&header), sizeof(header));

			// TODO: write data blocks

			file.write(reinterpret_cast<const char *>(&footer), sizeof(footer));
		}
	}
}

//------------------------------------------------------------------------------
// Name: checkHeader(struct SessionFileHeader &header)
// Desc: 
//------------------------------------------------------------------------------
bool SessionFileManager::checkHeader(struct SessionFileHeader &header) {
	bool ret = false;

	if(std::memcmp(header.Signature, "EDB SESS", 8) == 0) {
		if(header.Version == edb::v1::intVersion()) {

			const QByteArray md5 = edb::v1::getFileMD5(header.File);
			
			if(std::memcmp(header.MD5, md5.data(), md5.size()) == 0) {
				if(header.Reserved == 0) {
					ret = true;
				}
			}
		}
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: checkFooter(struct SessionFileFooter &footer)
// Desc: 
//------------------------------------------------------------------------------
bool SessionFileManager::checkFooter(struct SessionFileFooter &footer) {
	bool ret = false;

	if(std::memcmp(footer.Signature, "EDB SESS", 8) == 0) {
		if(footer.Version == edb::v1::intVersion()) {
			if(footer.Reserved == 0) {
				ret = true;
			}
		}
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: loadSessionData(const QString &filename)
// Desc: 
//------------------------------------------------------------------------------
void SessionFileManager::loadSessionData(const QString &filename) {

	edb::v1::log("loading session file %s", qPrintable(filename));

	QFile file(filename);
	if(file.open(QIODevice::ReadOnly)) {
		bool ok;

		SessionFileHeader	header;
		SessionFileFooter	footer;
		SessionFileBlock	*block;

		file.read(reinterpret_cast<char *>(&header), sizeof(header));
		ok = checkHeader(header);
		
		while(ok) {		
			file.read(reinterpret_cast<char *>(&footer), sizeof(footer));
			
			if(checkFooter(footer)) {
				ok = checkFooter(footer);
				break;
			} else {
				SessionFileBlock *const p = reinterpret_cast<SessionFileBlock *>(&footer);
				block = reinterpret_cast<SessionFileBlock *>(new char[sizeof(SessionFileBlock) + p->BlockSize]);
				std::memcpy(block, p, sizeof(footer));
				file.read(reinterpret_cast<char *>(&block->Data), sizeof(block->BlockSize));
			}
		
		}		
	}
}
