// Server part of Server-Client chat. Developed by Mr_Dezz

#include <iostream>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <stdio.h>
#include <vector>

#pragma comment(lib, "Ws2_32.lib") 

using namespace std;


int main(void)
{
    
    //Key constants
    const char IP_SERV[] = "127.0.0.1";          // Enter local Server IP address
    const int PORT_NUM = 5000;             // Enter Open working server port
    const short BUFF_SIZE = 1024;           // Maximum size of buffer for exchange info between server and client

    // Key variables for all program
    int erStat;                             // Keeps socket errors status

    //IP in string format to numeric format for socket functions. Data is in "ip_to_num"
    in_addr ip_to_num;
    erStat = inet_pton(AF_INET, IP_SERV, &ip_to_num);
    
    if (erStat <= 0) {
        cout << "Error in IP translation to special numeric format" << endl;
        return 1;
    }
    

    // WinSock initialization
    WSADATA wsData;
        
    erStat = WSAStartup(MAKEWORD(2,2), &wsData);
    
    if ( erStat != 0 ) {
        cout << "Error WinSock version initializaion #";
        cout << WSAGetLastError();
        return 1;
    }
    else
        cout << "WinSock initialization is OK" << endl;
    
    // Server socket initialization
    SOCKET ServSock = socket(AF_INET, SOCK_STREAM, 0);

    if (ServSock == INVALID_SOCKET) {
        cout << "Error initialization socket # " << WSAGetLastError() << endl; 
        closesocket(ServSock);
        WSACleanup();
        return 1;
    }
    else
        cout << "Server socket initialization is OK" << endl;

    // Server socket binding
    sockaddr_in servInfo;
    ZeroMemory(&servInfo, sizeof(servInfo));    // Initializing servInfo structure
                
    servInfo.sin_family = AF_INET;
    servInfo.sin_addr = ip_to_num;  
    servInfo.sin_port = htons(PORT_NUM);

    erStat = bind(ServSock, (sockaddr*)&servInfo, sizeof(servInfo));

    if ( erStat != 0 ) {
        cout << "Error Socket binding to server info. Error # " << WSAGetLastError() << endl;
        closesocket(ServSock);
        WSACleanup();
        return 1;
    }
    else 
        cout << "Binding socket to Server info is OK" << endl;
    
    //Starting to listen to any Clients
    erStat = listen(ServSock, SOMAXCONN);

    if ( erStat != 0 ) {
        cout << "Can't start to listen to. Error # " << WSAGetLastError() << endl;
        closesocket(ServSock);
        WSACleanup();
        return 1;
    }
    else {
        cout << "Listening..." << endl;
    }

    //Client socket creation and acception in case of connection
    sockaddr_in clientInfo; 
    ZeroMemory(&clientInfo, sizeof(clientInfo));    // Initializing clientInfo structure

    int clientInfo_size = sizeof(clientInfo);

    SOCKET ClientConn = accept(ServSock, (sockaddr*)&clientInfo, &clientInfo_size);

    if (ClientConn == INVALID_SOCKET) {
        cout << "Client detected, but can't connect to a client. Error # " << WSAGetLastError() << endl;
        closesocket(ServSock);
        closesocket(ClientConn);
        WSACleanup();
        return 1;
    }
    else {
        cout << "Connection to a client established successfully" << endl;
        char clientIP[22];

        inet_ntop(AF_INET, &clientInfo.sin_addr, clientIP, INET_ADDRSTRLEN);    // Convert connected client's IP to standard string format

        cout << "Client connected with IP address " << clientIP << endl;

    }

    //Exchange text data between Server and Client. Disconnection if a client send "xxx"

    vector <char> servBuff(BUFF_SIZE), clientBuff(BUFF_SIZE);                           // Creation of buffers for sending and receiving data
    short packet_size = 0;                                              // The size of sending / receiving packet in bytes
    
    while (true) {
        packet_size = recv(ClientConn, servBuff.data(), servBuff.size(), 0);                    // Receiving packet from client. Program is waiting (system pause) until receive
        cout << "Client's message: " << servBuff.data() << endl; 

        cout << "Your (host) message: ";
        fgets(clientBuff.data(), clientBuff.size(), stdin);

        // Check whether server would like to stop chatting 
        if (clientBuff[0] == 'x' && clientBuff[1] == 'x' && clientBuff[2] == 'x') {
            shutdown(ClientConn, SD_BOTH);
            closesocket(ServSock);
            closesocket(ClientConn);
            WSACleanup();
            return 0;
        }

        packet_size = send(ClientConn, clientBuff.data(), clientBuff.size(), 0);

        if (packet_size == SOCKET_ERROR) {
            cout << "Can't send message to Client. Error # " << WSAGetLastError() << endl;
            closesocket(ServSock);
            closesocket(ClientConn);
            WSACleanup();
            return 1;
        }

    }

    closesocket(ServSock);
    closesocket(ClientConn);
    WSACleanup();

    return 0;

}
