
#include "sockets.h"
#include <string.h>
#include "Mstcpip.h"

#include <exception>
#pragma comment(lib, "wsock32.lib")
#pragma comment(lib, "Ws2_32.lib")



// ------------------------------------------------------------------------------------------------


void sockets :: socketsStart(void) 
{
	WSADATA versionInfo;
	if (WSAStartup(MAKEWORD(2,0), &versionInfo) != 0)
	{
		throw std :: exception("Failed to initialize sockets.");
	}
}

// ------------------------------------------------------------------------------------------------

sockets :: clientSocket :: clientSocket(void)
{
	socketHandle_ = 0;
}

// ------------------------------------------------------------------------------------------------

sockets :: clientSocket :: clientSocket(SOCKET socketHandle)
{
	socketHandle_ = socketHandle;
}

// ------------------------------------------------------------------------------------------------

bool sockets :: clientSocket :: connectToHost(const char* hostName, int port)
{
	hostent* host = gethostbyname(hostName);
	if (host == NULL)
	{
		throw std::exception("gethostbyname call failure");
	}

	sockaddr_in sai;	
	memset(&sai, 0, sizeof(sockaddr_in));

	sai.sin_family = AF_INET;
	sai.sin_port   = htons(port);
	sai.sin_addr   = *((in_addr*)host->h_addr);

	socketHandle_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (socketHandle_ == INVALID_SOCKET)
	{
		throw std :: exception("Socket creation failed!");
	}

	BOOL value = TRUE;
    int value_size = sizeof(BOOL);
	
    DWORD         dwBytesRet = 0;
    tcp_keepalive   alive;

	alive.onoff = TRUE;
	alive.keepalivetime = 2000;
	alive.keepaliveinterval = 2000;

	if (WSAIoctl(socketHandle_, SIO_KEEPALIVE_VALS, &alive, sizeof(alive), NULL, 0, &dwBytesRet, NULL, NULL) == SOCKET_ERROR) {
		throw std :: exception("Cannot set KEEP_ALIVE state in socket!");
	}

	/* pokus se otevrit socket */
	if (connect(socketHandle_, (sockaddr *) &sai, sizeof(sockaddr_in)) == 0)
	{
		if (setsockopt(socketHandle_, SOL_SOCKET, SO_KEEPALIVE, (char*)&value, value_size) == SOCKET_ERROR) 
		{
        	throw std :: exception("Cannot set KEEP_ALIVE state in socket!");
    	}	
		return true;
	}
	else 
	{
		/* nepodarilo se otevrit socket, zavri ho a uvolni */
		closesocket(socketHandle_);
		socketHandle_ = 0;
		return false;
	}
}

// ------------------------------------------------------------------------------------------------

int sockets :: clientSocket :: sendData(const char* data, int size)
{
	int len;
	if ((len = send(socketHandle_, data, size, 0)) == SOCKET_ERROR) 
	{
		throw std :: exception("Send failure!");
	}

	return len;
}

// ------------------------------------------------------------------------------------------------

int sockets :: clientSocket :: recvData(char* buf, int size)
{
	int len;
	if ((len = recv(socketHandle_, buf, size, 0)) == SOCKET_ERROR)
	{
		throw std :: exception("Recv failure!");
	}

	return len;
}

// ------------------------------------------------------------------------------------------------

void sockets :: clientSocket :: close(void)
{
	if (socketHandle_ != 0)
	{
		/* jestlize se nepodari zavrit socket, vyhod vyjimku */
		if (closesocket(socketHandle_) != 0)
		{
			throw std :: exception("Closing socket failed!");
		}
		else
		{
			socketHandle_ = 0;
		}
	}
}

// ================================================================================================

sockets :: serverSocket :: serverSocket(void)
{
	socketHandle_ = 0;
}

// ------------------------------------------------------------------------------------------------

void sockets :: serverSocket :: listenPort(int port)
{
	listenPort(port, SOMAXCONN);
}

// ------------------------------------------------------------------------------------------------

void sockets :: serverSocket :: listenPort(int port, int maxConnections)
{
	sockaddr_in sai;
	memset(&sai, 0, sizeof(sockaddr_in));

	sai.sin_family = PF_INET;
	sai.sin_port   = htons(port);
	
	// vytvorime socket
	socketHandle_        = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (socketHandle_ == INVALID_SOCKET)
	{
		throw std :: exception("Socket creation failed!");
	}
	
	// bindujeme ho s portem
	if (bind(socketHandle_, (sockaddr*)&sai, sizeof(sockaddr_in)) == SOCKET_ERROR)
	{
		throw std :: exception("Socket binding error!");
	}

	// zacneme poslouchat na portu
	if (listen(socketHandle_, maxConnections) != 0)
	{
		throw std :: exception("Listening to port call failed!");
	}
}

// ------------------------------------------------------------------------------------------------

sockets :: clientSocket* sockets :: serverSocket :: acceptConnection(void)
{
	// accept socket
	SOCKET newSocket = accept(socketHandle_, 0, 0);
	if (newSocket == INVALID_SOCKET)
	{
		throw std :: exception("Acceptation error!");
	}

    BOOL value = TRUE;
    int value_size = sizeof(BOOL);


    if (setsockopt(newSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&value, value_size) == SOCKET_ERROR) {
        throw std :: exception("Cannot set KEEP_ALIVE state in socket!");
    }

	try
	{
		clientSocket * result = new clientSocket(newSocket);
		return result;
	}
	catch (std :: bad_alloc b) {
		closesocket(newSocket);
		throw;
	}
}

// ------------------------------------------------------------------------------------------------

void sockets :: serverSocket :: close(void)
{
	/* jestlize se nepodari zavrit socket, vyhod vyjimku */
	if (closesocket(socketHandle_) != 0)
	{
		throw std :: exception("Closing socket failed!");
	}

	socketHandle_ = 0;
}

// ------------------------------------------------------------------------------------------------