
#pragma once

/** @file Socket.h
	@brief The Socket class. */

#ifdef WIN32

#include "kNetBuildConfig.h"
#include "win32/WS2Include.h"
#define KNET_EWOULDBLOCK WSAEWOULDBLOCK
#define KNET_SOCKET_ERROR SOCKET_ERROR
#define KNET_ACCEPT_FAILURE SOCKET_ERROR

namespace kNet
{
typedef int socklen_t;
}

#elif defined(UNIX) || defined(ANDROID)

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>

#define INVALID_SOCKET (0)
#define KNET_SOCKET_ERROR (-1)
#define KNET_ACCEPT_FAILURE (-1)
#define KNET_EWOULDBLOCK EWOULDBLOCK
#define closesocket close
#define TIMEVAL timeval
#define SD_SEND SHUT_WR
#define SD_BOTH SHUT_RDWR
#define _stricmp strcasecmp

namespace kNet
{
typedef unsigned int SOCKET;
}
#endif

#include <vector>
#include <list>

#include "SharedPtr.h"
#include "EndPoint.h"
#include "WaitFreeQueue.h"
#include "Event.h"

namespace kNet
{

/// Identifiers for the possible bottom-level tranport layers.
enum SocketTransportLayer
{
	InvalidTransportLayer = 0, ///< A default invalid value for uninitialized sockets.
	SocketOverUDP,
	SocketOverTCP
};

std::string SocketTransportLayerToString(SocketTransportLayer transport);

/// Converts the given string (case-insensitive parsing) to the corresponding SocketTransportLayer enum.
/// "tcp" & "socketovertcp" -> SocketOverTCP.
/// "udp" & "socketoverudp" -> SocketOverUDP.
/// Other strings -> InvalidTransportLayer.
SocketTransportLayer StringToSocketTransportLayer(const char *str);

enum SocketType
{
	InvalidSocketType = 0, ///< A default invalid value for uninitialized sockets.
	ServerListenSocket, ///< For TCP: a listen socket. For UDP: the single master socket handle that is used to send & receive all data.
	ServerClientSocket, ///< For TCP: a client data socket. For UDP: a slave-mode Socket object that shares the underlying socket handle with the UDP master Socket.
	ClientSocket ///< A client-side socket.
};

std::string SocketTypeToString(SocketType type);

typedef int OverlappedTransferTag;

#ifdef WIN32
typedef WSABUF kNetBuffer;
#else
struct kNetBuffer
{
	/// Stores the number of bytes allocated to buf.
	/// When sending out a message, this field specifies the number of bytes the client
	/// can write to buf, at maximum.
	unsigned long len;

	char *buf;
};
#endif

struct OverlappedTransferBuffer
{
	kNetBuffer buffer;
#ifdef WIN32
	WSAOVERLAPPED overlapped;
#endif

	/// Stores the number of bytes actually in use in buffer.buf. When sending out a message,
	/// specify the actual number of bytes filled to buffer.buf here.
	int bytesContains;

    /// Stores the total number of bytes allocated to the buffer in the overlapped structure.
    int bytesAllocated;

	sockaddr_in from;
	socklen_t fromLen;
};

/// Represents a low-level network socket.
class Socket : public RefCountable
{
public:
	Socket();

	Socket(SOCKET connection, const EndPoint &localEndPoint, const char *localHostName,
		const EndPoint &remoteEndPoint, const char *remoteHostName, 
		SocketTransportLayer transport, SocketType type, size_t maxSendSize);

	Socket(const Socket &);
	~Socket();

	Socket &operator=(const Socket &);

	/// Sets the underlying socket send buffer (SO_SNDBUF) size.
	void SetSendBufferSize(int bytes);
	/// Sets the underlying socket receive buffer (SO_RCVBUF) size.
	void SetReceiveBufferSize(int bytes);

	/// Returns the current value for the send buffer of this socket.
	int SendBufferSize() const;
	/// Returns the current value for the receive buffer of this socket.
	int ReceiveBufferSize() const;

	/// Returns true if the connection is both write-open AND read-open.
	bool Connected() const;
	/// Returns true if the connection is open for reading from. This does not mean that there necessarily is new data
	/// to be immediately read - it only means that the socket is open for receiving data.
	bool IsReadOpen() const { return IsOverlappedReceiveReady() || readOpen; }
	/// Returns true if the connection is open for writing to.
	bool IsWriteOpen() const { return writeOpen; }
	/// Forces this socket to be treated as if it has been read-closed.
	void MarkReadClosed() { readOpen = false; }
	/// Forces this socket to be treated as if it has been write-closed.
	void MarkWriteClosed() { writeOpen = false; }

	/// If this function returns true, this socket represents a UDP server socket instance. For a UDP server,
	/// the data for all clients is received through this same socket, and there are no individual sockets created for
	/// each new connection, like is done with TCP.
	bool IsUDPServerSocket() const { return transport == SocketOverUDP && type == ServerListenSocket; }
	/// Returns whether this socket is a UDP slave socket. [worker thread]
	bool IsUDPSlaveSocket() const { return transport == SocketOverUDP && type == ServerClientSocket; }

