#pragma once

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

#include "Socket.h"
#include "BaseTCP.h"

namespace ehs
{
	/// A wrapper class for the transmission control protocol socket.
	class EHS_LIB_IO TCP : public BaseTCP
	{
	protected:
		Socket hdl;

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

		/// Default members initialization.
		TCP();

		TCP(IP IP);

		TCP(TCP&& tcp) noexcept;

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

		TCP& operator=(TCP&& tcp) noexcept;

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

		void Initialize() override;

		/// 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 Str_8& address, UInt_16 port) override;

		/// Listens for incoming connections. Used for servers or PtP.
		void Listen() override;

		/// Accepts an incoming connection. Used for servers or PtP.
		/// @returns The accepted client object.
		TCP* Accept() override;

		/// Connects to a TCP Socket that listens for incoming connections. Used for clients or PtP.
		/// @param address The address of the listening TCP socket. Resolves domain names. The given address can be empty, "127.0.0.1", or "localhost" to automatically find the appropriate device.
		/// @param port The port of the listening TCP socket.
		void Connect(const Str_8& address, UInt_16 port) override;

		/// Sends data in a C-style array with raw functionality. Meaning no internal help outside of native functions besides error checking.
		/// @param [in] buffer The C-style array to send.
		/// @param [in] size The size of the given C-style array.
		/// @returns The size of the data actually sent in bytes.
		UInt_64 Send(const Byte* buffer, UInt_32 size) override;

		/// Receives data in a C-style array with raw functionality. Meaning no internal help outside of native functions besides error checking.
		/// @param [out] buffer The C-style array to receive with.
		/// @param [in] size The size of the given C-style array.
		/// @returns The size of the data actually received in bytes.
		UInt_64 Receive(Byte* buffer, UInt_32 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);

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

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

		void Connect_v4(const Str_8& address, UInt_16 port);
	};
}