#pragma once

#include "base_server.hpp"
#include <utilities/concurrency.hpp>

namespace demonware
{
	class udp_server : public base_server
	{
	public:
		struct endpoint_data
		{
			SOCKET socket{};
			sockaddr_in address{};

			endpoint_data() = default;

			endpoint_data(const SOCKET sock, const sockaddr* addr, const int size)
			{
				if (size != sizeof(this->address))
				{
					throw std::runtime_error("Invalid size");
				}

				this->socket = sock;
				std::memcpy(&this->address, addr, sizeof(this->address));
			}
		};

		using base_server::base_server;

		void handle_input(const char* buf, size_t size, endpoint_data endpoint);
		size_t handle_output(SOCKET socket, char* buf, size_t size, sockaddr* address, int* addrlen);
		bool pending_data(SOCKET socket);

		void frame() override;

	protected:
		virtual void handle(const endpoint_data& endpoint, const std::string& data) = 0;
		void send(const endpoint_data& endpoint, std::string data);

	private:
		struct in_packet
		{
			std::string data;
			endpoint_data endpoint;
		};

		struct out_packet
		{
			std::string data;
			sockaddr_in address;
		};

		using in_queue = std::queue<in_packet>;
		using out_queue = std::queue<out_packet>;
		using socket_queue_map = std::unordered_map<SOCKET, out_queue>;

		utilities::concurrency::container<in_queue> in_queue_;
		utilities::concurrency::container<socket_queue_map> out_queue_;
	};
}