/*
 *  JsonRpc-Cpp - JSON-RPC implementation.
 *  Copyright (C) 2008-2012 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_handler.h
 * \brief JSON-RPC server processor engine.
 * \author Sebastien Vincent
 */

#ifndef JSONRPC_HANDLER_H
#define JSONRPC_HANDLER_H

#include <cstdio>

#include <string>
#include <list>

#include <json/json.h>

#include "jsonrpc_common.h"

namespace Json
{
  namespace Rpc
  {
    /**
     * \class CallbackMethod
     * \brief Abstract callback-style method.
     * \see RpcMethod
     */
    class CallbackMethod
    {
      public:
        /**
         * \brief Destructor.
         */
        virtual ~CallbackMethod();

        /**
         * \brief Call the method.
         * \param msg JSON-RPC request or notification
         * \param response response produced (may be Json::Value::null)
         * \return true if message has been correctly processed, false otherwise
         * \note Have to be implemented by subclasses
         */
        virtual bool Call(const Json::Value& msg, Json::Value& response) = 0;

        /**
         * \brief Get the name of the methods (optional).
         * \return name of the method as std::string
         * \note Have to be implemented by subclasses
         */
        virtual std::string GetName() const = 0;

        /**
         * \brief Get the description of the methods (optional).
         * \return description
         */
        virtual Json::Value GetDescription() const = 0;
    };

    /**
     * \class RpcMethod
     * \brief Template class that represent the RPC method.
     *
     * Typically RpcMethod object is created inside another class and use
     * a method of this class. See Handler class documentation for an example
     * of how to use RpcMethod class.
     *
     * \warning As class keep pointer of object reference, you should take
     * care at the lifetime of object you pass in RpcMethod constructor,
     * else it could lead to crash your program.
     */
    template<class T> class RpcMethod : public CallbackMethod
    {
      public:
        /**
         * \typedef Method
         * \brief T method signature.
         */
        typedef bool (T::*Method)(const Json::Value& msg,
            Json::Value& response);

        /**
         * \brief Constructor.
         * \param obj object
         * \param method class method
         * \param name symbolic name (i.e. system.describe)
         * \param description method description (in JSON format)
         */
        RpcMethod(T& obj, Method method, const std::string& name,
            const Json::Value description = Json::Value::null)
        {
          m_obj = &obj;
          m_name = name;
          m_method = method;
          m_description = description;
        }

        /**
         * \brief Call the method.
         * \param msg JSON-RPC request or notification
         * \param response response produced (may be Json::Value::null)
         * \return true if message has been correctly processed, false otherwise
         * \note JSON-RPC's notification method MUST set response to
         * Json::Value::null
         */
        virtual bool Call(const Json::Value& msg, Json::Value& response)
        {
          return (m_obj->*m_method)(msg, response);
        }

        /**
         * \brief Get the name of the methods (optional).
         * \return name of the method as std::string
         */
        virtual std::string GetName() const
        {
          return m_name;
        }

        /**
         * \brief Get the description of the methods (optional).
         * \return description
         */
        virtual Json::Value GetDescription() const
        {
          return m_description;
        }

      private:
        /**
         * \brief Copy constructor (private to avoid copy).
         * \param obj objec to copy
         */
        RpcMethod(const RpcMethod& obj);

         /**
         * \brief Operator copy assignment (redefined because of "resource"
         * class).
         * \param obj object to copy
         * \return object copied object reference
         */
        RpcMethod& operator=(const RpcMethod& obj);

        /**
         * \brief JSON reader.
         */
        Json::Reader m_reader;

        /**
         * \brief Object pointer.
         */
        T* m_obj;

        /**
         * \brief Method of T class.
         */
        Method m_method;

        /**
         * \brief Symbolic name.
         */
        std::string m_name;

        /**
         * \brief JSON-formated description of the RPC method.
         */
        Json::Value m_description;
    };

