

#pragma once

#include "Allogen/CSharp/Converter.hpp"
#include <functional>

#if defined(_MSC_VER)
# define ALLOGEN_CALLING_CONVENTION __stdcall
#else
# define ALLOGEN_CALLING_CONVENTION
#endif

namespace Allogen {
	namespace CSharp {

		/**
		 * A converter specialization for C++ integral types
		 *
		 * @tparam IntegralType the integral type to be converted
		 */
		template<typename R, typename... Args>
		struct Converter<std::function<R(Args...)>> {
			/**
			 * The C++ type this converter is operating on
			 */
			using Type = std::function<R(Args...)>;

			/**
			 * The JNI type this converter supports
			 */
			using CSharpType = void*;

            /**
			 * The JNI type this converter supports
			 */
            using CSharpFunc = typename Converter<R>::CSharpType(ALLOGEN_CALLING_CONVENTION *)(typename Converter<Args>::CSharpType...);

			/**
			 * Converts a C++ integer into a CSharp integer
			 *
			 * @param env the JNI environment
			 * @param i the C++ integer
			 *
			 * @return the CSharp integer
			 */
			static CSharpType toCSharp(Type func) {
                return nullptr;
			}

			/**
			 * Converts a CSharp integer into a C++ integer
			 *
			 * @param env the JNI environment
			 * @param i the CSharp integer
			 *
			 * @return the C++ integer
			 */
			static Type fromCSharp(CSharpType func) {
                return [func](Args... args) {
                    auto csfunc = reinterpret_cast<CSharpFunc>(func);
                    return Converter<R>::fromCSharp(csfunc(
                            Converter<Args>::toCSharp(
                                    std::forward<Args>(args)
                            )...
                    ));
                };
			}
		};

		/**
		 * A converter specialization for C++ integral types
		 *
		 * @tparam IntegralType the integral type to be converted
		 */
		template<typename... Args>
		struct Converter<std::function<void(Args...)>> {
			/**
			 * The C++ type this converter is operating on
			 */
			using Type = std::function<void(Args...)>;

			/**
			 * The JNI type this converter supports
			 */
			using CSharpType = void*;

            /**
			 * The JNI type this converter supports
			 */
            using CSharpFunc = void(ALLOGEN_CALLING_CONVENTION *)(typename Converter<Args>::CSharpType...);

			/**
			 * Converts a C++ integer into a CSharp integer
			 *
			 * @param env the JNI environment
			 * @param i the C++ integer
			 *
			 * @return the CSharp integer
			 */
			static CSharpType toCSharp(Type func) {
                return nullptr;
			}

			/**
			 * Converts a CSharp integer into a C++ integer
			 *
			 * @param env the JNI environment
			 * @param i the CSharp integer
			 *
			 * @return the C++ integer
			 */
			static Type fromCSharp(CSharpType func) {
                return [func](Args... args) {
                    auto csfunc = reinterpret_cast<CSharpFunc>(func);
                    csfunc(
                            Converter<Args>::toCSharp(
                                    std::forward<Args>(args)
                            )...
                    );
                };
			};
		};

	}
}
