# -*- coding: utf-8 -*-
__doc__ = """
Add WebSocket support to the built-in WSGI server
provided by the :py:mod:`wsgiref`. This is clearly not
meant to be a production server so please consider this
only for testing purpose.

Mostly, this module overrides bits and pieces of
the built-in classes so that it supports the WebSocket
workflow.

.. code-block:: python

    from wsgiref.simple_server import make_server
    from ambari_ws4py.websocket import EchoWebSocket
    from ambari_ws4py.server.wsgirefserver import WSGIServer, WebSocketWSGIRequestHandler
    from ambari_ws4py.server.wsgiutils import WebSocketWSGIApplication

    server = make_server('', 9000, server_class=WSGIServer,
                         handler_class=WebSocketWSGIRequestHandler,
                         app=WebSocketWSGIApplication(handler_cls=EchoWebSocket))
    server.initialize_websockets_manager()
    server.serve_forever()

.. note::
   For some reason this server may fail against autobahntestsuite.
"""
import logging
import sys
import itertools
import operator
from wsgiref.handlers import SimpleHandler
from wsgiref.simple_server import WSGIRequestHandler, WSGIServer as _WSGIServer
from wsgiref import util

util._hoppish = {}.__contains__

from ambari_ws4py.manager import WebSocketManager
from ambari_ws4py import format_addresses
from ambari_ws4py.server.wsgiutils import WebSocketWSGIApplication
from ambari_ws4py.compat import get_connection

__all__ = ['WebSocketWSGIHandler', 'WebSocketWSGIRequestHandler',
           'WSGIServer']

logger = logging.getLogger('ambari_ws4py')

class WebSocketWSGIHandler(SimpleHandler):
    def setup_environ(self):
        """
        Setup the environ dictionary and add the
        `'ws4py.socket'` key. Its associated value
        is the real socket underlying socket.
        """
        SimpleHandler.setup_environ(self)
        self.environ['ws4py.socket'] = get_connection(self.environ['wsgi.input'])
        self.http_version = self.environ['SERVER_PROTOCOL'].rsplit('/')[-1]

    def finish_response(self):
        """
        Completes the response and performs the following tasks:

        - Remove the `'ws4py.socket'` and `'ws4py.websocket'`
          environ keys.
        - Attach the returned websocket, if any, to the WSGI server
          using its ``link_websocket_to_server`` method.
        """
        # force execution of the result iterator until first actual content
        rest = iter(self.result)
        first = list(itertools.islice(rest, 1))
        self.result = itertools.chain(first, rest)

        # now it's safe to look if environ was modified
        ws = None
        if self.environ:
            self.environ.pop('ws4py.socket', None)
            ws = self.environ.pop('ws4py.websocket', None)

        try:
            SimpleHandler.finish_response(self)
        except:
            if ws:
                ws.close(1011, reason='Something broke')
            raise
        else:
            if ws:
                self.request_handler.server.link_websocket_to_server(ws)

class WebSocketWSGIRequestHandler(WSGIRequestHandler):
    WebSocketWSGIHandler = WebSocketWSGIHandler
    def handle(self):
        """
        Unfortunately the base class forces us
        to override the whole method to actually provide our wsgi handler.
        """
        self.raw_requestline = self.rfile.readline()
        if not self.parse_request(): # An error code has been sent, just exit
            return

        # next line is where we'd have expect a configuration key somehow
        handler = self.WebSocketWSGIHandler(
            self.rfile, self.wfile, self.get_stderr(), self.get_environ()
        )
        handler.request_handler = self      # backpointer for logging
        handler.run(self.server.get_app())

class WSGIServer(_WSGIServer):
    def initialize_websockets_manager(self):
        """
        Call thos to start the underlying websockets
        manager. Make sure to call it once your server
        is created.
        """
        self.manager = WebSocketManager()
        self.manager.start()

    def shutdown_request(self, request):
        """
        The base class would close our socket
        if we didn't override it.
        """
        pass

    def link_websocket_to_server(self, ws):
        """
        Call this from your WSGI handler when a websocket
        has been created.
        """
        self.manager.add(ws)

    def server_close(self):
        """
        Properly initiate closing handshakes on
        all websockets when the WSGI server terminates.
        """
        if hasattr(self, 'manager'):
            self.manager.close_all()
            self.manager.stop()
            self.manager.join()
            delattr(self, 'manager')
        _WSGIServer.server_close(self)

if __name__ == '__main__':
    from ambari_ws4py import configure_logger
    configure_logger()

    from wsgiref.simple_server import make_server
    from ambari_ws4py.websocket import EchoWebSocket

    server = make_server('', 9000, server_class=WSGIServer,
                         handler_class=WebSocketWSGIRequestHandler,
                         app=WebSocketWSGIApplication(handler_cls=EchoWebSocket))
    server.initialize_websockets_manager()
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        server.server_close()
