////////////////////////////////////////////////////////////////////////////////
// MechWarrior3Reader.cpp
// reader.zbd parser - todo: does not work yet, incomplete, work in progress
// maybe too difficult to justify the minor gain of functionality?
// Copyright Bjoern Ganster
// Developed since 2007
// Status: extracted data may have errors and may be incomplete

#include "MechWarrior3Reader.h"

#include <algorithm>

#define FILE_NUMBER FILE_NO_MECHWARRIOR3_READER_CPP

// Constructor
MW3ReaderZbdArchive::MW3ReaderZbdArchive()
: MWArchive("MW3ReaderZbdArchive", MW3ReaderZbdArchive_ID)
{
}

// Destructor
MW3ReaderZbdArchive::~MW3ReaderZbdArchive()
{
}

// Open file
bool MW3ReaderZbdArchive::open(const PathChar* PRJFN, bool readOnly)
{
	bool result = mMb.loadFromFile(PRJFN);
	addFile("contents", mMb.getSize(), 0, MW3ZReaderContents);
	return result;
}

// Close file
void MW3ReaderZbdArchive::close()
{
}

// Read bufSize bytes from file numbered index at offset into buf
bool MW3ReaderZbdArchive::getFileChunk(size_t index, size_t offset, void* buf, size_t bufSize)
{
	return false;
}

// Read a list of values
void MW3ReaderZbdArchive::readList(size_t& offset, XMLTreeIterator& xmlIter,size_t argNo)
{
	SET_SCOPE;
	auto count = std::min<uint32_t>(mMb.get<uint32_t>(offset), 256);
	offset += 4;
	size_t i = 1;
	while (i < count && offset < mMb.getSize()) {
		uint32_t datatype = mMb.get<uint32_t>(offset);
		offset += 4;
		switch (datatype) {
		case 1:
			// integer???
			{
				auto value = mMb.get<int32_t>(offset);
				offset += 4;
				BGCPString valname = BGCPString("val") + BGCPString::fromInt(argNo) + BGCPString("_") + BGCPString::fromInt(i) + BGCPString("i");
				xmlIter.addIntAttribute(valname, value);
			}
			break;
		case 2:
			// float???
			{
				auto value = mMb.get<float>(offset);
				offset += 4;
				BGCPString valname = BGCPString("val") + BGCPString::fromInt(argNo) + BGCPString("_") + BGCPString::fromInt(i) + BGCPString("f");
				xmlIter.addDoubleAttribute(valname, value);
			}
			break;
		case 3:
			{
				uint32_t datalen = mMb.get<uint32_t>(offset);
				offset += 4;
				if (datalen < 256) {
					char* dataStr = mMb.getPtr<char>(offset, false);
					offset += datalen;
					if (dataStr != nullptr) {
						xmlIter.addText(BGCPString(dataStr, datalen, CP_ASCII));
					}
				} else {
					COUT << "Implausible datalen: " << datalen << std::endl;
				}
			}
			break;
		case 4:
			COUT << "Really? A list inside of alist? What to make of this?" << std::endl;
			break;
		default:
			COUT << "Welcome new problem " << datatype << std::endl;
			break;
		}
		++i;
	}
	RETURN;
}

void MW3ReaderZbdArchive::readValues(size_t& offset, XMLTreeIterator& xmlIter, size_t argCount)
{
	SET_SCOPE;
	for (size_t i = 0; i < argCount; ++i) {
		auto data3 = mMb.get<uint32_t>(offset);
		offset += 4;
		BGCPString valname = BGCPString("val") + BGCPString::fromInt(i+1);
		switch (data3) {
		case 1:
			{
				// integer???
				int32_t value = mMb.get<int32_t>(offset);
				offset += 4;
				xmlIter.addIntAttribute(valname + BGCPString("i"), value);
				break;
			}
		case 2:
			// float???
			{
				auto value = mMb.get<float>(offset);
				offset += 4;
				xmlIter.addDoubleAttribute(valname + BGCPString("f"), value);
			}
			break;
		case 3:
			// string???
			{
				auto valuelen = mMb.get<uint32_t>(offset);
				offset += 4;
				if (valuelen < 256) {
					auto value = mMb.getPtr<char>(offset, false);
					offset += valuelen;
					xmlIter.addAttribute(valname + BGCPString("s"), BGCPString(value, valuelen, CP_ASCII));
				}
			}
			break;
		case 4:
			// list???
			{
				readList(offset, xmlIter, i);
			}
			break;
		default:
			COUT << "This is new..." << std::endl;
		}
	}
	RETURN;
}

