/*
 * Copyright (c) 2017-2023 zhllxt
 *
 * author   : zhllxt
 * email    : 37792738@qq.com
 * 
 * Distributed under the Boost Software License, Version 1.0. (See accompanying
 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 */

#ifndef __ASIO2_RDC_OPTION_HPP__
#define __ASIO2_RDC_OPTION_HPP__

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
#pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include <cstdint>

#include <memory>
#include <functional>
#include <type_traits>
#include <any>

#include <asio2/base/error.hpp>
#include <asio2/base/define.hpp>

#include <asio2/base/detail/util.hpp>
#include <asio2/base/detail/function_traits.hpp>

#include <asio2/component/rdc/rdc_invoker.hpp>

namespace asio2::rdc
{
	struct option_base
	{
		virtual ~option_base() {}
		virtual std::any call_parser(bool is_send, void* data) = 0;
		virtual void emplace_request(std::any& k, void* v) = 0;
		virtual void execute_and_erase(std::any& k, std::function<void(void*)> cb) = 0;
		virtual void foreach_and_clear(std::function<void(void*, void*)> cb) = 0;
	};

	template<class IdT, class SendDataT, class RecvDataT = SendDataT>
	class option : public rdc::option_base
	{
	protected:
		using send_parser_fun = std::function<IdT(SendDataT)>;
		using recv_parser_fun = std::function<IdT(RecvDataT)>;

		send_parser_fun rdc_send_parser_;
		recv_parser_fun rdc_recv_parser_;

		using invoker_type  = detail::rdc_invoker_t<IdT, SendDataT, RecvDataT>;
		using iterator_type = typename invoker_type::iterator_type;

		using key_type = IdT;
		using val_type = typename invoker_type::value_type;

		invoker_type rdc_invoker_;

	public:
		option(option&&) noexcept = default;
		option(option const&) = default;
		option& operator=(option&&) noexcept = default;
		option& operator=(option const&) = default;

		template<class ParserFun, std::enable_if_t<
			!std::is_base_of_v<option, detail::remove_cvref_t<ParserFun>>, int> = 0>
		explicit option(ParserFun&& parser)
			: rdc_send_parser_(std::forward<ParserFun>(parser))
			, rdc_recv_parser_(rdc_send_parser_)
		{
		}
		template<class SendParserFun, class RecvParserFun>
		explicit option(SendParserFun&& send_parser, RecvParserFun&& recv_parser)
			: rdc_send_parser_(std::forward<SendParserFun>(send_parser))
			, rdc_recv_parser_(std::forward<RecvParserFun>(recv_parser))
		{
		}

		template<class ParserFun>
		option& set_send_parser(ParserFun&& parser)
		{
			rdc_send_parser_ = std::forward<ParserFun>(parser);
			return (*this);
		}
		template<class ParserFun>
		option& set_recv_parser(ParserFun&& parser)
		{
			rdc_recv_parser_ = std::forward<ParserFun>(parser);
			return (*this);
		}

		send_parser_fun& get_send_parser() noexcept { return rdc_send_parser_; }
		recv_parser_fun& get_recv_parser() noexcept { return rdc_recv_parser_; }

		detail::rdc_invoker_t<IdT, SendDataT, RecvDataT>& invoker() noexcept { return rdc_invoker_; }

		virtual std::any call_parser(bool is_send, void* data) override
		{
			if (is_send)
			{
				typename detail::remove_cvref_t<SendDataT>& d = *((typename detail::remove_cvref_t<SendDataT>*)data);
				key_type id = rdc_send_parser_(d);
				return std::any(std::move(id));
			}
			else
			{
				typename detail::remove_cvref_t<RecvDataT>& d = *((typename detail::remove_cvref_t<RecvDataT>*)data);
				key_type id = rdc_recv_parser_(d);
				return std::any(std::move(id));
			}
		}
		virtual void emplace_request(std::any& k, void* v) override
		{
			key_type& key = *std::any_cast<key_type>(std::addressof(k));
			val_type& val = *((val_type*)v);

			// 2023-07-11 bug fix : can't use std::move(key), beacuse the key will be used at later.
			rdc_invoker_.emplace(key, std::move(val));
		}
		virtual void execute_and_erase(std::any& k, std::function<void(void*)> cb) override
		{
			key_type& key = *std::any_cast<key_type>(std::addressof(k));

			if (auto iter = rdc_invoker_.find(key); iter != rdc_invoker_.end())
			{
				cb((void*)(std::addressof(iter->second)));

				rdc_invoker_.erase(iter);
			}
		}
		virtual void foreach_and_clear(std::function<void(void*, void*)> cb) override
		{
			for (auto& [k, v] : rdc_invoker_.reqs())
			{
				cb((void*)std::addressof(k), (void*)std::addressof(v));
			}

			rdc_invoker_.reqs().clear();
		}
	};

	// C++17 class template argument deduction guides
	template<class ParserFun>
	option(ParserFun)->option<
		typename detail::function_traits<detail::remove_cvref_t<ParserFun>>::return_type,
		typename detail::function_traits<detail::remove_cvref_t<ParserFun>>::template args<0>::type,
		typename detail::function_traits<detail::remove_cvref_t<ParserFun>>::template args<0>::type
	>;

	template<class SendParserFun, class RecvParserFun>
	option(SendParserFun, RecvParserFun)->option<
		typename detail::function_traits<detail::remove_cvref_t<SendParserFun>>::return_type,
		typename detail::function_traits<detail::remove_cvref_t<SendParserFun>>::template args<0>::type,
		typename detail::function_traits<detail::remove_cvref_t<RecvParserFun>>::template args<0>::type
	>;
}

#endif // !__ASIO2_RDC_OPTION_HPP__