# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012 NEC Corporation.  All rights reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
# @author: Ryota MIBU

import httplib
import json
import socket

from neutron.openstack.common import log as logging
from neutron.plugins.nec.common import exceptions as nexc


LOG = logging.getLogger(__name__)


class OFCClient(object):
    """A HTTP/HTTPS client for OFC Drivers."""

    def __init__(self, host="127.0.0.1", port=8888, use_ssl=False,
                 key_file=None, cert_file=None):
        """Creates a new client to some OFC.

        :param host: The host where service resides
        :param port: The port where service resides
        :param use_ssl: True to use SSL, False to use HTTP
        :param key_file: The SSL key file to use if use_ssl is true
        :param cert_file: The SSL cert file to use if use_ssl is true
        """
        self.host = host
        self.port = port
        self.use_ssl = use_ssl
        self.key_file = key_file
        self.cert_file = cert_file
        self.connection = None

    def get_connection(self):
        """Returns the proper connection."""
        if self.use_ssl:
            connection_type = httplib.HTTPSConnection
        else:
            connection_type = httplib.HTTPConnection

        # Open connection and send request, handling SSL certs
        certs = {'key_file': self.key_file, 'cert_file': self.cert_file}
        certs = dict((x, certs[x]) for x in certs if certs[x] is not None)
        if self.use_ssl and len(certs):
            conn = connection_type(self.host, self.port, **certs)
        else:
            conn = connection_type(self.host, self.port)
        return conn

    def _format_error_message(self, status, detail):
        detail = ' ' + detail if detail else ''
        return (_("Operation on OFC failed: %(status)s%(msg)s") %
                {'status': status, 'msg': detail})

    def do_request(self, method, action, body=None):
        LOG.debug(_("Client request: %(host)s:%(port)s "
                    "%(method)s %(action)s [%(body)s]"),
                  {'host': self.host, 'port': self.port,
                   'method': method, 'action': action, 'body': body})
        if type(body) is dict:
            body = json.dumps(body)
        try:
            conn = self.get_connection()
            headers = {"Content-Type": "application/json"}
            conn.request(method, action, body, headers)
            res = conn.getresponse()
            data = res.read()
            LOG.debug(_("OFC returns [%(status)s:%(data)s]"),
                      {'status': res.status,
                       'data': data})

            # Try to decode JSON data if possible.
            try:
                data = json.loads(data)
            except (ValueError, TypeError):
                pass

            if res.status in (httplib.OK,
                              httplib.CREATED,
                              httplib.ACCEPTED,
                              httplib.NO_CONTENT):
                return data
            else:
                LOG.warning(_("Operation on OFC failed: "
                              "status=%(status)s, detail=%(detail)s"),
                            {'status': res.status, 'detail': data})
                params = {'reason': _("Operation on OFC failed"),
                          'status': res.status}
                if isinstance(data, dict):
                    params['err_code'] = data.get('err_code')
                    params['err_msg'] = data.get('err_msg')
                else:
                    params['err_msg'] = data
                raise nexc.OFCException(**params)
        except (socket.error, IOError) as e:
            reason = _("Failed to connect OFC : %s") % e
            LOG.error(reason)
            raise nexc.OFCException(reason=reason)

    def get(self, action):
        return self.do_request("GET", action)

    def post(self, action, body=None):
        return self.do_request("POST", action, body=body)

    def put(self, action, body=None):
        return self.do_request("PUT", action, body=body)

    def delete(self, action):
        return self.do_request("DELETE", action)
