# -*- coding: utf-8 -*-
# Elisa - Home multimedia server
# Copyright (C) 2006-2008 Fluendo Embedded S.L. (www.fluendo.com).
# All rights reserved.
#
# This file is available under one of two license agreements.
#
# This file is licensed under the GPL version 3.
# See "LICENSE.GPL" in the root of this distribution including a special
# exception to use Elisa with Fluendo's plugins.
#
# The GPL part of Elisa is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Elisa" in the root directory of this distribution package
# for details on that license.

"""
Twisted based HTTP server
"""

__maintainer__ = "Philippe Normand <philippe@fluendo.com>"

from elisa.core import common
from elisa.base_components import service_provider
from elisa.core.utils import network

from twisted.internet import reactor
from twisted.internet.error import CannotListenError
from twisted.web import resource, server

plugin_registry = common.application.plugin_registry
HTTPResourceMessage = plugin_registry.get_component_class('httpd:httpresource_message')

class RootResource(resource.Resource):
    addSlash = True

    def render(self, request):
        return """
        <html>
        <head>
        <title>Elisa HTTP Server</title>
        </head>
        <body>
        <h1>Empty page</h1>
        </body>
        </html>
        """

    def getChild(self, path, request):
        if (path == ''):
            return self
        else:
            return self.children.get(path)

class SubResource(resource.Resource):
    addSlash = True
    def __init__(self, name):
        resource.Resource.__init__(self)
        self._name = name

    def render(self, request):
        return """
        <html>
        <head>
        <title>Elisa HTTP Server</title>
        </head>
        <body>
        <h1>%s Resource</h1>
        </body>
        </html>
        """ % self._name

    def getChild(self, path, request):
        return self.children.get(path)

class HttpServer(service_provider.ServiceProvider):
    """
    DOCME
    """

    default_config = {'port': '8000', 'interface': '' }
    config_doc = {'port': 'TCP port the server should listen on',
                  'interface': 'the hostname to bind to, defaults to "" (all)'}

    def initialize(self):
        service_provider.ServiceProvider.initialize(self)
        self._root = RootResource()
        self._ip_port = ()
        common.application.bus.register(self._bus_message_received,
                                        HTTPResourceMessage)

    def start(self):
        port = int(self.config.get('port'))
        self._interface = self.config.get('interface','')
        site = server.Site(self._root)

        iface = self._interface or 'all'
        self.info("Listening on %s:%s", iface, port)

        try:
            self._twisted_port = reactor.listenTCP(port, site,
                                                   interface=self._interface)
        except CannotListenError, error:
            self.warning(error)
        else:
            reactor.addSystemEventTrigger('before', 'shutdown', self.stop)

    def stop(self):
        self._twisted_port.stopListening()

    def _get_ip_port(self):
        if not self._ip_port:
            thost = self._twisted_port.getHost()
            ip = thost.host
            port = thost.port

            if ip == '0.0.0.0':
                ip = network.get_host_address()
            elif self._interface:
                ip = network.get_ip_address(self._interface)
            self._ip_port = (ip, port)

        return self._ip_port

    def _bus_message_received(self, msg, sender):
        path = msg.path
        rsrc = msg.resource
        parts = path.split('/')

        self.info("Adding web resource handler for: %r", path)

        # process resource path and lazily add SubResources where needed
        subs = parts[:-1]
        current = self._root
        while subs:
            sub = subs.pop(0)
            rs = current.getChild(sub, None)
            if not rs:
                rs = SubResource(sub)
                current.putChild(sub, rs)
            current = rs

        # give my ip/port to the resource so that it can build valid URLs
        # pointing to me
        rsrc.ip_port = self._get_ip_port()
        current.putChild(parts[-1], rsrc)