	/// Performs a write close operation on the socket, signalling the other end that no more data will be sent. Any data
	/// currently left in the send buffers will be sent over to the other side, but no new data can be sent. The connection
	/// will remain half-open for reading, and Receive() calls may still be made to the socket. When a read returns 0, the
	/// connection will be transitioned to bidirectionally closed state (Connected() will return false).
	void Disconnect();
	/// Performs an immediate write and read close on the socket, without waiting for the connection to gracefully shut down.
	void Close();

	/// Sends the given data through the socket. This function may only be called if Socket::IsWriteOpen() returns true. If
	/// the socket is not write-open, calls to this function will fail.
	/// This function is an orthogonal API to the overlapped IO Send routines. Do not mix these API calls when doing
	/// networking, but instead choose one preferred method and consistently use it.
	bool Send(const char *data, size_t numBytes);

	/// Waits until the Socket is ready for sending. Returns true if the socket transitioned to write-ready state in the given
	/// time period, or false if the wait timed out or if some other error occurred.
	/// This function is an orthogonal API to the overlapped IO Send routines. Do not mix these API calls when doing
	/// networking, but instead choose one preferred method and consistently use it.
	bool WaitForSendReady(int msecs);

	/// Starts the sending of new data. After having filled the data to send to the OverlappedTransferBuffer that is
	/// returned here, commit the send by calling EndSend. If you have called BeginSend, but decide not to send any data,
	/// call AbortSend instead (otherwise memory will leak).
    /// @param maxBytesToSend Specifies the size of the buffer that must be returned. Specify the size (or at least an 
    ///         upper limit) of the message you are sending here. Specify the actual number of bytes filled in the resulting
    ///         structure.
	/// @return A transfer buffer where the data to send is to be filled in. If no new data can be sent at this time,
	///         this function returns 0.
	OverlappedTransferBuffer *BeginSend(int maxBytesToSend);
	/// Finishes and queues up the given transfer that was created with a call to BeginSend.
	/// @return True if send succeeded, false otherwise. In either case, the ownership of the passed buffer send
	///         is taken by this Socket and may not be accessed anymore. Discard the pointer after calling this function.
	bool EndSend(OverlappedTransferBuffer *send);
	/// Cancels the sending of data. Call this function if you first call BeginSend, but decide you don't want to send the data.
	/// This frees the given buffer, do not dereference it after calling this function.
	void AbortSend(OverlappedTransferBuffer *send);

#ifdef WIN32
	/// Returns the number of sends in the send queue.
	int NumOverlappedSendsInProgress() const { return queuedSendBuffers.Size(); }
	/// Returns the maximum number of sends that can be queued up simultaneously.
	int MaxOverlappedSendsInProgress() const { return queuedSendBuffers.Capacity(); }
#endif
	/// Returns true if it is possible to send out new data. In this case, a call to BeginSend will succeed.
	bool IsOverlappedSendReady();
	/// Returns the event object that should be waited on to receive a notification when it is possible to send out new data.
	Event GetOverlappedSendEvent();

	/// Waits for the max given amount of time for new data to be received from the socket.
	/// @return True if new data was received, false if the timeout period elapsed.
//	bool WaitForData(int msecs); // this will be removed completely.

	/// Reads in data from the socket. If there is no data available, this function will not block, but will immediately
	/// return 0.
	/// This function issues an immediate recv() call to the socket and is not compatible with the Overlapped Transfer API
	/// above. Do not mix the use of these two APIs, but pick one method to use and stay with it.
	/// @param endPoint [out] If the socket is an UDP socket that is not bound to an address, this will contain the source address.
	/// @return The number of bytes that were successfully read.
	size_t Receive(char *dst, size_t maxBytes, EndPoint *endPoint = 0);

	/// Call to receive new data from the socket.
	/// @return A buffer that contains the data, or 0 if no new data was available. When you are finished reading the buffer, call
	///         EndReceive to free up the buffer, or memory will leak.
	OverlappedTransferBuffer *BeginReceive();
	/// Finishes a read operation on the socket. Frees the given buffer to be re-queued for a future socket read operation.
	void EndReceive(OverlappedTransferBuffer *buffer);
#ifdef WIN32
	/// Returns the number of receive buffers that have been queued for the socket.
	int NumOverlappedReceivesInProgress() const { return queuedReceiveBuffers.Size(); }
	/// Returns the maximum number of receive buffers that can be queued for the socket.
	int MaxOverlappedReceivesInProgress() const { return queuedReceiveBuffers.Capacity(); }
#endif
	/// Returns true if there is new data to be read in. In that case, BeginReceive() will not return 0.
	bool IsOverlappedReceiveReady() const;
	/// Returns the event object that will be notified whenever data is available to be read from the socket.
	Event GetOverlappedReceiveEvent(); // [worker thread]

