/* --COPYRIGHT--,BSD
 * Copyright (c) 2012, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * --/COPYRIGHT--*/
#include "stdafx.h"
#include "MSP430CRC_Calc.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include "MSPBSL_CRCEngine.h"
#include <iomanip>
#include <vector>
#include <algorithm>

using namespace std;
void Write_address_to_file(ofstream *outputfile, uint32_t addr);
void Write_Byte_to_file(ofstream *outputfile, uint8_t val);
void Write_Word_to_file(ofstream *outputfile, uint16_t val);

MSP430CRC_Calc::MSP430CRC_Calc(string inputfile, string outputfile, uint32_t StartAddr, uint32_t EndAddr, uint32_t CRC_Addr , uint8_t Fill_Val, bool Fill_mem)
{

	input_filename = inputfile;
	output_filename = outputfile;
	Mem_Start = StartAddr;
	Mem_End = EndAddr;
	CRC_Address = CRC_Addr;
	Fill_value = Fill_Val;
	Fill_unused = Fill_mem;

}

MSP430CRC_Calc::~MSP430CRC_Calc(void)
{
}


int8_t MSP430CRC_Calc::CalculateCRC(uint8_t type){
	uint8_t *Memory_Map;
	uint32_t i,j,block_start, block_end, block_offset=0, startadress, datapointer;
	uint8_t lastblock=0;
	string ignore = "\b\t\n\r\f\v "; //ignore those characters if they are between the strings. 
	string hexchars = "0123456789abcdefABCDEF";
	vector<string> txt_not_in_range;
	vector<int> addresses_not_in_range;
	vector<uint32_t> addresses;
	vector<vector <uint8_t>> memorycontents;
	vector<uint8_t> temp_content; 
	stringstream temp_txt; 



	ifstream txt(input_filename, ifstream::out); 
	if (txt.fail())
	{
		return -1;
	}
	
	stringstream s;
	s << txt.rdbuf();
	string file = s.str();
	txt.close();

	Memory_Map = new uint8_t[Mem_End - Mem_Start + 1];
	for (i=0 ; i < (Mem_End - Mem_Start+1); i++)
	{
		if (Fill_unused == true)
			Memory_Map[i] = Fill_value;
		else
			Memory_Map[i] = 0xFF;
	}


	while(!lastblock)
	{

		//get start and end of current data block
		block_start=file.find("@", block_offset);	
		block_end=file.find_first_of("@q", block_start+1);
		block_offset=block_end;
		if(file[block_end] == 'q')
		{
			lastblock = 1;
		}

		//get start adress 
		i=file.find_first_of(ignore, block_start)-1;	//find last char of adress
		j=0;
		startadress=0;
		while(i != block_start) //manually calculate adress
		{
			startadress += hextoint(file[i]) * (uint32_t)pow(double(16), int(j));
			i--;
			j++;
		}
		temp_txt << "@" << hex << uppercase << setfill('0') << setw(4) << startadress;
		
		//parse data
		i=file.find_first_of(ignore, block_start);//put pointer after adress
		datapointer=0;

		while(i < block_end)
		{
			uint8_t current_data;
			uint32_t current_addr;
			if(hextoint(file[i]) == 0xFF)
			{
				temp_txt << file[i];
				i++;
				continue; //increase pointer if no hex character
			}

			temp_txt << file[i] << file[i+1];
			//high nibble
			current_data = 16 * hextoint(file[i++]);
			//low nibble
			current_data += hextoint(file[i++]);
			current_addr = datapointer++ + startadress;

			if ((current_addr >= Mem_Start) && (current_addr <= Mem_End))
			{
				Memory_Map[current_addr-Mem_Start] = current_data;
			}
			else
			{
				if ((addresses.size() == 0) || ((addresses[addresses.size() -1] + temp_content.size()) != current_addr))
				{
					// New address outside the memory area
					if (temp_content.size() != 0x00)
					{
						memorycontents.push_back (temp_content);
						temp_content.clear();
					}
					addresses.push_back(current_addr);
				}
				temp_content.push_back(current_data);
			}

		}
		
		//addresses.push_back(startadress);
		//memorycontents.push_back (temp);

		if ((startadress < Mem_Start) || (startadress > Mem_End))
		{
			txt_not_in_range.push_back((string) temp_txt.str());
			addresses_not_in_range.push_back (startadress);
		}
	}//parser mainloop

	if (temp_content.size() != 0)
	{
		memorycontents.push_back (temp_content);
		temp_content.clear();
	}

	if (type == 16)
	{
		MSPBSL_CRCEngine crcEngine("5/6xx");
		crcEngine.initEngine( 0xFFFF );
		crcEngine.addBytes(Memory_Map,Mem_End - Mem_Start + 1);
		//crcEngine.addBytes(Memory_Map,2);
		CRC_Result = (crcEngine.getHighByte() << 8) + crcEngine.getLowByte();
		if ((CRC_Address >= Mem_Start) && (CRC_Address <= Mem_End))
		{
			// If CRC address is between calculated address, simply add it (note that the value in this address should be 0xFFFF)
			Memory_Map[CRC_Address-Mem_Start] = (uint8_t) (CRC_Result&0xFF);
			Memory_Map[CRC_Address-Mem_Start+1] = (uint8_t) ((CRC_Result&0xFF00)>>8);
		}
		else
		{
			addresses.push_back(CRC_Address);
			temp_content.push_back((uint8_t) (CRC_Result&0xFF));
			temp_content.push_back((uint8_t) ((CRC_Result&0xFF00)>>8));
			memorycontents.push_back (temp_content);
			temp_content.clear();
		}
	}
	else if (type == 8)
	{
		MSPBSL_CRCEngine crcEngine("CRC8");
		crcEngine.initEngine( 0x00 );
		crcEngine.addBytes(Memory_Map,Mem_End - Mem_Start + 1);
		//crcEngine.addBytes(Memory_Map,2);
		CRC_Result = crcEngine.getLowByte();
		if ((CRC_Address >= Mem_Start) && (CRC_Address <= Mem_End))
		{
			// If CRC address is between calculated address, simply add it (note that the value in this address should be 0xFFFF)
			Memory_Map[CRC_Address-Mem_Start] = (uint8_t) (CRC_Result);
		}
		else
		{
			addresses.push_back(CRC_Address);
			temp_content.push_back((uint8_t) (CRC_Result));
			memorycontents.push_back (temp_content);
			temp_content.clear();
		}
	}

	{
		addresses.push_back(Mem_Start);
		for (int ii = 0; ii < (Mem_End - Mem_Start +1); ii++)
		{
			temp_content.push_back(Memory_Map[ii]);
		}
		memorycontents.push_back (temp_content);
		temp_content.clear();
	}


	ofstream ofs(output_filename);
	if (ofs.fail())
	{
		return -2;
	}

	int loopcounter;
	int current_address=0;
	int num_sectors = addresses.size();
	for (i=0 ; i < num_sectors; i ++)
	{
		int min_index = std::min_element(addresses.begin(), addresses.end()) - addresses.begin();
		if (addresses[min_index] != (current_address))
		{
			if (current_address != 0)
				ofs << endl;
			Write_address_to_file(&ofs, addresses[min_index]);
			current_address =  addresses[min_index];
			loopcounter = 0;
		}
		for (int jj=0 ;jj < memorycontents[min_index].size(); jj++)
		{
			if ((loopcounter!= 0) && (loopcounter%16 == 0))
				ofs << endl;
			Write_Byte_to_file(&ofs,  memorycontents[min_index][jj]);
			current_address++;
			loopcounter++;
		}
		addresses.erase(addresses.begin() + min_index);
		memorycontents.erase(memorycontents.begin() + min_index);

		//int minimum = min(addresses.begin(), addresses.end());
		//auto min = min_element(addresses.begin(), addresses.end());
		//current_address = addresses[(int)min];

	}

#ifdef UNDO_WRITE_2

	for (i=0; i < addresses_not_in_range.size(); i++)
	{
		if (addresses_not_in_range[i] < (int)Mem_Start)
			ofs << txt_not_in_range[i];
	}
	// Write CRC at the beginning if CRC Address < Start Address
	if (CRC_Address < Mem_Start)
	{
		Write_address_to_file(&ofs, CRC_Address);
		if (type == 16)
			Write_Word_to_file(&ofs, CRC_Result);
		else 
			Write_Byte_to_file(&ofs, (uint8_t) CRC_Result);
		ofs << endl;
	}
	// Write the memory map
	Write_address_to_file(&ofs, Mem_Start);
	for (i=0 ;i < (Mem_End - Mem_Start +1) ; i++)
	{
		if ((i!= 0) && (i%16 == 0))
			ofs << endl;
		Write_Byte_to_file(&ofs, Memory_Map[i]);
	}
	ofs << endl;

	// Write CRC at the end if CRC Address > End Address
	if (CRC_Address > Mem_End)
	{
		Write_address_to_file(&ofs, CRC_Address);
		if (type == 16)
			Write_Word_to_file(&ofs, CRC_Result);
		else 
			Write_Byte_to_file(&ofs, (uint8_t) CRC_Result);
		ofs << endl;
	}

	for (i=0; i < addresses_not_in_range.size(); i++)
	{
		if (addresses_not_in_range[i] > Mem_End)
			ofs << txt_not_in_range[i];
	}
#endif

	ofs << endl << "q" << endl;
	//ofs << Memory_Map;
	//Write_Byte_to_file(&ofs, Memory_Map);


	delete[] Memory_Map;
	ofs.close();

	return 0;
}

