//---------------------------------------------------------------------------
#include "SocketPort.h"
//---------------------------------------------------------------------------
#if defined(_WIN32) || defined(_WINDOWS)
	#include <windows.h>
	//#include <minwinbase.h>
	#include <winnt.h>
	//#include <fileapi.h>
	#pragma comment(lib, "Ws2_32.lib")
	#pragma comment(lib, "Mswsock.lib")
	#pragma comment(lib, "AdvApi32.lib")
#else

#endif
//---------------------------------------------------------------------------
SocketPort::SocketPort()
{
	//id = 0;
    bOpen = false;
	my_sock=0;
}
//------------------------------------------------------------------------------
SocketPort::~SocketPort()
{
    //Close();
}
//------------------------------------------------------------------------------
//#define PORT 666
//#define SERVERADDR "*.*.*.*" //Звёздочками пометил свой IP
//------------------------------------------------------------------------------
bool SocketPort::Open(const char* IPAddress,int PortNo)
{
	if (bOpen)
		Close();

#if defined(_WIN32) || defined(_WINDOWS)
	WORD wVersionRequested;
    WSADATA wsaData;
    int err;
    //Шаг 1 - инициализация
    wVersionRequested = MAKEWORD(2, 2);
    err = WSAStartup(wVersionRequested, &wsaData);
    if(err)
    {
        printf("WSAStart error %d\n", WSAGetLastError());
		bOpen = false;
        return bOpen;
    }
 
    // Шаг 2 - создание сокета
    my_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (my_sock < 0)
    {
        printf("Socket() error %d\n", WSAGetLastError());
		bOpen = false;
		return bOpen;
    }
 
	//Отключаю буферизацию для маленьких пакетов, алгоритм Нейгла 200 миллисекунд
	DWORD value = 1;
	if (setsockopt(my_sock, IPPROTO_TCP, TCP_NODELAY, (char*)&value, sizeof(value)) != 0){
		printf("setsockopt() error %d\n", WSAGetLastError());
		bOpen = false;
		return bOpen;
	}

	//Задаю таймаут для чтения из сокета в миллисекундах те. *1000  (Ниже есть отдельная функция для этого)
	DWORD timeout = 1 * 1000;
	if(setsockopt(my_sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout))!=0)
	{
        printf("setsockopt() error %d\n", WSAGetLastError());
		bOpen = false;
		return bOpen;
	}

    // Шаг 3 - установка соединения
    // заполнение структуры sockaddr_in - указание адреса и порта сервера
    sockaddr_in dest_addr;
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(PortNo);
    HOSTENT *hst;
 
    // преобразование IP адреса из символьного в сетевой формат
    if (inet_addr(IPAddress) != INADDR_NONE)
        dest_addr.sin_addr.s_addr = inet_addr(IPAddress);
    else
    {
		//Новая функция на замен gethostbyname  
/*		struct addrinfo hints = { 0 }, * addrs;
		int err = getaddrinfo(IPAddress, PortNo, &hints, &addrs);
		if (err == 0)
		{
			// hst->h_addr_list содержит не массив адресов,
			// а массив указателей на адреса
			((unsigned long*)&dest_addr.sin_addr)[0] = ((unsigned long**)hst->h_addr_list)[0][0];

			freeaddrinfo(addrs);

		} else {
			printf("Invalid address %s\n", IPAddress);
			closesocket(my_sock);
			WSACleanup();
			bOpen = false;
			return bOpen;
		}*/

        // попытка получить IP адрес по доменному имени сервера
    	hst = gethostbyname(IPAddress);
        if (hst!=NULL)
            // hst->h_addr_list содержит не массив адресов,
            // а массив указателей на адреса
            ((unsigned long *)&dest_addr.sin_addr)[0] = ((unsigned long **)hst->h_addr_list)[0][0];
        else
        {
            printf("Invalid address %s\n", IPAddress);
            closesocket(my_sock);
            WSACleanup();
			bOpen = false;
			return bOpen;
        }

    }
 
    // адрес сервера получен - пытаемся установить соединение
    if (connect(my_sock, (sockaddr *)&dest_addr, sizeof(dest_addr)))
    {
        printf("Connect error %d\n", WSAGetLastError());
		bOpen = false;
		return bOpen;
    }
    //printf("Soedinenie s %s uspeshno ustanovlenno\n \ Type quit for quit\n\n", IPAddress);
	bOpen = true;
