#
# See LICENCE for detail
# Copyright (c) 2014 IRI
#

import json

from autobahn.twisted.websocket import WebSocketServerFactory, \
                                       WebSocketServerProtocol
from autobahn.websocket import http

from baseserver import BaseProtocol
import utils

class BroadcastServerProtocol(WebSocketServerProtocol):

    def onConnect(self, request):
        if request.params:
            self.factory.registerFilter(self, request.params)

    def onOpen(self):
        self.factory.register(self)

    def onMessage(self, payload, isBinary):
        if not isBinary:
            msg = "{} from {}".format(payload.decode('utf8'), self.peer)
            self.factory.broadcast(msg)

    def connectionLost(self, reason):
        WebSocketServerProtocol.connectionLost(self, reason)
        self.factory.unregister(self)


class BroadcastServerFactory(WebSocketServerFactory):
    """
    Simple broadcast server broadcasting any message it receives to all
    currently connected clients.
    """

    def __init__(self, url, debug = False, debugCodePaths = False):
        WebSocketServerFactory.__init__(self, url, debug = debug, debugCodePaths = debugCodePaths)
        self.clients = []
        self.filters = {}
        self.protocol = BroadcastServerProtocol

    def registerFilter(self, client, filter):
        print("registered filter {} for client {}".format(repr(filter),client.peer))
        self.filters[client] = filter

    def register(self, client):
        if not client in self.clients:
            print("registered client {}".format(client.peer))
            self.clients.append(client)

    def unregister(self, client):
        if client in self.clients:
            print("unregistered client {}".format(client.peer))
            self.clients.remove(client)
        if client in self.filters:
            self.filters.pop(client, None)

    def broadcast(self, msg, filter_list):
        print("broadcasting prepared message '{}' ..".format(msg))
        preparedMsg = self.prepareMessage(msg)
        for c in self.clients:
            if all([ (k in filter_list and filter_list[k] in v) for k,v in self.filters.get(c, {}).items()]):
                c.sendPreparedMessage(preparedMsg)
                print("prepared message sent to {}".format(c.peer))


class AnnotationServerProtocol(WebSocketServerProtocol, BaseProtocol):

    def onConnect(self, request):
        event_ids = request.params.get('event', [])
        if not event_ids or not event_ids[0]:
            raise http.HttpException(400, "The event parameter is missing")
        self.event = event_ids[0]
        self._init_props(self.factory.ws_factory, self.factory.conn)

    def clean_annot(self, annot):
        if 'ts' in annot:
            annot['ts'] = annot['ts'].isoformat()
        if 'uuid' in annot:
            annot['uuid'] = str(annot['uuid'])

    #TODO: add error handling
    def onMessage(self, payload, isBinary):
        if isBinary:
            return
        msg = payload.decode('utf8')
        params_annot = json.loads(msg)

        params = {
            'event_code': self.event,
            'channel' : utils.ANNOTATION_CHANNEL,
            'content' : params_annot
        }

        def error_callback(failure):
            res = params.copy()
            self.clean_annot(res)
            res['status'] = 'KO'
            res['failure'] = str(failure)
            self.sendMessage(json.dumps(res))

        def annot_callback(res):
            self.clean_annot(res)
            res['status'] = 'OK'
            self.sendMessage(json.dumps(res))


        defer = self.process_annotation(params)
        defer.addErrback(error_callback)
        defer.addCallback(annot_callback)


class AnotationServerFactory(WebSocketServerFactory):

    def __init__(self, url, ws_factory, conn, debug = False, debugCodePaths = False):
        WebSocketServerFactory.__init__(self, url, debug = debug, debugCodePaths = debugCodePaths)
        self.ws_factory = ws_factory
        self.conn = conn
        self.clients = {}
        self.protocol = AnnotationServerProtocol