    /**
     * \class Handler
     * \brief Container of methods which can be called remotely.
     *
     * Preferred use of this class is to construct RpcMethod inside
     * another class and pass <code>*this</code> as obj parameter:\n
     * \n
     * \code
     * class MyClass
     * {
     *    public:
     *      void Init()
     *      {
     *        RpcMethod* method = new RpcMethod<MyClass>(*this, &MyClass::RemoteMethod, std::string("remote_method"),
     *          std::string("Description"));
     *        m_handler.AddMethod(method);
     *      }
     *
     *      bool RemoteMethod(const Json::Value& msg, Json::Value& response)
     *      {
     *        // do stuff
     *      }
     *
     *    private:
     *      Handler m_handler;
     * };
     * \endcode
     * \note Always pass it in function with reference
     * (i.e. void foo(Json::Rpc::Handler& handler)).
     * \see RpcMethod
     */
    class Handler
    {
      public:
        /**
         * \brief Constructor.
         */
        Handler();

        /**
         * \brief Destructor.
         */
        virtual ~Handler();

        /**
         * \brief Add a new RPC method.
         * \param method RPC method to add (MUST be dynamically allocated)
         * \note Json::Rpc::Handler object takes care of freeing method memory\n
         * The way of calling this method is:
         * <code>
         * handler.AddMethod(new RpcMethod<MyClass>(...));
         * </code>
         * \warning The "method" parameter MUST be dynamically allocated.
         */
        void AddMethod(CallbackMethod* method);

        /**
         * \brief Remote a RPC method.
         * \param name name of the RPC method
         */
        void DeleteMethod(const std::string& name);

        /**
         * \brief Process a JSON-RPC message.
         * \param msg JSON-RPC message as std::string
         * \param response JSON-RPC response (could be Json::Value::null)
         * \return true if the request has been correctly processed, false
         * otherwise (may be caused by parsed error, ...)
         * \note in case msg is a notification, response is equal to
         * Json::Value::null and the return value is true.
         */
        bool Process(const std::string& msg, Json::Value& response);

        /**
         * \brief Process a JSON-RPC message.
         * \param msg JSON-RPC message as C-String
         * \param response JSON-RPC response (could be Json::Value::null)
         * \return true if the request has been correctly processed, false
         * otherwise (may be caused by parsed error, ...)
         * \note in case msg is a notification, response is equal to
         * Json::Value::null and the return value is true.
         */
        bool Process(const char* msg, Json::Value& response);

        /**
         * \brief RPC method that get all the RPC methods and their description.
         * \param msg request
         * \param response response
         * \return true if processed correctly, false otherwise
         */
        bool SystemDescribe(const Json::Value& msg, Json::Value& response);

        /**
         * \brief Get a std::string representation of Json::Value.
         * \param value JSON message
         * \return string representation
         */
        std::string GetString(Json::Value value);

      private:
         /**
          * \brief Copy constructor (private to avoid copy).
          * \param obj object to copy
          */
        Handler(const Handler& obj);

        /**
         * \brief Operator copy assignment (redefined because of "resource"
         * class).
         * \param obj object to copy
         * \return object copied object reference
         */
        Handler& operator=(const Handler& obj);

        /**
         * \brief JSON reader.
         */
        Json::Reader m_reader;

        /**
         * \brief JSON writer.
         */
        Json::FastWriter m_writer;

        /**
         * \brief List of RPC methods.
         */
        std::list<CallbackMethod*> m_methods;

        /**
         * \brief Find CallbackMethod by name.
         * \param name name of the CallbackMethod
         * \return a CallbackMethod pointer if found, 0 otherwise
         */
        CallbackMethod* Lookup(const std::string& name) const;

        /**
         * \brief Check if the message is a valid JSON object one.
         * \param root message to check validity
         * \param error complete JSON-RPC error message if method failed
         * \return true if the message is a JSON one, false otherwise
         */
        bool Check(const Json::Value& root, Json::Value& error);

        /**
         * \brief Process a JSON-RPC object message.
         * \param root JSON-RPC message as Json::Value
         * \param response JSON-RPC response that will be filled in this method
         * \return true if request has been correctly processed, false otherwise
         * (may be caused by parsed error, ...)
         * \note In case msg is a notification, response is equal to
         * Json::Value::null and the return value is true.
         */
        bool Process(const Json::Value& root, Json::Value& response);
    };
  } /* namespace Rpc */
} /* namespace Json */

#endif /* JSONRPC_HANDLER_H */
