
import os, urllib

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 base32.b2a(storage_index_or_none)[:6]
        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 = urllib.quote(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 = urllib.quote(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, urllib.quote(node.get_uri()))
        done_url = "%s/uri/%s?t=info" % (root, urllib.quote(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
