#!env python

import sys
import time
import platform
import logging
import asyncio
import signal
from autobahn.websocket.protocol import parseWsUrl
from autobahn.asyncio.wamp import protocol
from autobahn.asyncio.wamp import ApplicationSession
from autobahn.asyncio.websocket import WampWebSocketClientFactory
from autobahn.wamp.types import ComponentConfig
from autobahn.wamp import auth

class SessionBase(ApplicationSession):
    """Manages a session on the WAMP Router"""

    def __init__(self, realm_name):
        self.realm_name = realm_name
        self.loop = None
        self.connected = False
        self.session_is_open = False

        self.auth_id = None
        self.auth_secret = None

        cfg = ComponentConfig(self.realm_name, {})
        ApplicationSession.__init__(self, cfg)

        self.logger = logging.getLogger(name=self.__class__.__name__)

    def set_auth(self, authid, secret):
        self.auth_id = authid
        self.auth_secret = secret

    def start(self, url, loop=None, debug=False, debug_wamp=False):
        """
        Starts a WAMP session based on the arguments above.
        """
        self.logger.info('wamp_session: start')

        if loop is None:
            loop = asyncio.get_event_loop()
        self.loop = loop
        self.router_url = url

        isSecure, host, port, resource, path, params = parseWsUrl(url)

        transport_factory = WampWebSocketClientFactory(
            lambda: self, url=url, serializers=None,
            debug=debug, debug_wamp=debug_wamp)

        connection = loop.create_connection(
            transport_factory,
            host,
            port,
            ssl=isSecure)

        asyncio.async(connection)

        return loop

    def sigterm_handler(self, signum, frame):
        self.loop.stop()
        self.logger.info('Signal %s received. Loop stopped.' % signum)
        sys.exit(0)

    def bind_signals(self):
        signal.signal(signal.SIGTERM, self.sigterm_handler)
        signal.signal(signal.SIGINT, self.sigterm_handler)

    def run_loop(self):
        try:
            self.bind_signals()
        except Exception as ex:
            self.logger.warn('Exception while trying to bind signals: %s' % ex)
        return self.loop.run_forever()

    def create_server(self, factory, host, port):
        server = self.loop.create_server(factory, host, port)
        return server

    def run_server(self, server):
        server_runner = self.loop.run_until_complete(server)
        return self.loop.run_until_complete(server_runner.wait_closed())

    def onConnect(self):
        self.logger.info('onConnect: %s' % self.config.realm)
        if self.auth_id:
            self.join(self.config.realm, [u"wampcra"], self.auth_id)
        else:
            self.join(self.config.realm)
        self.logger.info('onConnect end')

    @asyncio.coroutine
    def onChallenge(self, challenge):
        self.logger.info('onChallenge: %s' % challenge)
        # yield from asyncio.sleep(1)
        if challenge.method == "wampcra":
            signature = auth.compute_wcs(self.auth_secret.encode('utf8'),
                challenge.extra['challenge'].encode('utf8'))
            return signature.decode('ascii')

    def on_packet(self, msg, details):
        """
        This is just a placeholder.
        Overwrite this method, please. :)
        """
        self.logger.info('Received message [%s] from topic [%s]' % (
            msg,
            details.topic
        ))

    @asyncio.coroutine
    def onJoin(self, details):
        self.logger.info('Joined the WAMP router.')
        self.session_is_open = True
        yield from self.on_join(details)  # NOQA

    @asyncio.coroutine
    def on_join(self, details):
        pass

    def publish(self, topic_name, msg):
        ApplicationSession.publish(self, topic_name, msg)
        self.logger.info('Published in %s: %s', topic_name, msg)

    def subscribe(self, callback, topic_name, options):
        opts = {'details_arg': 'details'}
        opts.update(options)

        yield from ApplicationSession.subscribe(  # NOQA
            self,
            callback,
            topic_name,
            protocol.types.SubscribeOptions(**opts))

    def onOpen(self, *args, **kwargs):
        self.connected = True
        self.logger.info(
            'wamp_session: connection opened. %s/%s' % (args, kwargs))
        ApplicationSession.onOpen(self, *args, **kwargs)

    def onClose(self, wasClean):
        if wasClean:
            self.logger.info('wamp_session: cleanly closed')
        else:
            self.logger.info('wamp_session: NOT cleanly closed')
        self.connected = False
        ApplicationSession.onClose(self, wasClean)

    def onDisconnect(self):
        self.logger.info('onDisconnect.')
        self.connected = False
        ApplicationSession.onDisconnect(self)

    def onLeave(self, details):
        self.logger.info('onLeave: %s' % details)
        self.session_is_open = False
        ApplicationSession.onLeave(self, details)

    def onUserError(self, *args, **kwargs):
        self.logger.info('onUserError: %s / %s' % (args, kwargs))
        ApplicationSession.onUserError(self, *args, **kwargs)