#else
    struct sockaddr_in server;

    //Create socket
    my_sock = socket(AF_INET , SOCK_STREAM , 0);
    if (my_sock == -1)
    {
        printf("Could not create socket");
    }
    puts("Socket created");

    server.sin_addr.s_addr = inet_addr(IPAddress);
    server.sin_family = AF_INET;
    server.sin_port = htons( PortNo );

    //Connect to remote server
    if (connect(my_sock , (struct sockaddr *)&server , sizeof(server)) < 0)
    {
        perror("connect failed. Error");
        return 1;
    }

    puts("Connected\n");
#endif
     return true;
}
//------------------------------------------------------------------------------
//Сколько ждать данных в миллисекундах при чтении данных
bool SocketPort::SetTimeout(unsigned long time)
{
#if defined(_WIN32) || defined(_WINDOWS)
	DWORD timeout = time;
	if(setsockopt(my_sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout))!=0)
	{
        return false;
	}
#else
	struct timeval tv;
	tv.tv_sec = time/1000;
	tv.tv_usec = (time%1000)*1000;
	int result=setsockopt(my_sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
	if(result!=0)
	{
		return false;
	}
	result=setsockopt(my_sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
	if(result!=0)
	{
		return false;
	}


#endif
	return true;
}
//------------------------------------------------------------------------------
bool SocketPort::Close()
{
#if defined(_WIN32) || defined(_WINDOWS)
    if (my_sock)
        closesocket(my_sock);
    //WSACleanup(); Закоментил так как сокеты могут использоваться во многих местах
#else
	shutdown(my_sock,2);
	close(my_sock);
#endif
	bOpen = false;
	return true;
}
//------------------------------------------------------------------------------
unsigned long SocketPort::Write(const void* lpBuffer,unsigned long nNumberOfBytesToWrite)
{
	return send(my_sock,(const char*)lpBuffer,nNumberOfBytesToWrite,0);
}
//------------------------------------------------------------------------------
unsigned char SocketPort::WriteChar(signed char ch)
{
	return (unsigned char)Write(&ch,1);
}
//------------------------------------------------------------------------------
unsigned char SocketPort::WriteUChar(unsigned char ch)
{
	return (unsigned char)Write(&ch,1);
}
//------------------------------------------------------------------------------
unsigned char SocketPort::WriteUInt(unsigned int val)
{
	return (unsigned char)Write(&val,4);
}
//------------------------------------------------------------------------------
//Для MQTT протокола
int SocketPort::writeUTF8(std::string str)
{
	if(str.length()>=65535) return 0;
	unsigned short size=str.length();
	int len=0;
	len+=WriteUChar(((uint8_t*)&size)[1]);
	len+=WriteUChar(((uint8_t*)&size)[0]);
	len+=Write(str.c_str(),str.length());
	return len;
}
//------------------------------------------------------------------------------
//Читать столько сколько за раз придёт
int SocketPort::Read(void* lpBuffer,unsigned long nNumberOfBytesToRead)
{
	if(nNumberOfBytesToRead<=0) return 0;
	return recv(my_sock,(char*)lpBuffer,nNumberOfBytesToRead,0); 
}
//------------------------------------------------------------------------------
//Попытаться и заполнить весь буфер
//Результат количество прочитанных байт
int SocketPort::ReadAll(void* lpBuffer,unsigned long nNumberOfBytesToRead)
{
	if(nNumberOfBytesToRead==0) return 0;
	int len;
	int pos=0;
	while(true){
		len=recv(my_sock,&((char*)lpBuffer)[pos],nNumberOfBytesToRead,0);
		if(len==-1) break;
		if(len==0) break;
		pos+=len;
		nNumberOfBytesToRead-=len;
		if(nNumberOfBytesToRead==0)
			break;
	}
	return pos;
}
//------------------------------------------------------------------------------
//Прочитать и изменить порядок байт
int SocketPort::ReadR(void* lpBuffer,unsigned long nNumberOfBytesToRead)
{
	if(nNumberOfBytesToRead==0) return 0;
	int result=recv(my_sock,(char*)lpBuffer,nNumberOfBytesToRead,0);
	if(result>1){
		char ch;
		int d=result/2;
		for(int i=0;i<d;i++){
			ch=((char*)lpBuffer)[i];
			((char*)lpBuffer)[i]=((char*)lpBuffer)[result-i-1];
			((char*)lpBuffer)[result-i-1]=ch;
		}
	}
	return result;
}
//------------------------------------------------------------------------------
