# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.

from node import NodeVisitor, ValueNode, ListNode, BinaryExpressionNode
from parser import atoms, precedence

atom_names = {v:"@%s" % k for (k,v) in atoms.iteritems()}

named_escapes = set(["\a", "\b", "\f", "\n", "\r", "\t", "\v"])

def escape(string, extras=""):
    rv = ""
    for c in string:
        if c in named_escapes:
            rv += c.encode("unicode_escape")
        elif c == "\\":
            rv += "\\\\"
        elif c < '\x20':
            rv += "\\x%02x" % ord(c)
        elif c in extras:
            rv += "\\" + c
        else:
            rv += c
    return rv.encode("utf8")


class ManifestSerializer(NodeVisitor):
    def __init__(self, skip_empty_data=False):
        self.skip_empty_data = skip_empty_data

    def serialize(self, root):
        self.indent = 2
        rv = "\n".join(self.visit(root))
        if rv[-1] != "\n":
            rv = rv + "\n"
        return rv

    def visit_DataNode(self, node):
        rv = []
        if not self.skip_empty_data or node.children:
            if node.data:
                rv.append("[%s]" % escape(node.data, extras="]"))
                indent = self.indent * " "
            else:
                indent = ""

            for child in node.children:
                rv.extend("%s%s" % (indent if item else "", item) for item in self.visit(child))

            if node.parent:
                rv.append("")

        return rv

    def visit_KeyValueNode(self, node):
        rv = [escape(node.data, ":") + ":"]
        indent = " " * self.indent

        if len(node.children) == 1 and isinstance(node.children[0], (ValueNode, ListNode)):
            rv[0] += " %s" % self.visit(node.children[0])[0]
        else:
            for child in node.children:
                rv.append(indent + self.visit(child)[0])

        return rv

    def visit_ListNode(self, node):
        rv = ["["]
        rv.extend(", ".join(self.visit(child)[0] for child in node.children))
        rv.append("]")
        return ["".join(rv)]

    def visit_ValueNode(self, node):
        if "#" in node.data or (isinstance(node.parent, ListNode) and
                                ("," in node.data or "]" in node.data)):
            if "\"" in node.data:
                quote = "'"
            else:
                quote = "\""
        else:
            quote = ""
        return [quote + escape(node.data, extras=quote) + quote]

    def visit_AtomNode(self, node):
        return [atom_names[node.data]]

    def visit_ConditionalNode(self, node):
        return ["if %s: %s" % tuple(self.visit(item)[0] for item in node.children)]

    def visit_StringNode(self, node):
        rv = ["\"%s\"" % escape(node.data, extras="\"")]
        for child in node.children:
            rv[0] += self.visit(child)[0]
        return rv

    def visit_NumberNode(self, node):
        return [str(node.data)]

    def visit_VariableNode(self, node):
        rv = escape(node.data)
        for child in node.children:
            rv += self.visit(child)
        return [rv]

    def visit_IndexNode(self, node):
        assert len(node.children) == 1
        return ["[%s]" % self.visit(node.children[0])[0]]

    def visit_UnaryExpressionNode(self, node):
        children = []
        for child in node.children:
            child_str = self.visit(child)[0]
            if isinstance(child, BinaryExpressionNode):
                child_str = "(%s)" % child_str
            children.append(child_str)
        return [" ".join(children)]

    def visit_BinaryExpressionNode(self, node):
        assert len(node.children) == 3
        children = []
        for child_index in [1, 0, 2]:
            child = node.children[child_index]
            child_str = self.visit(child)[0]
            if (isinstance(child, BinaryExpressionNode) and
                precedence(node.children[0]) < precedence(child.children[0])):
                child_str = "(%s)" % child_str
            children.append(child_str)
        return [" ".join(children)]

    def visit_UnaryOperatorNode(self, node):
        return [str(node.data)]

    def visit_BinaryOperatorNode(self, node):
        return [str(node.data)]


def serialize(tree, *args, **kwargs):
    s = ManifestSerializer(*args, **kwargs)
    return s.serialize(tree)
