#include "VBFParserSmall.h"
//#include "Public.h"
#include "VBFBlock.h"
#include "algorithm.h"
#include "FlashFileUtils.h"
#include "MyStringUtils.h"

#include "tracer.h"
#include <mutex>
#include "jidu_macro.h"

std::mutex g_mutexVBFParser;

VBFParserSmall::VBFParserSmall()
{
	//generate_crc32_table();
}

VBFParserSmall::~VBFParserSmall()
{

}

bool VBFParserSmall::ParseVBFFile(const std::string& strFileName, const std::string& strRSAPrivateKeyFileName, const std::string& strKeyInfo)
{
	std::lock_guard<std::mutex> lock(g_mutexVBFParser);
#ifdef _VM_PROTECT_
	VMStart();
#endif // _WINDOWS_
	m_strVBFileName = strFileName;
	m_strRSAPrivateKeyFileName = strRSAPrivateKeyFileName;
	m_strKeyInfo = strKeyInfo;

	if (!OpenVBFFile(m_strVBFileName))
	{
		Tracer::error("OpenVBFFile", strFileName);
		return false;
	}

	if (!m_oHeader.ParseHeader(m_vecFileBuffer))
	{
		Tracer::error("ParseHeader", strFileName);
		return false;
	}

	std::string strECUAddress = getHeader().getEcu_address();

	m_usMASK = MyStringUtils::toLong(strECUAddress, 16);

	if (!ParseBlocks())
	{
		Tracer::error("fail", "ParseBlocks");
		return false;
	}

	if (m_vecVBFBlocks.size() <= 0)
	{
		Tracer::error("fail", "m_vecVBFBlocks");
		return false;
	}

#ifdef _VM_PROTECT_
	VMEnd();
#endif // _WINDOWS_
	return true;
}

VBFHeader& VBFParserSmall::getHeader()
{
	return m_oHeader;
}

vector<VBFBlock>& VBFParserSmall::GetVBFBlocks()
{
	return m_vecVBFBlocks;
}

unsigned int VBFParserSmall::GetVBFDataSize()
{
	int nSum = 0;

	for (int i = 0; i < m_vecVBFBlocks.size(); i++)
	{
		nSum += m_vecVBFBlocks[i].GetLength();
	}

	return nSum;
}

vector<unsigned char> VBFParserSmall::GetVBFData()
{
	return m_vecFileBuffer;
}

bool VBFParserSmall::OpenVBFFile(std::string strFileName)
{
	m_vecFileBuffer.clear();
	FILE* fp = fopen(strFileName.c_str(), "rb");
	if (fp == NULL)
	{
		Tracer::error(strFileName.c_str(), "Open file failed AND not found");
		return false;
	}

	fseek(fp, 0, SEEK_END);
	unsigned int datasize = ftell(fp);
	fseek(fp, 0, SEEK_SET);

	unsigned char* pBuffer = new unsigned char[datasize + 1];
	memset(pBuffer, 0, datasize + 1);

	if (!m_strRSAPrivateKeyFileName.empty() && !m_strKeyInfo.empty())
	{
		fseek(fp, 0x40, SEEK_SET);
		datasize = datasize - 0x40;

		fread(pBuffer, 1, datasize, fp);
		fclose(fp);

		CFlashFileUtils fileUtils;
		if (!fileUtils.InitRSA(m_strRSAPrivateKeyFileName, m_strKeyInfo))
		{
			delete[] pBuffer;
			pBuffer = nullptr;

			return false;
		}

		if (!fileUtils.DecryptFlashFile(pBuffer, pBuffer, datasize))
		{
			delete[] pBuffer;
			pBuffer = nullptr;
			return false;
		}
	}
	else
	{
		fread(pBuffer, 1, datasize, fp);
		fclose(fp);
	}

	m_vecFileBuffer.assign(pBuffer, pBuffer + datasize);

	delete[] pBuffer;
	pBuffer = nullptr;

	return true;
}

bool VBFParserSmall::ParseBlocks()
{
	m_vecVBFBlocks.clear();
	bool finished = false;
	int pos = m_oHeader.getHeaderLength() + 1;

	unsigned char* buffer = m_vecFileBuffer.data();
	std::string strFileChecksum = getHeader().getFileChecksum();
	unsigned int unFileChecksum = MyStringUtils::toLong(strFileChecksum, 16);

	while (!finished)
	{
		if (m_vecFileBuffer.size() < pos + 8)
		{
			return false;
		}

		unsigned char a1 = (buffer[pos++] & 0xFF);
		unsigned char a2 = (buffer[pos++] & 0xFF);
		unsigned char a3 = (buffer[pos++] & 0xFF);
		unsigned char a4 = (buffer[pos++] & 0xFF);

		unsigned char b1 = (buffer[pos++] & 0xFF);
		unsigned char b2 = (buffer[pos++] & 0xFF);
		unsigned char b3 = (buffer[pos++] & 0xFF);
		unsigned char b4 = (buffer[pos++] & 0xFF);

		unsigned int unStartAddress = ((a1 << 0x18) | (a2 << 0x10) | (a3 << 0x08) | (a4 << 0x00));
		unsigned int unLength = ((b1 << 0x18) | (b2 << 0x10) | (b3 << 0x08) | (b4 << 0x00));

		char tempBuffer[32] = { 0 };
		itoa(unStartAddress, tempBuffer, 16);
		if (m_vecFileBuffer.size() < pos + unLength)
		{
			Tracer::error(__FUNCTION__, "data");
			return false;
		}

		vector<unsigned char> data;
		data.assign(m_vecFileBuffer.begin() + pos, m_vecFileBuffer.begin() + pos + unLength);

		pos += unLength;

		if (m_vecFileBuffer.size() < pos + 2)
		{
			return false;
		}

		unsigned char c1 = (buffer[pos++] & 0xFF);
		unsigned char c2 = (buffer[pos++] & 0xFF);

		unsigned short unCheckSum = (c1 << 0x08) | (c2 << 0x00);

		//unsigned short crc = CalcCRC16(data.size(), data.data());

#ifndef FLASH_TEST
		//if (unCheckSum != crc)
		//{
		//	LOG(INFO) << ("unCheckSum[%X] crc[%X]", unCheckSum, crc);
		//	return false;
		//}
#endif

		VBFBlock oneBlock = VBFBlock(unStartAddress, unLength, unCheckSum, data);
		m_vecVBFBlocks.push_back(oneBlock);

		if (pos >= m_vecFileBuffer.size())
		{
			finished = true;
		}
	}

	return true;
}