#pragma once

#include "ehs/EHS.h"
#include "ehs/Str.h"
#include "BaseUDP.h"

namespace ehs
{
	/// A wrapper class for the user datagram protocol socket.
	class EHS_LIB_IO UDP : public BaseUDP
	{
	private:
		Socket hdl;

	public:
		/// Frees any native handles.
		~UDP() override;

		UDP();

		/// Default members initialization.
		UDP(IP version);

		UDP(UDP&& udp) noexcept;

		/// Copies some members from the given UDP object.
		/// @param [in] udp The UDP object to copy from.
		UDP(const UDP& udp);

		UDP& operator=(UDP&& udp) noexcept;

		/// Copies some members from the given UDP object.
		/// @param [in] udp The UDP object to copy from.
		/// @returns The UDP object that has been assigned to.
		UDP& operator=(const UDP& udp);

		/// Frees native handles and uninitializes them.
		void Release() override;

		/// Binds the UDP socket to a local address and port.
		/// @param [in] address The local IPv4 or IPv6 address to bind to. Resolves domain names. The given address can be empty, "127.0.0.1", or "localhost" to automatically find the appropriate device.
		/// @param [in] port The port to bind to.
		/// @note Requires the port given to be forwarded if this is called.
		void Bind(const Endpoint &endpoint) override;

		/// Sends data using a C-style byte array.
		/// @param [in] addr The remote Ipv4 or Ipv6 address to send to. Resolves domain names. The given address can be empty, "127.0.0.1", or "localhost" to automatically find the appropriate device.
		/// @param [in] port The remote port to send to.
		/// @param [in] data The C-style byte array to send.
		/// @param [in] size The size of the C-style byte array.
		/// @note The size of data to be sent cannot exceed "UDP::maxPayloadIpv4" or "UDP::maxPayloadIpv6".
		UInt_64 Send(const Endpoint &endpoint, const Byte* data, UInt_64 size) override;

		/// Receives data using the packet helper class.
		/// @param [out] addr The Ipv4 or Ipv6 address of the sender.
		/// @param [out] port The port of the sender.
		/// @param [out] data The C-style byte array received.
		/// @param [in] size The size of the pre-allocated C-style byte array.
		/// @returns The size of the data received.
		/// @warning The provided C-style byte array must be freed when finished using.
		UInt_64 Receive(Endpoint *endpoint, Byte* data, UInt_64 size) override;

		/// Sets whether or not receiving data blocks the next task.
		/// @param [in] blocking Whether or not to block.
		void SetBlocking(bool blocking) override;

		/// Retrieves whether or not this socket will block when receiving data.
		/// @returns The result.
		bool IsBlocking() const override;

		void SetIPv6Only(bool value) override;

		bool IsIPv6Only() const override;

		bool IsValid() const override;

	private:
		void Bind_v6(const Str_8& address, UInt_16 port) const;

		void Bind_v4(const Str_8& address, UInt_16 port) const;

		UInt_64 Send_v6(const Str_8& address, UInt_16 port, const Byte* data, UInt_64 size);

		UInt_64 Send_v4(const Str_8& address, UInt_16 port, const Byte* data, UInt_64 size);
	};
}