/*************************************************************************
 * libjson-rpc-cpp
 *************************************************************************
 * @file    windowstcpsocketserver.h
 * @date    17.07.2015
 * @author  Alexandre Poirot <alexandre.poirot@legrand.fr>
 * @license See attached LICENSE.txt
 ************************************************************************/

#ifndef JSONRPC_CPP_WINDOWSTCPSOCKETSERVERCONNECTOR_H_
#define JSONRPC_CPP_WINDOWSTCPSOCKETSERVERCONNECTOR_H_

#include <stdarg.h>
#include <stdint.h>
#include <winsock2.h>

#include "../abstractserverconnector.h"

namespace jsonrpc {
  /**
   * This class is the Windows implementation of TCPSocketServer.
   * It uses the Winsock2 socket API and Windows thread API to performs its job.
   * Each client request is handled in a new thread.
   */
  class WindowsTcpSocketServer : public AbstractServerConnector {
  public:
    /**
     * @brief WindowsTcpSocketServer, constructor of the Windows implementation of class TcpSocketServer
     * @param ipToBind The ipv4 address on which the server should bind and listen
     * @param port The port on which the server should bind and listen
     */
    WindowsTcpSocketServer(const std::string &ipToBind, const unsigned int &port);
    /**
     * @brief ~WindowsTcpSocketServer, the destructor of WindowsTcpSocketServer
     */
    ~WindowsTcpSocketServer();
    /**
     * @brief The real implementation TcpSocketServer::StartListening method.
     *
     * This method launches the listening loop that will handle client connections.
     * The return value depends on the current listening states :
     *  - not listening and no error come up while bind and listen returns true
     *  - not listening but error happen on bind or listen returns false
     *  - is called while listening returns false
     *
     * @return A boolean that indicates the success or the failure of the operation.
     */
    bool StartListening();
    /**
     * @brief The real implementation TcpSocketServer::StopListening method.
     *
     * This method stops the listening loop that will handle client connections.
     * The return value depends on the current listening states :
     *  - listening and successfully stops the listen loop returns true
     *  - is called while not listening returns false
     *
     * @return A boolean that indicates the success or the failure of the operation.
     */
    bool StopListening();

    /**
     * @brief The real implementation TcpSocketServer::SendResponse method.
     *
     * This method sends the result of the RPC Call over the tcp socket that the client has used to perform its request.
     * @param response The response to send to the client
     * @param addInfo Additionnal parameters (mainly client socket file descriptor)
     * @return A boolean that indicates the success or the failure of the operation.
     */
    bool SendResponse(const std::string &response, void *addInfo = NULL);

  protected:
    bool running;         /*!< A boolean that is used to know the listening state*/
    std::string ipToBind; /*!< The ipv4 address on which the server should bind and listen*/
    unsigned int port;    /*!< The port on which the server should bind and listen*/
    SOCKET socket_fd;     /*!< The file descriptior of the listening socket*/
    SOCKADDR_IN address;  /*!< The listening socket*/

    DWORD listenning_thread; /*!< The identifier of the listen loop thread*/

    /**
     * @brief The static method that is used as listening thread entry point
     * @param lp_data The parameters for the thread entry point method
     */
    static DWORD WINAPI LaunchLoop(LPVOID lp_data);
    /**
     * @brief The method that launches the listenning loop
     */
    void ListenLoop();
    struct GenerateResponseParameters {
      WindowsTcpSocketServer *instance;
      SOCKET connection_fd;
    }; /*!< The structure used to give parameters to the Response generating method*/
       /**
        * @brief The static method that is used as client request handling entry point
        * @param lp_data The parameters for the thread entry point method
        */
    static DWORD WINAPI GenerateResponse(LPVOID lp_data);
    /**
     * @brief A method that write a message to  socket
     *
     * Tries to send the full message.
     * @param fd The file descriptor of the socket message should be sent
     * @param toSend The message to send over socket
     * @returns A boolean indicating the success or the failure of the operation
     */
    bool WriteToSocket(const SOCKET &fd, const std::string &toSend);
    /**
     * @brief A method that wait for the client to close the tcp session
     *
     * This method wait for the client to close the tcp session in order to avoid the server to enter in TIME_WAIT status.
     * Entering in TIME_WAIT status with too many clients may occur in a DOS attack
     * since server will not be able to use a new socket when a new client connects.
     * @param fd The file descriptor of the socket that should be closed by the client
     * @param timeout The maximum time the server will wait for the client to close the tcp session in milliseconds.
     * @returns A boolean indicating the success or the failure of the operation
     */
    bool WaitClientClose(const SOCKET &fd, const int &timeout = 100);
    /**
     * @brief A method that close a socket by reseting it
     *
     * This method reset the tcp session in order to avoid enter in TIME_WAIT state.
     * @param fd The file descriptor of the socket that should be reset
     * @returns The return value of POSIX close() method
     */
    int CloseByReset(const SOCKET &fd);
    /**
     * @brief A method that cleanly close a socket by avoid TIME_WAIT state
     *
     * This method uses WaitClientClose and ClodeByReset to clenly close a tcp session with a client
     * (avoiding TIME_WAIT to avoid DOS attacks).
     * @param fd The file descriptor of the socket that should be cleanly closed
     * @returns The return value of POSIX close() method
     */
    int CleanClose(const SOCKET &fd);
  };

} /* namespace jsonrpc */
#endif /* JSONRPC_CPP_WINDOWSTCPSOCKETSERVERCONNECTOR_H_ */
