"""
Ported to Python 3.
"""

import os
from urllib.parse import quote as urlquote

from twisted.python.filepath import FilePath
from twisted.web.template import tags as T, Element, renderElement, XMLFile, renderer

from allmydata.util import base32
from allmydata.interfaces import IDirectoryNode, IFileNode, MDMF_VERSION
from allmydata.web.common import MultiFormatResource
from allmydata.mutable.common import UnrecoverableFileError # TODO: move


class MoreInfo(MultiFormatResource):
    """
    A ``Resource`` for describing more information about a node.

    :param node Node: The node to describe.
    """

    def __init__(self, node):
        super(MoreInfo, self).__init__()
        self.node = node

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

    render_INFO = render_HTML


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

    :param Node node: The node to describe.
    """

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

    def __init__(self, node):
        super(MoreInfoElement, self).__init__()
        self.original = node

    def abbrev(self, storage_index_or_none):
        if storage_index_or_none:
            return str(base32.b2a(storage_index_or_none)[:6], "ascii")
        return "LIT file"

    def get_type(self):
        node = self.original
        if IDirectoryNode.providedBy(node):
            if not node.is_mutable():
                return "immutable directory"
            return "directory"
        if IFileNode.providedBy(node):
            si = node.get_storage_index()
            if si:
                if node.is_mutable():
                    ret = "mutable file"
                    if node.get_version() == MDMF_VERSION:
                        ret += " (mdmf)"
                    else:
                        ret += " (sdmf)"
                    return ret
                return "immutable file"
            return "immutable LIT file"
        return "unknown"

    @renderer
    def title(self, req, tag):
        node = self.original
        si = node.get_storage_index()
        t = "More Info for %s" % self.get_type()
        if si:
            t += " (SI=%s)" % self.abbrev(si)
        return tag(t)

    @renderer
    def header(self, req, tag):
        return self.title(req, tag)

    @renderer
    def type(self, req, tag):
        return tag(self.get_type())

    @renderer
    def si(self, req, tag):
        si = self.original.get_storage_index()
        if not si:
            return "None"
        return tag(base32.b2a(si))

    @renderer
    def size(self, req, tag):
        node = self.original
        d = node.get_current_size()
        def _no_size(size):
            if size is None:
                return "?"
            return size
        d.addCallback(_no_size)
        def _handle_unrecoverable(f):
            f.trap(UnrecoverableFileError)
            return "?"
        d.addErrback(_handle_unrecoverable)
        d.addCallback(lambda size: tag(str(size)))
        return d

    @renderer
    def directory_writecap(self, req, tag):
        node = self.original
        if not IDirectoryNode.providedBy(node):
            return ""
        if node.is_readonly():
            return ""
        return tag(node.get_uri())

    @renderer
    def directory_readcap(self, req, tag):
        node = self.original
        if not IDirectoryNode.providedBy(node):
            return ""
        return tag(node.get_readonly_uri())

    @renderer
    def directory_verifycap(self, req, tag):
        node = self.original
        if not IDirectoryNode.providedBy(node):
            return ""
        verifier = node.get_verify_cap()
        if verifier:
            return tag(node.get_verify_cap().to_string())
        return ""

    @renderer
    def file_writecap(self, req, tag):
        node = self.original
        if IDirectoryNode.providedBy(node):
            node = node._node
        write_uri = node.get_write_uri()
        if not write_uri:
            return ""
        return tag(write_uri)

    @renderer
    def file_readcap(self, req, tag):
        node = self.original
        if IDirectoryNode.providedBy(node):
            node = node._node
        read_uri = node.get_readonly_uri()
        if not read_uri:
            return ""
        return tag(read_uri)

    @renderer
    def file_verifycap(self, req, tag):
        node = self.original
        if IDirectoryNode.providedBy(node):
            node = node._node
        verifier = node.get_verify_cap()
        if verifier:
            return tag(node.get_verify_cap().to_string())
        return ""

    def get_root(self, req):
        # the addSlash=True gives us one extra (empty) segment
        depth = len(req.prepath) + len(req.postpath) - 1
        link = "/".join([".."] * depth)
        return link

    @renderer
    def raw_link(self, req, tag):
        node = self.original
        if IDirectoryNode.providedBy(node):
            node = node._node
        elif IFileNode.providedBy(node):
            pass
        else:
            return ""
        root = self.get_root(req)
        quoted_uri = urlquote(node.get_uri())
        text_plain_url = "%s/file/%s/@@named=/raw.txt" % (root, quoted_uri)
        return T.li("Raw data as ", T.a("text/plain", href=text_plain_url))

    @renderer
    def is_checkable(self, req, tag):
        node = self.original
        si = node.get_storage_index()
        if si:
            return tag
        # don't show checker button for LIT files
        return ""

    @renderer
    def check_form(self, req, tag):
        node = self.original
        quoted_uri = urlquote(node.get_uri())
        target = self.get_root(req) + "/uri/" + quoted_uri
        if IDirectoryNode.providedBy(node):
            target += "/"
        check = T.form(action=target, method="post",
                       enctype="multipart/form-data")(
            T.fieldset(
            T.input(type="hidden", name="t", value="check"),
            T.input(type="hidden", name="return_to", value="."),
            T.legend("Check on this object", class_="freeform-form-label"),
            T.div(
            "Verify every bit? (EXPENSIVE):",
            T.input(type="checkbox", name="verify"),
            ),
            T.div("Repair any problems?: ",
                  T.input(type="checkbox", name="repair")),
            T.div("Add/renew lease on all shares?: ",
                  T.input(type="checkbox", name="add-lease")),
            T.div("Emit results in JSON format?: ",
                  T.input(type="checkbox", name="output", value="JSON")),

            T.input(type="submit", value="Check"),

            ))
        return tag(check)

    @renderer
    def is_mutable_file(self, req, tag):
        node = self.original
        if IDirectoryNode.providedBy(node):
            return ""
        if (IFileNode.providedBy(node)
            and node.is_mutable() and not node.is_readonly()):
            return tag
        return ""

    @renderer
    def overwrite_form(self, req, tag):
        node = self.original
        root = self.get_root(req)
        action = "%s/uri/%s" % (root, urlquote(node.get_uri()))
        done_url = "%s/uri/%s?t=info" % (root, urlquote(node.get_uri()))
        overwrite = T.form(action=action, method="post",
                           enctype="multipart/form-data")(
            T.fieldset(
            T.input(type="hidden", name="t", value="upload"),
            T.input(type='hidden', name='when_done', value=done_url),
            T.legend("Overwrite", class_="freeform-form-label"),
            "Upload new contents: ",
            T.input(type="file", name="file"),
            " ",
            T.input(type="submit", value="Replace Contents")
            ))
        return tag(overwrite)

    @renderer
    def is_directory(self, req, tag):
        node = self.original
        if IDirectoryNode.providedBy(node):
            return tag
        return ""

    @renderer
    def deep_check_form(self, req, tag):
        ophandle = base32.b2a(os.urandom(16))
        deep_check = T.form(action=req.path, method="post",
                            enctype="multipart/form-data")(
            T.fieldset(
            T.input(type="hidden", name="t", value="start-deep-check"),
            T.input(type="hidden", name="return_to", value="."),
            T.legend("Run a deep-check operation (EXPENSIVE)", class_="freeform-form-label"),
            T.div(
            "Verify every bit? (EVEN MORE EXPENSIVE):",
            T.input(type="checkbox", name="verify"),
            ),
            T.div("Repair any problems?: ",
                  T.input(type="checkbox", name="repair")),
            T.div("Add/renew lease on all shares?: ",
                  T.input(type="checkbox", name="add-lease")),
            T.div("Emit results in JSON format?: ",
                  T.input(type="checkbox", name="output", value="JSON")),

            T.input(type="hidden", name="ophandle", value=ophandle),
            T.input(type="submit", value="Deep-Check"),

            ))
        return tag(deep_check)

    @renderer
    def deep_size_form(self, req, tag):
        ophandle = base32.b2a(os.urandom(16))
        deep_size = T.form(action=req.path, method="post",
                            enctype="multipart/form-data")(
            T.fieldset(
            T.input(type="hidden", name="t", value="start-deep-size"),
            T.legend("Run a deep-size operation (EXPENSIVE)", class_="freeform-form-label"),
            T.input(type="hidden", name="ophandle", value=ophandle),
            T.input(type="submit", value="Deep-Size"),
            ))
        return tag(deep_size)

    @renderer
    def deep_stats_form(self, req, tag):
        ophandle = base32.b2a(os.urandom(16))
        deep_stats = T.form(action=req.path, method="post",
                            enctype="multipart/form-data")(
            T.fieldset(
            T.input(type="hidden", name="t", value="start-deep-stats"),
            T.legend("Run a deep-stats operation (EXPENSIVE)", class_="freeform-form-label"),
            T.input(type="hidden", name="ophandle", value=ophandle),
            T.input(type="submit", value="Deep-Stats"),
            ))
        return tag(deep_stats)

    @renderer
    def manifest_form(self, req, tag):
        ophandle = base32.b2a(os.urandom(16))
        manifest = T.form(action=req.path, method="post",
                            enctype="multipart/form-data")(
            T.fieldset(
            T.input(type="hidden", name="t", value="start-manifest"),
            T.legend("Run a manifest operation (EXPENSIVE)", class_="freeform-form-label"),
            T.div("Output Format: ",
                  T.select(name="output")
                  ( T.option("HTML", value="html", selected="true"),
                    T.option("text", value="text"),
                    T.option("JSON", value="json"),
                    ),
                  ),
            T.input(type="hidden", name="ophandle", value=ophandle),
            T.input(type="submit", value="Manifest"),
            ))
        return tag(manifest)


# TODO: edge metadata