void Write_address_to_file(ofstream *outputfile, uint32_t addr)
{
	if (addr <= 0xFFFF)
			*outputfile << "@" << hex << uppercase << setfill('0') << setw(4) << addr << endl;
		else
			*outputfile << "@" << hex << uppercase << setfill('0') << setw(6) << addr << endl;
}

void Write_Byte_to_file(ofstream *outputfile, uint8_t val)
{
	*outputfile << hex << uppercase << setfill('0') << setw(2) << (int) val;
	//*outputfile << (int) val;
	*outputfile << " ";
}

void Write_Word_to_file(ofstream *outputfile, uint16_t val)
{
	Write_Byte_to_file(outputfile , (uint8_t)(val&0xFF));
	Write_Byte_to_file(outputfile , (uint8_t)((val&0xFF00)>>8));
}

/***************************************************************************//**
* A small function that converts chars 0-F to integers 0-16
*
* \param hex the char representing a hex number
* 
******************************************************************************/
uint8_t MSP430CRC_Calc::hextoint(char hex){
	if( (hex >= '0') && (hex <= '9') ){
		return(uint8_t(hex - '0'));
	}
	else if( (hex >= 'a') && (hex <= 'f') ){
		return(uint8_t(10 + hex - 'a'));
	}
	else if( (hex >= 'A') && (hex <= 'F') ){
		return(uint8_t(10 + hex - 'A'));
	}
	else{
		return 0xFF;
	}
}