# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""
JSON-RPC protocol implementation.
http://www.jsonrpc.org/specification
"""

import json
import uuid


class JSONRPCObject(object):
    """
    Base object for JSON-RPC requests, responses,
    notifications and errors.
    """

    def __init__(self):
        return JSONRPCEncoder().default(self)

    def __str__(self, *args, **kwargs):
        return json.dumps(self, cls=JSONRPCEncoder)

    def __call__(self):
        return JSONRPCEncoder().default(self)


class JSONRPCEncoder(json.JSONEncoder):
    """
    Creates the JSON-RPC message.
    """

    def default(self, obj):
        """
        Returns a Python dictionary corresponding to a JSON-RPC message.
        """

        if isinstance(obj, JSONRPCObject):
            message = {"jsonrpc": 2.0}
            for field in dir(obj):
                if not field.startswith('_'):
                    value = getattr(obj, field)
                    message[field] = value
            return message
        return json.JSONEncoder.default(self, obj)


class JSONRPCInvalidRequest(JSONRPCObject):
    """
    Error response for an invalid request.
    """

    def __init__(self):
        JSONRPCObject.__init__(self)
        self.id = None
        self.error = {"code": -32600, "message": "Invalid Request"}


class JSONRPCMethodNotFound(JSONRPCObject):
    """
    Error response for an method not found.

    :param request_id: JSON-RPC identifier
    """

    def __init__(self, request_id):
        JSONRPCObject.__init__(self)
        self.id = request_id
        self.error = {"code": -32601, "message": "Method not found"}


class JSONRPCInvalidParams(JSONRPCObject):
    """
    Error response for invalid parameters.

    :param request_id: JSON-RPC identifier
    """

    def __init__(self, request_id):
        JSONRPCObject.__init__(self)
        self.id = request_id
        self.error = {"code": -32602, "message": "Invalid params"}


class JSONRPCInternalError(JSONRPCObject):
    """
    Error response for an internal error.

    :param request_id: JSON-RPC identifier (optional)
    """

    def __init__(self, request_id=None):
        JSONRPCObject.__init__(self)
        self.id = request_id
        self.error = {"code": -32603, "message": "Internal error"}


class JSONRPCParseError(JSONRPCObject):
    """
    Error response for parsing error.
    """

    def __init__(self):
        JSONRPCObject.__init__(self)
        self.id = None
        self.error = {"code": -32700, "message": "Parse error"}


class JSONRPCCustomError(JSONRPCObject):
    """
    Error response for an custom error.

    :param code: JSON-RPC error code
    :param message: JSON-RPC error message
    :param request_id: JSON-RPC identifier (optional)
    """

    def __init__(self, code, message, request_id=None):
        JSONRPCObject.__init__(self)
        self.id = request_id
        self.error = {"code": code, "message": message}


class JSONRPCResponse(JSONRPCObject):
    """
    JSON-RPC successful response.

    :param result: JSON-RPC result
    :param request_id: JSON-RPC identifier
    """

    def __init__(self, result, request_id):
        JSONRPCObject.__init__(self)
        self.id = request_id
        self.result = result


class JSONRPCRequest(JSONRPCObject):
    """
    JSON-RPC request.

    :param method: JSON-RPC destination method
    :param params: JSON-RPC params for the corresponding method (optional)
    :param request_id: JSON-RPC identifier (generated by default)
    """

    def __init__(self, method, params=None, request_id=None):
        JSONRPCObject.__init__(self)
        if request_id is None:
            request_id = str(uuid.uuid4())
        self.id = request_id
        self.method = method
        if params:
            self.params = params


class JSONRPCNotification(JSONRPCObject):
    """
    JSON-RPC notification.

    :param method: JSON-RPC destination method
    :param params: JSON-RPC params for the corresponding method (optional)
    """

    def __init__(self, method, params=None):
        JSONRPCObject.__init__(self)
        self.method = method
        if params:
            self.params = params
