#pragma once

#include "Tuple.h"
#include "../PackAndUnpackOpers/ParseExcelParameter.h"
#include "../ExcelDoNotEdit/excel_addin_utils.h"


using std::initializer_list;

template <typename outputType, typename... ArgTypes>
struct Bind_Excel_Wrapper
{
	
	static XLOPER12* bind_excel_func(
		// the list of input from excel
		initializer_list<XLOPER12* >& excel_params,
		// function to test inputs are valid before executing actual function
		const std::function< bool(XLOPER12*, ArgTypes *...) >& cpp_function_check,
		// the function which does the c++ work for the excel function
		const std::function< void(outputType*, ArgTypes *...) >& cpp_function,
		// store output of cpp_function
		outputType* output,
		// arguments used by cpp_function 
		ArgTypes*... excel_function_params)
	{
		// create the output object and ensure no memory leak
		XLOPER12* excel_output = new XLOPER12;
		excel_output->xltype = xltypeErr | xlbitDLLFree;
		excel_output->val.err = xlerrValue;

		try
		{

			std::vector<XLOPER12*> v_excel_params;

			// tell vector to allocate enough memory
			v_excel_params.reserve(excel_params.size());

			for (auto& p : excel_params)
				v_excel_params.push_back(p);

			// load the parameters from excel
			ParseExcelParameter::parse(v_excel_params, excel_function_params...);

			// check all the inputs are valid, returning error if not
			if (!cpp_function_check(excel_output, excel_function_params...))
				return excel_output;

			// run the cpp function
			cpp_function(output, excel_function_params...);

			ParseExcelParameter::pack(output, excel_output);

		}
		catch (std::exception const& ex)
		{
			std::string message("Exception: ");
			message += ex.what();
			create_error_message(excel_output, message.c_str());
		}
		catch (...)
		{
			create_error_message(excel_output, "Unknown exception caught");
		}

		return excel_output;
	}
	
	static XLOPER12* (*bind_excel_ptr) (
		initializer_list<XLOPER12* >&,
		const std::function< bool(XLOPER12*, ArgTypes *...) >&,
		const std::function< void(outputType*, ArgTypes *...) >&,
		outputType*,
		ArgTypes*...
		);

};


template <typename outputType, typename... ArgTypes>
XLOPER12* bind_excel_func(
	// the list of input from excel
	initializer_list<XLOPER12* >& excel_params,
	// function to test inputs are valid before executing actual function
	const std::function< bool(XLOPER12*, ArgTypes * ...) >& cpp_function_check,
	// the function which does the c++ work for the excel function
	const std::function< void(outputType*, ArgTypes * ...) >& cpp_function)
{

	std::tuple<outputType, ArgTypes...> cpp_args;
	std::tuple<outputType*, ArgTypes *...> cpp_arg_ptrs = Tuple::make_pointer_tuple(cpp_args);
	std::tuple<	initializer_list<XLOPER12* >&,
		const std::function< bool(XLOPER12*, ArgTypes *...) >&,
		const std::function< void(outputType*, ArgTypes *...) >&> other_args(excel_params, cpp_function_check, cpp_function);
	auto all_args = std::tuple_cat(other_args, cpp_arg_ptrs);

	static XLOPER12* (*bind_excel_ptr) (
		initializer_list<XLOPER12* >&,
		const std::function< bool(XLOPER12*, ArgTypes *...) >&,
		const std::function< void(outputType*, ArgTypes *...) >&,
		outputType*,
		ArgTypes *...
		)
		=
		&Bind_Excel_Wrapper<outputType, ArgTypes...>::bind_excel_func;

	Tuple::TupleBind
		<
		XLOPER12*,
		initializer_list<XLOPER12* >&,
		const std::function< bool(XLOPER12*, ArgTypes *...) >&,
		const std::function< void(outputType*, ArgTypes *...) >&,
		outputType*,
		ArgTypes *...
		>
		func_call(bind_excel_ptr, all_args);

	return func_call.delayed_dispatch();
}