"""Simple example comet server for educational purposes.

Requires Spawning and Eventlet

    http://pypi.python.org/pypi/eventlet/
    http://pypi.python.org/pypi/Spawning/

Run the comet server with:

	spawn spawningcomet.wsgi_application --threads=0

Then, to send an event to any browsers waiting, run this in another terminal:

	python spawningcomet.py hello world
"""
import struct
import sys
import uuid

from eventlet import api
from eventlet import coros


SEND_EVENT_INTERFACE = ''
SEND_EVENT_PORT = 4200


HTML_TEMPLATE = """<html>
    <head>
        <script type="text/javascript">
<!--
function make_request(event_id) {
    var req = new XMLHttpRequest();

    req.onreadystatechange = function() {
        if (req.readyState == 4) {
            var newdiv = document.createElement("div");
            newdiv.appendChild(document.createTextNode(req.responseText));
            document.getElementById("body").appendChild(newdiv);
            var next_event = req.getResponseHeader("X-Next-Event");
            if (next_event) {
                make_request(next_event);
            }
        }
    }
    req.open("GET", event_id);
    req.send(null);
}

make_request("%s");
-->
        </script>
    </head>
    <body id="body">
        <h1>Dynamic content will appear below</h1>
    </body>
</html>
"""

class Comet(object):
    def __init__(self):
        api.spawn(
            api.tcp_server,
            api.tcp_listener((SEND_EVENT_INTERFACE, SEND_EVENT_PORT)),
            self.read_events_forever)

        self.current_event = {'event': coros.event(), 'next': None}
        self.first_event_id = str(uuid.uuid1())
        self.events = {self.first_event_id: self.current_event}

    def read_events_forever(self, (sock, addr)):
        reader = sock.makefile('r')
        try:
            while True:
                ## Read the next event value out of the socket
                valuelen = reader.read(4)
                if not valuelen:
                    break

                valuelen, = struct.unpack('!L', valuelen)
                value = reader.read(valuelen)

                ## Make a new event and link the current event to it
                old_event = self.current_event
                old_event['next'] = str(uuid.uuid1())
                self.current_event = {
                    'event': coros.event(), 'next': None}
                self.events[old_event['next']] = self.current_event

                ## Send the event value to any waiting http requests
                old_event['event'].send(value)
        finally:
            reader.close()
            sock.close()

    def __call__(self, env, start_response):
        if env['REQUEST_METHOD'] != 'GET':
            start_response('405 Method Not Allowed', [('Content-type', 'text/plain')])
            return ['Method Not Allowed\n']

        if not env['PATH_INFO'] or env['PATH_INFO'] == '/':
            start_response('200 OK', [('Content-type', 'text/html')])
            return HTML_TEMPLATE % (self.first_event_id, )

        event = self.events.get(env['PATH_INFO'][1:], None)
        if event is None:
            start_response('404 Not Found', [('Content-type', 'text/plain')])
            return ['Not Found\n']

        value = event['event'].wait()
        start_response('200 OK', [
            ('Content-type', 'text/plain'),
            ('X-Next-Event', event['next'])])
        return [value, '\n']


def send_event(where, value):
    sock = api.connect_tcp(where)
    writer = sock.makefile('w')
    writer.write('%s%s' % (struct.pack('!L', len(value)), value))


if __name__ == '__main__':
    if len(sys.argv) > 1:
        value = ' '.join(sys.argv[1:])
    else:
        value = sys.stdin.read()
    send_event((SEND_EVENT_INTERFACE, SEND_EVENT_PORT), value)
else:
    wsgi_application = Comet()

