/*
 *  JsonRpc-Cpp - JSON-RPC implementation.
 *  Copyright (C) 2008-2011 Sebastien Vincent <sebastien.vincent@cppextrem.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

/**
 * \file jsonrpc_httpclient.h
 * \brief JSON-RPC HTTP client.
 * \author Brian Panneton
 */

#ifndef JSONRPC_HTTPCLIENT_H 
#define JSONRPC_HTTPCLIENT_H 

#include <iostream>
#include <curl/curl.h>

#include "jsonrpc_client.h"
#include "system.h" 

namespace Json
{
  namespace Rpc
  {
    /**
     * \class HttpClient
     * \brief JSON-RPC Http client.
     */
    class HttpClient : public Client
    {
      public:
        /**
         * \brief Constructor without address or port.
         * \note Make sure you set them yourself.
         */
        HttpClient();
       
        /**
         * \brief Destructor.
         */
        virtual ~HttpClient();

        /**
         * \brief Constructor.
         * \param address address URL
         * \note Make sure you either set the port or include it
         * in the string. If you do both, the one set by ChangePort()
         * will override.
         */
        HttpClient(const std::string& address);
 
        /**
         * \brief Constructor.
         * \param address remote network address
         * \param port remote local port
         * \note Port will override whatever is set in the address string.
         */
        HttpClient(const std::string& address, uint16_t port);

        /**
         * \brief Provide the option to change the address.
         * \param address string containing the address (include the port here)
         * \note ChangePort() will override the port added in address.
         */
        void ChangeAddress(const std::string& address);
        
        /**
         * \brief Provide the option to change the port, this will override the
         * port set in ChangeAddress.
         * \param port number
         */
        void ChangePort(uint16_t port);

        /**
         * \brief Receive last string received from server.
         *
         * Strings received from server are placed in a linked list (FIFO)
         * which Recv pops from the front. 'data' will be null if there
         * are not strings left.
         * \param data if data is received it will put in this reference
         * \return length of string
         */
        ssize_t Recv(std::string& data);

        /**
         * \brief Receives next string from server, will wait for data.
         * \param data data received is put in this reference
         * \return length of string
         */
        ssize_t WaitRecv(std::string& data);

        /**
         * \brief Receives next string from server, will wait for data for max
         * timeout seconds.
         * \param data data received is put in this reference
         * \param timeout timeout for receive operation
         * \return length of string
         */
        ssize_t WaitRecv(std::string& data, unsigned int timeout);

        /**
         * \brief Sets the size of the receive string list.
         *
         * If the list exceeds this size, a strings will be popped off of the
         * front until it is of correct size; Default size: -1 == unlimited.
         * \param size number of strings to keep in the list
         */
        void SetRecvListSize(int size);

        /**
         * \brief Gets the size of the receive string list. 
         * \return size of the receive string list (-1 == unlimited)
         */
        int GetRecvListSize();

        /**
         * \brief Send data.
         * \param data data to send
         * \return 0 if successful or libcurl error code if error
         */
        ssize_t Send(const std::string& data);

        /**
         * \brief Connect to the remote machine.
         * \return true if success, false otherwise
         * \note This method is not needed in HTTP and always returns false.
         */
        bool Connect();

        /**
         * \brief Close socket.
         * \note This method is useless in HTTP and does nothing.
         */
        void Close();

        /**
         * \brief Get socket descriptor.
         * \return socket descriptor.
         * \note This method is useless in HTTP and always returns -1.
         */
        int GetSocket() const;

      private:   
        /**
         * \brief Receive data from the network.
         *
         * This is a static function that is the callback when receiving data
         * from the network. It calls Recv providing it with 'this'.
         * \param buffer Non-null terminated data that it received back
         * \param size size_t count of how many nmemb make up buffer
         * \param nmemb size_t multiply by size to get full size of buffer
         * \param f pointer to user data (in our case it is out class instance)
         * \return the size of the data received
         */
        static size_t StaticRecv(void *buffer, size_t size, size_t nmemb, 
                                 void*f);

        /**
         * \brief Stores the last received json string from the server
         * This allows the last json string that the server sent
         * to the static callback to be stored within the class.
         * The next json string from the server will overwrite this.
         * \param data the string represention of the received json
         * \note Any server errors will also be stored here.
         */
        void StoreRecv(const std::string& data);

        /**
         * \brief Initializes the class instance
         */
        void Initialize();

        /**
         * \brief CURL handle.
         */
        CURL *m_curlHandle;

        /**
         * \brief List of CURL headers.
         */
        struct curl_slist *m_headers;

        /**
         * \brief List of received JSON-RPC response.
         */
        std::list<std::string> m_lastReceivedList;

        /**
         * \brief Size of the m_lastReceivedList list.
         */
        int m_lastReceivedListSize;

        /**
         * \brief Mutex to protected m_lastReceivedList.
         */
        system_util::Mutex m_mutex;
    };
  } /* namespace Rpc */
} /* namespace Json */

#endif /* JSONRPC_HTTPCLIENT_H */