// Export MW3 zreader archive as XML (recursive part)
void MW3ReaderZbdArchive::exportMW3ZReaderAsXmlRec(size_t& offset, XMLTreeIterator& xmlIter, int recursions)
{
	++recursions;
	if (recursions > 10) {
		COUT << "Too deep for me" << std:: endl;
		return;
	}

	/*if (offset >= 9656) {
		COUT << "Debug me@" << offset << std::endl;
	}*/

	// 3 == key???
	SET_SCOPE;
	auto startOffset = offset;
	size_t jump = 0;
	while (mMb.get<uint32_t>(offset+jump) != 3 && offset + jump < mMb.getSize()) {
		// Advance by at least one byte to prevent an infinite loop?
		++jump;
	}

	if (jump > 0) {
		//COUT << "Jumped " << jump << " bytes from offset " << offset << std::endl;
		offset += jump;
	}

	SET_OP;
	offset += 4; // skip value "3" which indicates a key name
	DWORD keyLen = mMb.get<DWORD>(offset);
	offset += 4;
	if (keyLen > 256) {
		std::cout << "oops" << std::endl;
		keyLen = 1;
	}
	char* name = mMb.getPtr<char>(offset, false);
	offset += keyLen;
	DWORD data1 = mMb.get<DWORD>(offset);
	offset += 4;
	DWORD data2 = mMb.get<DWORD>(offset);
	offset += 4;
	BGCPString key(name, keyLen, CP_ASCII);
	COUT << "At offset " << startOffset << ", data1=" << data1 << ", data2=" << data2 << ", key=";
	key.printLn();

	if (data1 != 4) { // 4 == key???
		return;
	}

	switch (data2) {
	case 1: // no value???
		{
			SET_OP;
			size_t childNum = xmlIter.addChild(key);
		}
		break;
	case 2: // just one value???
		{
			SET_OP;
			size_t childNum = xmlIter.addChild(key);
			xmlIter.goToChild(childNum);
			readValues(offset, xmlIter, 1);
			xmlIter.goToParent();
		}
		break;
	case 3: // pair of values?
		{
			SET_OP;
			size_t childNum = xmlIter.addChild(key);
			xmlIter.goToChild(childNum);
			readValues(offset, xmlIter, 2);
			xmlIter.goToParent();
		}
		break;
	case 4: // 3 values?
		{
			SET_OP;
			size_t childNum = xmlIter.addChild(key);
			xmlIter.goToChild(childNum);
			readValues(offset, xmlIter, 3);
			xmlIter.goToParent();
		}
		break;
	case 5:
		{ // Same as 4, but count is read from file???
			SET_OP;
			auto count = mMb.get<uint32_t>(offset)+1;
			size_t childNum = xmlIter.addChild(key);
			xmlIter.goToChild(childNum);
			readValues(offset, xmlIter, count);
			xmlIter.goToParent();
		}
		break;
	// more values???
	default: // recursive list
		{
			SET_OP;
			auto count = data2 / 2;
			size_t childNum = xmlIter.addChild(key);
			xmlIter.goToChild(childNum);
			size_t i = 0;
			while (offset < mMb.getSize() && i < count) {
				exportMW3ZReaderAsXmlRec(offset, xmlIter, recursions);
				++i;
			}
			xmlIter.goToParent();
		}
		break;
	}

	RETURN;
}
