"""
Ported to Python 3.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

from future.utils import PY2
if PY2:
    from future.builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, str, max, min  # noqa: F401

import time, os
from pkg_resources import resource_filename
from twisted.web.template import Element, XMLFile, renderElement, renderer
from twisted.python.filepath import FilePath
from twisted.web import static
import allmydata
from allmydata.util import idlib, jsonbytes as json
from allmydata.web.common import (
    render_time,
    MultiFormatResource,
    SlotsSequenceElement,
)


class IntroducerRoot(MultiFormatResource):
    """
    A ``Resource`` intended as the root resource for introducers.

    :param _IntroducerNode introducer_node: The introducer node to template
        information about.
    """

    def __init__(self, introducer_node):
        super(IntroducerRoot, self).__init__()
        self.introducer_node = introducer_node
        self.introducer_service = introducer_node.getServiceNamed("introducer")
        # necessary as a root Resource
        self.putChild(b"", self)
        static_dir = resource_filename("allmydata.web", "static")
        for filen in os.listdir(static_dir):
            self.putChild(filen.encode("utf-8"), static.File(os.path.join(static_dir, filen)))

    def _create_element(self):
        """
        Create a ``IntroducerRootElement`` which can be flattened into an HTML
        response.
        """
        return IntroducerRootElement(
            self.introducer_node, self.introducer_service)

    def render_HTML(self, req):
        """
        Render an HTML template describing this introducer node.
        """
        return renderElement(req, self._create_element())

    def render_JSON(self, req):
        """
        Render JSON describing this introducer node.
        """
        res = {}

        counts = {}
        for s in self.introducer_service.get_subscribers():
            if s.service_name not in counts:
                counts[s.service_name] = 0
            counts[s.service_name] += 1
        res[u"subscription_summary"] = counts

        announcement_summary = {}
        for ad in self.introducer_service.get_announcements():
            service_name = ad.service_name
            if service_name not in announcement_summary:
                announcement_summary[service_name] = 0
            announcement_summary[service_name] += 1
        res[u"announcement_summary"] = announcement_summary

        return (json.dumps(res, indent=1) + "\n").encode("utf-8")


class IntroducerRootElement(Element):
    """
    An ``Element`` HTML template which can be flattened to describe this
    introducer node.

    :param _IntroducerNode introducer_node: The introducer node to describe.
    :param IntroducerService introducer_service: The introducer service created
        by the node.
    """

    loader = XMLFile(FilePath(__file__).sibling("introducer.xhtml"))

    def __init__(self, introducer_node, introducer_service):
        super(IntroducerRootElement, self).__init__()
        self.introducer_node = introducer_node
        self.introducer_service = introducer_service
        self.node_data_dict = {
            "my_nodeid": idlib.nodeid_b2a(self.introducer_node.nodeid),
            "version": allmydata.__full_version__,
            "import_path": str(allmydata).replace("/", "/ "),  # XXX kludge for wrapping
            "rendered_at": render_time(time.time()),
        }

    @renderer
    def node_data(self, req, tag):
        return tag.fillSlots(**self.node_data_dict)

    @renderer
    def announcement_summary(self, req, tag):
        services = {}
        for ad in self.introducer_service.get_announcements():
            if ad.service_name not in services:
                services[ad.service_name] = 0
            services[ad.service_name] += 1
        service_names = list(services.keys())
        service_names.sort()
        return u", ".join(u"{}: {}".format(service_name, services[service_name])
                          for service_name in service_names)

    @renderer
    def client_summary(self, req, tag):
        counts = {}
        for s in self.introducer_service.get_subscribers():
            if s.service_name not in counts:
                counts[s.service_name] = 0
            counts[s.service_name] += 1
        return u", ".join(u"{}: {}".format(name, counts[name])
                          for name in sorted(counts.keys()))

    @renderer
    def services(self, req, tag):
        services = self.introducer_service.get_announcements()
        services.sort(key=lambda ad: (ad.service_name, ad.nickname))
        services = [{
            "serverid": ad.serverid,
            "nickname": ad.nickname,
            "connection-hints":
                u"connection hints: " + u" ".join(ad.connection_hints),
            "connected": u"?",
            "announced": render_time(ad.when),
            "version": ad.version,
            "service_name": ad.service_name,
        } for ad in services]
        return SlotsSequenceElement(tag, services)

    @renderer
    def subscribers(self, req, tag):
        subscribers = [{
            "nickname": s.nickname,
            "tubid": s.tubid,
            "connected": s.remote_address,
            "since": render_time(s.when),
            "version": s.version,
            "service_name": s.service_name,
        } for s in self.introducer_service.get_subscribers()]
        return SlotsSequenceElement(tag, subscribers)
