#!/usr/bin/python3

import itertools
import sys

import vistrails.core.application
from vistrails.core.api import Package
from vistrails.core.modules.module_registry import get_module_registry

heading_order = ['*', '=', '^', '-', '"', '+', '#']

def trim_docstring(docstring):
    """Copied from PEP 257: http://www.python.org/dev/peps/pep-0257/"""
    if not docstring:
        return ''
    # Convert tabs to spaces (following the normal Python rules)
    # and split into a list of lines:
    lines = docstring.expandtabs().splitlines()
    # Determine minimum indentation (first line doesn't count):
    indent = sys.maxsize
    for line in lines[1:]:
        stripped = line.lstrip()
        if stripped:
            indent = min(indent, len(line) - len(stripped))
    # Remove indentation (first line is special):
    trimmed = [lines[0].strip()]
    if indent < sys.maxsize:
        for line in lines[1:]:
            trimmed.append(line[indent:].rstrip())
    # Strip off trailing and leading blank lines:
    while trimmed and not trimmed[-1]:
        trimmed.pop()
    while trimmed and not trimmed[0]:
        trimmed.pop(0)
    # Return a single string:
    return '\n'.join(trimmed)

def indent_docstring(docstring, indent=0):
    new_docstring = ""
    for line in docstring.splitlines():
        new_docstring += (" " * indent) + line + "\n"
    return new_docstring

def format_docstring(docstring, indent=0):
    return indent_docstring(trim_docstring(docstring), indent)

def get_examples(desc):
    return []

def generate_module_doc(desc, f=None, depth=1, inherited=True):
    reg = get_module_registry()
    print(desc.name, file=f)
    print(heading_order[depth] * len(desc.name), file=f)
    print("", file=f)
    print(".. py:class:: %s" % desc.name, file=f)
    print("", file=f)
    if desc.module.__doc__:
        print(format_docstring(desc.module.__doc__, 2), file=f)
        print("", file=f)

    if inherited:
        input_ports = reg.module_destination_ports_from_descriptor(True, desc)
        output_ports = reg.module_source_ports_from_descriptor(True, desc)
    else:
        input_ports = list(reg.module_ports('input', desc).values())
        input_ports.sort(key=lambda x: (x.sort_key, x.id))
        output_ports = list(reg.module_ports('output', desc).values())
        output_ports.sort(key=lambda x: (x.sort_key, x.id))
        
    if len(input_ports) > 0:
        print("  *Input Ports*", file=f)
        for port in input_ports:
            sigstring = port.sigstring.replace(':', '.')[1:-1]
            print("    .. py:attribute:: %s" % port.name, file=f)
            print("", file=f)            
            print("      | *Signature*: :py:class:`%s`" % sigstring, file=f)
            if port.docstring():
                print("      | *Description*: %s" % format_docstring(port.docstring(), 9), file=f)
            print("", file=f)
    if len(output_ports) > 0:
        print("  *Output Ports*", file=f)
        for port in output_ports:
            sigstring = port.sigstring.replace(':', '.')[1:-1]
            print("    .. py:attribute:: %s" % port.name, file=f)
            print("", file=f)            
            print("      | *Signature*: :py:class:`%s`" % sigstring, file=f)
            if port.docstring():
                print("      | *Description*: %s" % format_docstring(port.docstring(), 9), file=f)
            print("", file=f)

    examples = get_examples(desc)
    if len(examples) > 0:
        print("  *Examples*", file=f)
        for example in examples:
            print("   * %s" % example, file=f)
        print("", file=f)

def generate_docs(pkg, namespace=None, f=None):
    print(pkg._package.name, file=f)
    print(heading_order[0] * len(pkg._package.name), file=f)
    print("", file=f)
    print(".. py:module:: %s\n" % pkg._package.identifier, file=f)
    print('| *Identifier*: %s' % pkg._package.identifier, file=f)
    print('| *Version*: %s\n' % pkg._package.version, file=f)
    print(format_docstring(pkg._package.description, 0), file=f)
    print("", file=f)
    
    if namespace == '':
        for desc in pkg._namespaces[1]:
            generate_module_doc(desc, f, 1)
    else:
        if namespace is None:
            namespace = ''
            for desc in pkg._namespaces[1]:
                generate_module_doc(desc, f)
            namespaces = sorted(item + (1,) for item in 
                                pkg._namespaces[0].items())
        else:
            namespace_dict = pkg._namespaces[0]
            descs = pkg._namespaces[1]
            split_ns = namespace.split('|')
            for i, ns in enumerate(split_ns):
                print(ns, file=f)
                print(heading_order[i+1] * len(ns), file=f)
                print("", file=f)
                (namespace_dict, descs) = namespace_dict[ns]
            namespaces = [(namespace, (namespace_dict, descs), len(split_ns))]
        
        for (ns, (child_namespaces, descs), depth) in namespaces:
            print(ns, file=f)
            print(heading_order[depth] * len(ns), file=f)
            print("", file=f)
            for desc in descs:
                generate_module_doc(desc, f, depth+1)
            namespaces = \
                itertools.chain([(ns + '|' + c[0], c[1]) 
                                 for c in child_namespaces.items()],
                                namespaces, depth+1)

def run():
    vistrails.core.application.init()
    pkg = Package("org.vistrails.vistrails.basic")
    # pkg = Package("org.vistrails.vistrails.matplotlib")
    generate_docs(pkg)

if __name__ == '__main__':
    run()
