#ifndef _WIN32
	#include <unistd.h>
	#include <arpa/inet.h>
	#define INVALID_SOCKET -1
	#define SOCKET_ERROR   -1
#else
	#include <winsock2.h>
	#include <windows.h>
	#define close(a) closesocket(a)
	HANDLE ghMutex = CreateMutex(NULL, FALSE, NULL);
	#pragma comment(lib, "ws2_32.lib")
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "libsocket.h"
#include "const.h"

#define bufferSize  (1 << 23)

int socketDescriptor;
int lenBuffer;
char sendBuff[bufferSize];


void connectServer(const char * ip, int port){

#ifdef _WIN32
	WSADATA wsaData;
	int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != 0){
		fprintf(stderr, "WSAStartup failed: %d\n", iResult);
		return;
	}
#endif

	socketDescriptor = socket(AF_INET, SOCK_STREAM, 0);

	if (socketDescriptor == INVALID_SOCKET){
		fprintf(stderr, "EXIT_OPEN_SOCKET_FAILED\n");
		exit(EXIT_OPEN_SOCKET_FAILED);

	}

	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = inet_addr(ip);
	int error = connect(socketDescriptor, (struct sockaddr *)&addr, sizeof(addr));
	if (error == SOCKET_ERROR){
		fprintf(stderr, "EXIT_CONNECTION_FAILED\n");
		exit(EXIT_CONNECTION_FAILED);
	}
}

void Receive(char* buff, int lenBuffer){
	int len = 0;
	while (len < lenBuffer){
		int currlen = recv(socketDescriptor, buff + len, lenBuffer - len, 0);
		if (currlen == SOCKET_ERROR){
			fprintf(stderr, "EXIT_CONNECTION_FAILED\n");
			exit(EXIT_CONNECTION_FAILED);
		}
		if (currlen == 0){
			fprintf(stderr, "Connection has been closed\n");
			exit(EXIT_SOCKET_CLOSED);
		}
		len += currlen;
	}
}
int nextInt(){
	char buff[8];
	Receive(buff, 4);
	return *(int *)buff;
}

float nextFloat(){
	char buff[8];
	Receive(buff, 4);
	return *(float *)buff;
}

void writeInt(int val){
	if (lenBuffer + 4 > bufferSize) return;
	*(int *)(sendBuff + lenBuffer) = val;
	lenBuffer += 4;
}

void writeFloat(float val){
	if (lenBuffer + 4 > bufferSize) return;
	*(float *)(sendBuff + lenBuffer) = val;
	lenBuffer += 4;
}

void flush(){
	int sendLen = 0;
	while (sendLen < lenBuffer){
		int sendRes = send(socketDescriptor, sendBuff + sendLen, lenBuffer - sendLen, 0);
		if (sendRes == SOCKET_ERROR){
			fprintf(stderr, "EXIT_CONNECTION_FAILED\n");
			exit(EXIT_CONNECTION_FAILED);
		}
		sendLen += sendRes;
	}
	lenBuffer = 0;
}

void lockMutex(){
#ifdef _WIN32
	WaitForSingleObject(ghMutex, INFINITE); 
#endif

}
void unlockMutex(){
#ifdef _WIN32
	ReleaseMutex(ghMutex);
#endif
}

void closeSocket(){
	close(socketDescriptor);
#ifdef _WIN32
	WSACleanup();
	CloseHandle(ghMutex);
#endif
}
