#@+leo-ver=5-thin
#@+node:ekr.20101110091234.5700: * @file leo_interface.py
#@+<< docstring >>
#@+node:ekr.20101112180523.5422: ** << docstring >>
""" Allows the user to browse XML documents in Leo.

This file implements an interface to XML generation,
so that the resulting file can be processed by leo.

class file represents the whole leo file.
class leo_node has a headline and body text.

See the end of this file for a minimal example on
how to use these classes.

If you encounter the first of a set of clones, create a leo_node. If you
encounter the same set of clones later, create a leo_clone node and refer back
to the first element.

"""
#@-<< docstring >>
debug = False
# Define globals
vnode_count = 0
if debug:
    allvnodes = {file:None}
    vnode_count = 0
    vnode_stack = []
#@@language python
#@@tabwidth -4
#@+others
#@+node:ekr.20101110092416.5699: ** escape
def escape(s):
    s = s.replace('&', "&amp;")
    s = s.replace('<', "&lt;")
    s = s.replace('>', "&gt;")
    return s
#@+node:ekr.20101112045055.60165: ** init
def init():
    '''Return True if the plugin has loaded successfully.'''
    return True
#@+node:ekr.20101110092416.5700: ** class node_with_parent
class node_with_parent:

    #@+others
    #@+node:ekr.20101110092416.5702: *3* set_parent
    def set_parent(self, node):
        self.mparent = node
    #@+node:ekr.20101110092416.5704: *3* parent
    def parent(self):
        return self.mparent
    #@-others
#@+node:ekr.20101110092416.5705: ** class LeoNode
error_count = 0

class LeoNode:
    """
    Abstrace class for generating xml.
    """
    #@+others
    #@+node:ekr.20101110092416.5707: *3* __init__
    def __init__(self):
        self.children = []
    #@+node:ekr.20101110092416.5709: *3* add_child
    def add_child(self, child):
        self.children.append(child)
        child.set_parent(self)
    #@+node:ekr.20101110092416.5711: *3* gen
    def gen(self, file):
        pass
    #@+node:ekr.20101110092416.5713: *3* gen_children
    def gen_children(self, file):
        for child in self.children:
            child.gen(file)
    #@+node:ekr.20101110092416.5715: *3* mark
    def mark(self, file, marker, func, newline=True):
        file.write("<%s>" % marker)
        if newline:
            file.write("\n")
        func(file)
        file.write("</%s>\n" % marker)
    #@+node:ekr.20101110092416.5717: *3* mark_with_attributes
    def mark_with_attributes(self, file, marker, attribute_list, func, newline=True):
        write = file.write
        write("<")
        write(marker)
        write(" ")
        for name, value in attribute_list:
            write('%s="%s" ' % (name, value))
        write(">")
        if newline:
            write("\n")
        func(file)
        write("</%s>" % marker)
        if newline:
            write("\n")
    #@+node:ekr.20101110092416.5719: *3* mark_with_attributes_short
    def mark_with_attributes_short(self, file, marker, attribute_list):
        write = file.write
        write("<")
        write(marker)
        write(" ")
        for name, value in attribute_list:
            write('%s="%s" ' % (name, value))
        write("/>\n")
    #@+node:ekr.20101110092416.5721: *3* nthChild
    def nthChild (self, n):
        return self.children[n]
    #@-others
#@+node:ekr.20101110092416.5722: ** class leo_file
class leo_file(LeoNode):
    """Leo specific class representing a file."""

    #@+others
    #@+node:ekr.20101110092416.5724: *3* headString
    def headString(self):
        return "[[This is the file root]]"
    #@+node:ekr.20101110092416.5726: *3* parent
    def parent(self):
        return None
    #@+node:ekr.20101110092416.5728: *3* empty
    def empty(self, file):
        pass
    #@+node:ekr.20101110092416.5730: *3* find_panel_settings
    def find_panel_settings(self, file):
        self.mark(file, "find_string", self.empty, newline=False)
        self.mark(file, "change_string", self.empty, newline=False)
    #@+node:ekr.20101110092416.5732: *3* gen
    def gen(self, file):
        global error_count
        error_count = 0
        file.write('<?xml version="1.0" encoding="UTF-8"?>\n')
        self.mark(file, "leo_file", self.gen1)
    #@+node:ekr.20101110092416.5734: *3* gen1
    def gen1(self, file):
        self.header(file)

        # This is a shortcut.
        file.write("""<globals body_outline_ratio="0.5">
    <global_window_position top="0" left="2" height="400" width="700"/>
    <global_log_window_position top="0" left="0" height="0" width="0"/>
    </globals>
        """)
        self.mark(file, "preferences", self.preferences)
        self.mark(file, "find_panel_settings", self.find_panel_settings)
        if debug:
            global vnode_count
            vnode_count = 0
        self.mark(file, "vnodes", self.gen_vnodes)
        self.mark(file, "tnodes", self.gen_tnodes)
    #@+node:ekr.20101110092416.5736: *3* gen_tnodes
    def gen_tnodes(self, file):
        for child in self.children:
            child.gen_tnodes(file)
    #@+node:ekr.20101110092416.5738: *3* gen_vnodes
    def gen_vnodes(self, file):
        if debug:
            global allvnodes, vnode_stack
            allvnodes = {file:None}
            vnode_stack = []
        for child in self.children:
            child.gen_vnodes(file)
    #@+node:ekr.20101110092416.5740: *3* header
    def header(self, file):
        self.mark_with_attributes_short(file, "leo_header",
                                  (("file_format", "1"),
                                   ("tnodes", repr(self.nr_tnodes())),
                                   ("max_tnode_index", repr(self.max_tnode_index())),
                                   ("clone_windows", "0")))
    #@+node:ekr.20101110092416.5742: *3* max_tnode_index
    def max_tnode_index(self):
        return leo_node.count
    #@+node:ekr.20101110092416.5744: *3* nr_tnodes
    def nr_tnodes(self):
        return leo_node.count
    #@+node:ekr.20101110092416.5746: *3* preferences
    def preferences(self, file):
        pass
    #@+node:ekr.20101110092416.5748: *3* sss
    def sss(self, file):
        file.write("sss")
    #@-others