	/// Returns which transport layer the connection is using. This value is either SocketOverUDP or SocketOverTCP.
	SocketTransportLayer TransportLayer() const { return transport; }

	/// Returns the type of this socket object.
	SocketType Type() const { return type; }

	/// Returns the maximum amount of bytes that can be sent through to the network in one call. If you try sending
	/// more data than specified by this value, the result is undefined.
	size_t MaxSendSize() const { return maxSendSize; }

	/// Returns the local EndPoint this socket is bound to.
	const EndPoint &LocalEndPoint() const { return localEndPoint; }
	/// Returns the local address (local hostname) of the local end point this socket is bound to.
	const char *LocalAddress() const { return localHostName.c_str(); }
	/// Returns the local port that this socket is bound to.
	unsigned short LocalPort() const { return localEndPoint.port; }

	/// Returns the remote EndPoint this socket is connected to.
	/// If SocketType == ServerListenSocket, this socket is not bound
	/// to any remote end point, and so the returned struct is uninitialized (filled with zeroes).
	const EndPoint &RemoteEndPoint() const { return remoteEndPoint; }
	/// Returns the destination address (destination hostname) of the remote end point this socket is connected to.
	/// If SocketType == ServerListenSocket, returns an empty string.
	const char *DestinationAddress() const { return remoteHostName.c_str(); }
	/// Returns the destination port of the remote end point this socket is connected to.
	/// If SocketType == ServerListenSocket, returns 0.
	unsigned short DestinationPort() const { return remoteEndPoint.port; }

	/// Returns a human-readable representation of this socket, specifying the peer address and port this socket is
	/// connected to.
	std::string ToString() const;

	/// Sets the socket to blocking or nonblocking state.
	void SetBlocking(bool isBlocking);

	SOCKET &GetSocketHandle() { return connectSocket; }

	/// Enables or disables the use of Nagle's algorithm (TCP_NODELAY) for this socket.
	/// Nagle's algorithm reduces the bandwidth consumption of the channel, but can increase latency.
	/// Conversely, disabling Nagle's algorithm improves the latency, but increases the number of IP packets sent to the network.
	/// By default, Nagle's algorithm is enabled.
	/// Read more about Nagle's algorithm here: http://msdn.microsoft.com/en-us/library/ms817942.aspx
	void SetNaglesAlgorithmEnabled(bool enabled);

private:
	/// Stores the handle to the underlying BSD socket object. Has the value INVALID_SOCKET if uninitialized.
	SOCKET connectSocket;

	/// Specifies the local system end point (IP and port) this socket is bound to.
	EndPoint localEndPoint;

	/// Specifies the network host name of the local end point (the local system).
	/// If the local end point does not have a hostname, this field is the string representation of the
	/// system IP address (one of them, there may be multiple IPs).
	std::string localHostName;
	
	/// Specifies the remote system end point (IP and port) this socket is bound to (== the "peer" address).
	/// If SocketType == ServerListenSocket or transport == SocketOverUDP, this socket is not bound
	/// to any remote end point, and so this struct is uninitialized (filled with zeroes).
	EndPoint remoteEndPoint;

	/// If this socket is a UDP socket, the remoteEndPoint variable is copied (cached) to the variable
	/// udpPeerAddress to avoid having to reconstruct it at each Socket::Send() call (this is an optimization).
	/// This field is only used if this socket is a UDP socket.
	sockaddr_in udpPeerAddress;

	/// Specifies the network host name of the remote end point (== the remote system == the "peer").
	/// If the remote end point does not have a known hostname, this field is the string representation of the
	/// remote IP address. If SocketType == ServerListenSocket, this field is empty.
	std::string remoteHostName;

	/// Specifies the underlying transport protocol that this Socket is using (TCP or UDP).
	SocketTransportLayer transport;

	/// Specifies the type of this SOCKET object.
	/// UDP server sockets are never bound to a remote, since the same socket is used to send and receive data to and 
	/// from all client addresses. UDP client sockets and TCP sockets on the other hand are always bound to a peer.
	/// UDP client sockets are shared slave copies of the single UDP server socket. In this case, this field is used 
	/// to remember that this Socket object is not authoritative over the OS socket object and should not close it 
	/// when tearing down this socket.
	SocketType type;

	/// Specifies the maximum amount of data that can be sent to the socket at one call to send().
	size_t maxSendSize;

	/// Tracks whether data can be sent through this socket.
	bool writeOpen;

	/// Tracks whether the socket is open for receiving data (doesn't mean that there necessarily exists new data to be read).
	bool readOpen;

#ifdef WIN32
	WaitFreeQueue<OverlappedTransferBuffer*> queuedReceiveBuffers;
	WaitFreeQueue<OverlappedTransferBuffer*> queuedSendBuffers;

	/// Frees all allocated data in the queuedReceiveBuffers and queuedSendBuffers queues.
	void FreeOverlappedTransferBuffers();

	void EnqueueNewReceiveBuffer(OverlappedTransferBuffer *buffer = 0);
#endif
};

} // ~kNet
