#ifndef NETWORK_SOCKET_H_
#define NETWORK_SOCKET_H_

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

#include "td/misc/DataBuffer.h"

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


#define closesocket close
#endif

#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif

#define REMOVE_COPY(className) \
	className(const className &) = delete;\
	className& operator=(const className &) = delete

namespace td {
namespace network {

class IPAddress;

typedef int SocketHandle;

class Socket {
public:
	enum Status { Connected, Disconnected, Error };
	enum Type { TCP, UDP };

private:
	bool m_Blocking;
	Type m_Type;
	Status m_Status;

protected:
	SocketHandle m_Handle;

	Socket(Type type);
	void SetStatus(Status status);

public:
	virtual ~Socket();

	Socket(Socket&& rhs) = default;
	Socket& operator=(Socket&& rhs) = default;

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

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

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

	void Disconnect();

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

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

	virtual std::size_t Receive(DataBuffer& buffer, std::size_t amount) = 0;
};

typedef std::shared_ptr<Socket> SocketPtr;

} // ns network
} // ns td

#endif

