#pragma once

#include <memory>
#include <string>
#include <vector>

#include "IPAddress.h"
#include "blitz/Defines.h"

#include "blitz/misc/DataBuffer.h"

#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#define closesocket close
#endif

#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif

namespace blitz {
namespace network {

class IPAddress;

typedef int SocketHandle;

class TCPSocket {
  public:
	enum class Status { Connected, Disconnected, Error };

  private:
	bool m_Blocking;
	Status m_Status;
	SocketHandle m_Handle;

	IPAddress m_RemoteIP;
	std::uint16_t m_Port;
	sockaddr m_RemoteAddr;

  public:
	TCPSocket();
	TCPSocket(TCPSocket&&);
	virtual ~TCPSocket();

	void SetStatus(Status status);

	bool SetBlocking(bool block);
	bool IsBlocking() const noexcept;

	Status GetStatus() const noexcept;
	SocketHandle GetHandle() const noexcept;

	bool Connect(const std::string& ip, std::uint16_t port);
	bool Connect(const IPAddress& address, std::uint16_t port);

	void Disconnect();

	std::size_t Send(const std::string& data);
	std::size_t Send(DataBuffer& buffer);

	std::size_t Send(const uint8_t* data, std::size_t size);
	DataBuffer Receive(std::size_t amount);

	std::size_t Receive(DataBuffer& buffer, std::size_t amount);

	REMOVE_COPY(TCPSocket);

	friend class TCPListener;
};

void SendPacket(const DataBuffer& data, network::TCPSocket& socket);

typedef std::shared_ptr<TCPSocket> SocketPtr;

}  // namespace network
}  // namespace blitz