#include "ExcelDoNotEdit/excel_addin_export.h"
#include "ExcelDoNotEdit/excel_addin_utils.h"
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include "ExcelDoNotEdit/xlcall.h"
#include "ExcelFunction.h"
#include <array>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <assert.h> 

using std::string;
using std::stringstream;
using std::ifstream;

const std::string CURRENT_FILE = __FILE__;
const std::string EXCEL_FUNC_DATA_FILE1 = CURRENT_FILE.substr(0, CURRENT_FILE.rfind("\\")) + "/excel_function_data.txt";
const std::string EXCEL_FUNC_DATA_FILE2 = CURRENT_FILE.substr(0, CURRENT_FILE.rfind("\\")) + "/hull_white_tests.txt";
const std::array<const std::string, 2> EXCEL_FUNC_DATA_FILES = { EXCEL_FUNC_DATA_FILE1, EXCEL_FUNC_DATA_FILE2 };
// maximum excel functions we can register
const unsigned int MAX_EXCEL_FUNCTIONS = 1000;

void read_funcs(string filename, std::vector<ExcelFunction>& func_data_out)
{	
	stringstream path;
	string p = path.str();
	ifstream in(filename);
	string line;

	int j = 0;

	std::vector<std::string> func_data;

	// read a single row
	while (getline(in, line))
	{

		// load row to stringstream
		stringstream ss(line);
		string data;

		if (j == 0)
		{
			func_data.resize(0);

			// read strings up to a tab
			while (getline(ss, data, '\t'))
			{
				if (data.size() > 0)
				{
					data.erase(std::remove(data.begin(), data.end(), '"'), data.end());
					func_data.push_back(data);
				}
				else
					// we reached dead space or an entry was forgotten
					break;
			}
			++j;
			if (func_data.size() == 3)
				continue;
			else
				break;
		}

		if (j == 1)
		{
			// we should have func name, help category and func description at this point
			assert(func_data.size() == 3);

			std::vector<std::string> data_to_join;

			// read strings up to a tab
			while (getline(ss, data, '\t'))
			{
				if (data.size() > 0)
				{
					data.erase(std::remove(data.begin(), data.end(), '"'), data.end());
					data_to_join.push_back(data);
				}
				else
					// we reached dead space or an entry was forgotten
					break;
			}

			std::stringstream ss2;
			for (size_t i = 0; i < data_to_join.size(); ++i)
			{
				if (i != 0)
					ss2 << ",";
				ss2 << data_to_join[i];
			}

			std::string joined_data = ss2.str();
			func_data.push_back(joined_data);
			++j;
			continue;
		}

		// third line should contains cpp types, which is not needed here
		if (j == 2)
		{
			++j;
			continue;
		}

		if (j == 3)
		{
			// we should have func name, help category, func description 
			// and all param names in single string at this point
			assert(func_data.size() == 4);

			// read strings up to a tab
			while (getline(ss, data, '\t'))
			{
				if (data.size() > 0)
				{
					data.erase(std::remove(data.begin(), data.end(), '"'), data.end());
					func_data.push_back(data);
				}
				else
					// we reached dead space or an entry was forgotten
					break;
			}
			j = 0;
		}

		// min args for function with one param is 5
		if (func_data.size() >= 5)
			func_data_out.push_back(func_data);
	}
}


std::vector<ExcelFunction> func_data_out;

registration_info g_registration_info[MAX_EXCEL_FUNCTIONS];



//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
// xlAutoOpen
//
//    A dll that exports this function is an xll. Excel calls xlAutoOpen
//    when an xll gets loaded/registered by Excel. This is where we
//    register all our function with Excel.
//
int WINAPI xlAutoOpen()
{
#pragma EXPORT_FUNC

	// functions read from tab seperated file
	for (auto file : EXCEL_FUNC_DATA_FILES)
		read_funcs(file, func_data_out);

	assert(func_data_out.size() < MAX_EXCEL_FUNCTIONS);

	for (size_t i = 0; i < func_data_out.size(); ++i)
	{
		g_registration_info[i] = func_data_out[i].get_registration_info();
	}
	
	register_functions((unsigned int) func_data_out.size(), g_registration_info);
	return 1;
}

//////////////////////////////////////////////////////////////////////////////
//
// xlAutoClose
//
//    Called by Excel when the xll is unloaded/unregistered.
//
int WINAPI xlAutoClose()
{
#pragma EXPORT_FUNC
	return 1;
}

//////////////////////////////////////////////////////////////////////////////
//
// xlAutoFree12
//
//    Called by Excel to free resources stored in an XLOPER12 that were
//    allocated in the xll.
//
void WINAPI xlAutoFree12(XLOPER12* px)
{
#pragma EXPORT_FUNC
	free_xloper_memory(px);
}