#@+node:ekr.20101110092416.5749: ** class leo_node
class leo_node(LeoNode, node_with_parent):
    """
    Leo specific class representing a node.

    These nodes correspond to tnodes in LEO.
    They have a headline and a body.

    They also represent the (only) vnode in an outline without clones.

    """
    __super_leo_node = LeoNode
    count = 0
    #@+others
    #@+node:ekr.20101110092416.5751: *3* __init__
    def __init__(self, headline='', body=''):
        self.__super_leo_node.__init__(self)
        leo_node.count += 1
        self.nr = leo_node.count
        self.headline =  headline
        self.body = body
    #@+node:ekr.20101110092416.5753: *3* bodyString
    def bodyString(self, body):
        return self.body
    #@+node:ekr.20101110092416.5755: *3* gen_tnodes
    def gen_tnodes(self, file):
        self.mark_with_attributes(file, "t", (
            ("tx", "T" + repr(self.nr)),
            ), self.gen_tnodes1, newline=False)
        for child in self.children:
            child.gen_tnodes(file)
    #@+node:ekr.20101110092416.5757: *3* gen_tnodes1
    def gen_tnodes1(self, file):
        self.write_body_escaped(file)
    #@+node:ekr.20101110092416.5759: *3* gen_vnodes
    def gen_vnodes(self, file):
        attributes = [("t", "T" + repr(self.nr))]
        if debug:
            # For debugging, make sure that we are not getting
            # cyclic references.
            # Also number all nodes for easier error hunting.
            vnode_stack.append(self)
            if self in allvnodes:
                print("Fix this; This is an endless recursive call in leo_interface.leo_node.gen_vnodes")
                x = vnode_stack[:]
                x.reverse()
                for i in x:
                    print(i.headline)
                import pdb; pdb.set_trace()
                return
            global vnode_count
            attributes.append(('model_node_number', repr(vnode_count)))
            vnode_count += 1
            allvnodes[self]=None
        self.mark_with_attributes(file, "v", attributes, self.gen_vnodes1)
        if debug:
            del allvnodes[self]
            vnode_stack.pop()
    #@+node:ekr.20101110092416.5761: *3* gen_vnodes1
    def gen_vnodes1(self, file):
        self.mark(file, "vh", self.write_headline_escaped, newline=False)
        for child in self.children:
            child.gen_vnodes(file)
    #@+node:ekr.20101110092416.5763: *3* headString
    def headString(self):
        return self.headline
    #@+node:ekr.20101110092416.5765: *3* set_body
    def set_body(self, body):
        self.body = body
    #@+node:ekr.20101110092416.5767: *3* set_headline
    def set_headline(self, headline):
        self.headline = headline
    #@+node:ekr.20101110092416.5769: *3* write_body_escaped
    def write_body_escaped(self, file):
        file.write(escape(self.body.encode("UTF-8")))

    #@+node:ekr.20101110092416.5771: *3* write_headline
    def write_headline(self, file):
        file.write(self.headline)
    #@+node:ekr.20101110092416.5773: *3* write_headline_escaped
    def write_headline_escaped(self, file):
        file.write(escape(self.headline.encode("UTF-8")))
    #@-others
#@+node:ekr.20101110092416.5774: ** class leo_clone
class leo_clone(node_with_parent):
    """
    Class representing a clone.

    The (only) data of a clone is the reference to a leo_node.

    When you encounter the first clone of a set of clones, generate
    a leo_node. The second clone should then reference this leo_node,
    and contain no other data.

    Since clones are indistinguishable, there is really not much to do in
    this class.
    """

    #@+others
    #@+node:ekr.20101110092416.5776: *3* __init__
    def __init__(self, orig):
        self.orig = orig
        self.mparent = None
    #@+node:ekr.20101110092416.5778: *3* gen_vnodes
    def gen_vnodes(self, file):
        self.orig.gen_vnodes(file)
        # There is nothing new to do here;
        # just repeat what we did when we encountered
        # the first clone.
    #@+node:ekr.20101110092416.5780: *3* gen_tnodes
    def gen_tnodes(self, file):
        pass
        # the tnodes are generated by the Leo_node

    #@-others
#@+node:ekr.20101110092416.5782: ** leotree
def leotree():
    f = leo_file()
    return f
#@-others

if __name__ == "__main__":
    import sys
    f = leotree()
    r = leo_node("Some headline", "some Body")
    f.add_child(r)
    f.gen(sys.stdout)
#@-leo
