
# 
#   Copyright 2017 RIFT.IO Inc
#
#   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.
#

import sys
import asyncio
from gi import require_version
require_version('RwTypes', '1.0')
require_version('RwsdnalYang', '1.0')
require_version('RwSdnYang', '1.0')

from gi.repository import (
        RwTypes,
        RwsdnalYang,
        RwSdnYang,
        )
import rw_peas

if sys.version_info < (3, 4, 4):
    asyncio.ensure_future = asyncio.async


class PluginLoadingError(Exception):
    pass


class SDNAccountCalError(Exception):
    pass


class SDNAccount(object):
    def __init__(self, log, rwlog_hdl, account_msg):
        self._log = log
        self._account_msg = account_msg.deep_copy()

        self._sdn_plugin = None
        self._engine = None

        self._sdn = self.plugin.get_interface("Topology")
        self._sdn.init(rwlog_hdl)

        self._status = RwsdnalYang.SDNAccount_ConnectionStatus(
                status="unknown",
                details="Connection status lookup not started"
                )

        self._validate_task = None

    @property
    def plugin(self):
        if self._sdn_plugin is None:
            try:
                self._sdn_plugin = rw_peas.PeasPlugin(
                        getattr(self._account_msg, self.account_type).plugin_name,
                        'RwSdn-1.0',
                        )

            except AttributeError as e:
                raise PluginLoadingError(str(e))

            self._engine, _, _ = self._sdn_plugin()

        return self._sdn_plugin

    def _wrap_status_fn(self, fn, *args, **kwargs):
        ret = fn(*args, **kwargs)
        rw_status = ret[0]
        if rw_status != RwTypes.RwStatus.SUCCESS:
            msg = "%s returned %s" % (fn.__name__, str(rw_status))
            self._log.error(msg)
            raise SDNAccountCalError(msg)

        # If there was only one other return value besides rw_status, then just
        # return that element.  Otherwise return the rest of the return values
        # as a list.
        return ret[1] if len(ret) == 2 else ret[1:]

    @property
    def sdn(self):
        return self._sdn

    @property
    def name(self):
        return self._account_msg.name

    @property
    def account_msg(self):
        return self._account_msg

    @property
    def sdnal_account_msg(self):
        return RwsdnalYang.SDNAccount.from_dict(
                self.account_msg.as_dict(),
                ignore_missing_keys=True,
                )

    def sdn_account_msg(self, account_dict):
        self._account_msg = RwSdnYang.SDNAccount.from_dict(account_dict)

    @property
    def account_type(self):
        return self._account_msg.account_type

    @property
    def connection_status(self):
        return self._status

    def update_from_cfg(self, cfg):
        self._log.debug("Updating parent SDN Account to %s", cfg)

        raise NotImplementedError("Update SDN account not yet supported")


    @asyncio.coroutine
    def validate_sdn_account_credentials(self, loop):
        self._log.debug("Validating SDN Account credentials %s", self._account_msg)
        self._status = RwSdnYang.SDNAccount_ConnectionStatus(
                status="validating",
                details="SDN account connection validation in progress"
                )
        rwstatus, status = yield from loop.run_in_executor(
                None,
                self._sdn.validate_sdn_creds,
                self.sdnal_account_msg,
                )
        if rwstatus == RwTypes.RwStatus.SUCCESS:
            self._status = RwSdnYang.SDNAccount_ConnectionStatus.from_dict(status.as_dict())
        else:
            self._status = RwSdnYang.SDNAccount_ConnectionStatus(
                    status="failure",
                    details="Error when calling SDNAL validate SDN creds"
                    )

        self._log.info("Got SDN account validation response: %s", self._status)

    def start_validate_credentials(self, loop):
        if self._validate_task is not None:
            self._validate_task.cancel()
            self._validate_task = None

        self._validate_task = asyncio.ensure_future(
                self.validate_sdn_account_credentials(loop),
                loop=loop
                )
