#! /usr/bin/env python

import sys
import datetime
from robots.actions import nav, primitive_nav, look_at, manipulation, configuration, games, administration

import robots.helpers.geometric_planning
import robots.helpers.position

# documentation of special parameters
special_doc = {"target": "a final position ([x,y,z], [x,y,z,qx,qy,qz,qw], a SPARK object or a ROS frame)",
               "place": "a position ([x,y,z], [x,y,z,qx,qy,qz,qw], a SPARK object or a ROS frame)",
               "waypoints": "a list of positions ([x,y,z], [x,y,z,qx,qy,qz,qw], a SPARK object or a ROS frame)",
               "callback": "a function (or functor) to call when the action is done",
               "object": "an object ID, as spelled by SPARK/Viman"}

def parse_docstring(doc):
    """ Parses the doc string, and return the doc without :param *:
    but with a list of params and their associated doc.

    Also replace known keywords (like action names) by hyperlinks.
    Also replace :see: by 'See also:'
    """
    doc = " ".join([(("[[#" + anchors[word] + "|" + word + "]]") if word in anchors else word) for word in doc.split(' ')])
    doc.replace(":see:", "'''\nSee also:'''")
    r = doc.split(":param ")
    doc = r[0]
    
    params = [param.split(':',1) for param in r[1:]]

    returndoc = None

    if params and ':return:' in params[-1][1]:
        params[-1][1], returndoc = params[-1][1].split(':return:')


    return (doc, params,returndoc)

actions = {}
helpers = {}
anchors = {}

m = sys.modules[__name__]

# Actions (free functions)
for act_m in [getattr(m, fn) for fn in dir(m)]:
    for fn in [getattr(act_m, fn) for fn in dir(act_m) if hasattr(getattr(act_m, fn), "_action")]:
        if act_m.__name__ != "__main__":
            actions.setdefault(act_m.__name__, []).append(
                (fn.__name__, 
                fn.__doc__, 
                fn._broken if hasattr(fn, "_broken") else False,
                fn._tested if hasattr(fn, "_tested") else None))
            anchors[fn.__name__] = act_m.__name__ + "." + fn.__name__

# Helpers (methods of helping classes)
robot_m = getattr(m, 'robots')
for helper_class in [getattr(robot_m, cls) for cls in dir(robot_m)]:
    for fn in [getattr(helper_class, fn) for fn in dir(helper_class) if hasattr(getattr(helper_class, fn), "_helper")]:
        if act_m.__name__ != "__main__":
            helpers.setdefault(helper_class.__module__, []).append(
                (fn.__name__, 
                fn.__doc__, 
                fn._helper_access if hasattr(fn, "_helper_access") else "",
                fn._tested if hasattr(fn, "_tested") else None))
            anchors[fn.__name__] =  helper_class.__module__ + "." + helper_class.__name__ + "." + fn.__name__


def moinmoin():
    print("= Documentation of pyRobots actions and helpers =")
    print("''Automatically generated from pyRobots source on " + datetime.date.today().strftime("%B %d, %Y") + "''")


    print("\n<<TableOfContents(4)>>")

    print("\n\n== Actions ==\n\n")

    for m in actions.keys():
        print("<<Anchor(" + m + ")>>") # anchor
        print("=== Category " + m.split('.')[2] + " ===")
        for name, doc, broken, tested in actions[m]:
            params = []
            print("<<Anchor(" + m + "." + name + ")>>") # anchor
            print("==== Action " + name + " ====")
            if broken:
                print("\n{{{#!wiki caution\n"
                    "'''/!\ Action currently broken!'''\n"
                    "}}}")
            if not tested:
                print("\n{{{#!wiki caution\n"
                    "'''{i} Action not tested!'''\n"
                    "}}}")
            else:
                print("\n{{{#!wiki note\n"
                    "(./) Action tested on the " + tested + "\n"
                    "}}}")
            if doc: 
                doc, params, returndoc = parse_docstring(doc)
                print(doc)
                if params:
                    print("===== Parameters =====")
                    for p, d in params:
                        print(" * '''" + p + "''': " + d)
                if returndoc:
                    print("===== Return value =====")
                    print(returndoc)
    
            print("\n")
            print("Example:\n{{{#!highlight python")
            print("import robots")
            print("nao = robots.Nao() # Pass 'dummy=True' to test without the real robot\n")
            if params:
                for p,d in params:
                    if p in special_doc:
                        print(p + " = " + p.upper() + " # " + special_doc[p] )
                    else:
                        print(p + " = " + p.upper() + " # define here a " + p )
                print("nao." + name + "(" + ", ".join([p for p,d in params]) + ")")
            else:
                print("nao." + name + "()")
            print("}}}")
            print("\n")

    print("\n\n== Helpers ==\n\n")

    for m in helpers.keys():
        print("<<Anchor(" + m + ")>>") # anchor
        print("=== Category " + m.split('.')[2]  + " ===")
        for name, doc, access, tested in helpers[m]:
            params = []
            print("<<Anchor(" + m + "." + name + ")>>") # anchor
            print("==== Helper " + name + " ====")
            if not tested:
                print("\n{{{#!wiki caution\n"
                    "'''Helper not tested!'''\n"
                    "}}}")
            else:
                print("\n{{{#!wiki note\n"
                    "Helper tested on the " + tested + "\n"
                    "}}}")
            if doc: 
                doc, params, returndoc = parse_docstring(doc)
                print(doc)
                if params:
                    print("===== Parameters =====")
                    for p, d in params:
                        print(" * '''" + p + "''': " + d)
                if returndoc:
                    print("===== Return value =====")
                    print(returndoc)
    
            print("\n")
            print("Example:\n{{{#!highlight python")
            print("import robots")
            print("nao = robots.Nao() # Pass 'dummy=True' to test without the real robot\n")
            if params:
                for p,d in params:
                    if p in special_doc:
                        print(p + " = " + p.upper() + " # " + special_doc[p] )
                    else:
                        print(p + " = " + p.upper() + " # define here a " + p )
                print("nao." + name + "(" + ", ".join([p for p,d in params]) + ")")
            else:
                print("nao." + access + "." + name + "()")
            print("}}}")
            print("\n")

def latex():

    print("\\begin{table}")
    print("\\begin{center}")
    print("\\begin{tabular}{l}")

    print("\\hline")
    for m in actions.keys():
        print("\t{\\bf " + m.split('.')[2].replace("_", "\\_") + "} \\\\")
        names = [i[0].replace("_", "\\_") for i in actions[m]]
        print("\t {\\tt " + "}, {\\tt ".join(names) + "} \\\\")
        print("\\hline")
    print("\\end{tabular}")

    print("\\end{center}")
    print("\\caption{List of {\\sc pyRobots} atomic actions, sorted by categories}")
    print("\\label{table|pyrobots_actions}")
    print("\\end{table}")

def usage():
    print("Usage: generate_doc [-h] {moinmoin|latex}")
    print("Defaults to moinmoin syntax.")

if len(sys.argv) > 1:
    if sys.argv[1] in ["-h", "--help"]:
        usage()
        sys.exit()
    if sys.argv[1] == "latex":
        latex()
        sys.exit()
else:
    moinmoin()
