#
# Copyright 2018 the original author or authors.
#
# 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.
#

"""
Agent to play gateway between adapters.
"""

from __future__ import absolute_import
import structlog
from uuid import uuid4
from twisted.internet.defer import inlineCallbacks, returnValue
from .container_proxy import ContainerProxy
from voltha_protos.inter_container_pb2 import InterAdapterHeader, \
    InterAdapterMessage
import time
import codecs
import six

log = structlog.get_logger()


class AdapterProxy(ContainerProxy):

    def __init__(self, kafka_proxy, core_topic, my_listening_topic):
        super(AdapterProxy, self).__init__(kafka_proxy,
                                           core_topic,
                                           my_listening_topic)

    def _to_string(self, unicode_str):
        if unicode_str is not None:
            if isinstance(unicode_str, six.string_types):
                return unicode_str
            else:
                return codecs.encode(unicode_str, 'ascii')
        else:
            return None

    @ContainerProxy.wrap_request(None)
    @inlineCallbacks
    def send_inter_adapter_message(self,
                                   msg,
                                   type,
                                   from_adapter,
                                   to_adapter,
                                   to_device_id=None,
                                   proxy_device_id=None,
                                   message_id=None):
        """
        Sends a message directly to an adapter. This is typically used to send
        proxied messages from one adapter to another.  An initial ACK response
        is sent back to the invoking adapter.  If there is subsequent response
        to be sent back (async) then the adapter receiving this request will
        use this same API to send back the async response.
        :param msg : GRPC message to send
        :param type : InterAdapterMessageType of the message to send
        :param from_adapter: Name of the adapter making the request.
        :param to_adapter: Name of the remote adapter.
        :param to_device_id: The ID of the device for to the message is
        intended. if it's None then the message is not intended to a specific
        device.  Its interpretation is adapter specific.
        :param proxy_device_id: The ID of the device which will proxy that
        message. If it's None then there is no specific device to proxy the
        message.  Its interpretation is adapter specific.
        :param message_id: A unique number for this transaction that the
        adapter may use to correlate a request and an async response.
        """

        try:
            # validate params
            assert msg
            assert from_adapter
            assert to_adapter

            # Build the inter adapter message
            h = InterAdapterHeader()
            h.type = type
            h.from_topic = self._to_string(from_adapter)
            h.to_topic = self._to_string(to_adapter)
            h.to_device_id = self._to_string(to_device_id)
            h.proxy_device_id = self._to_string(proxy_device_id)

            if message_id:
                h.id = self._to_string(message_id)
            else:
                h.id = uuid4().hex

            h.timestamp = int(round(time.time() * 1000))
            iaMsg = InterAdapterMessage()
            iaMsg.header.CopyFrom(h)
            iaMsg.body.Pack(msg)

            log.debug("sending-inter-adapter-message", type=iaMsg.header.type, from_topic=iaMsg.header.from_topic,
                  to_topic=iaMsg.header.to_topic, to_device_id=iaMsg.header.to_device_id)
            res = yield self.invoke(rpc="process_inter_adapter_message",
                                    to_topic=iaMsg.header.to_topic,
                                    msg=iaMsg)
            returnValue(res)
        except Exception as e:
            log.exception("error-sending-request", e=e)
