Compile a Pale module's endpoint documentation into RAML format.

    RAML calls Pale endpoints 'resources'. This function converts Pale
    endpoints into RAML resource format.

    The returned string should be appended to the RAML documentation file
    before it is returned.

def generate_raml_resources(module, api_root, user):
    """Compile a Pale module's endpoint documentation into RAML format.

    RAML calls Pale endpoints 'resources'. This function converts Pale
    endpoints into RAML resource format.

    The returned string should be appended to the RAML documentation file
    before it is returned.
    """

    from pale import extract_endpoints, extract_resources, is_pale_module
    if not is_pale_module(module):
        raise ValueError(
                """The passed in `module` (%s) is not a pale module. `paledoc`
                only works on modules with a `_module_type` set to equal
                `pale.ImplementationModule`.""")

    raml_resources = extract_endpoints(module)
    raml_resource_doc_flat = { ep._route_name: document_endpoint(ep) for ep in raml_resources }
    raml_resource_doc_tree = generate_raml_tree(raml_resource_doc_flat, api_root)

    # @TODO generate this dynamically
    pale_argument_type_map = {
        "StringArgument": "string",
        "QueryCursorArgument": "string",
        "KeyArgument": "string",
        "IntegerArgument": "integer",
        "BooleanArgument": "boolean",
        "DateArgument": "date",
        "StringChoiceArgument": "string",
        "QueryKindsArgument": "string",
        "StringChoiceArgument": "string",
        "ListArgument": "array"
    }


    def check_children_for_public_endpoints(subtree):
        """Recursively check a subtree to see if any of its children require permissions.
        Returns an object with property "public" set to True
        if there are children that do not require permissions, false if not."""

        found_children = {}
        found_children["public"] = False

        def subroutine(subtree, found_children):
            if subtree.get("path") != None and len(subtree["path"]) > 0:
                path = subtree["path"]
                for branch in path:
                    if path[branch].get("endpoint") != None and path[branch]["endpoint"].get("requires_permission") == None:
                        found_children["public"] = True
                    else:
                        subroutine(path[branch], found_children)

        subroutine(subtree, found_children)

        return found_children


    def print_resource_tree(tree, output, indent, user, level=0):
        """Walk the tree and add the endpoint documentation to the output buffer.
        The user will only see documentation for endpoints they have the appropriate
        permissions for.
        """

        # check for an endpoint at this level first
        if tree.get("endpoint") != None:

            this_endpoint = tree["endpoint"]

            # check if user has permission to view this endpoint
            # this is currently an on/off switch: if any endpoint has a "@requires_permission"
            # decorator, user.is_admin must be True for the user to see documentation
            # @TODO - make this permission more granular if necessary
            if this_endpoint.get("requires_permission") != None and user != None and user.is_admin or \
                this_endpoint.get("requires_permission") == None:

                indent += "  "
                # add the HTTP method
                if this_endpoint.get("http_method") != None:
                    output.write(indent + this_endpoint["http_method"].lower() + ":\n")
                    indent += "  "

                    # add the description
                    if this_endpoint.get("description") != None:
                        modified_description = clean_description(this_endpoint["description"])
                        output.write(indent + "description: " + modified_description + "\n")

                    # add queryParameters per RAML spec
                    if this_endpoint.get("arguments") != None and len(this_endpoint["arguments"]) > 0:
                        output.write(indent + "queryParameters:\n")
                        sorted_arguments = OrderedDict(sorted(this_endpoint["arguments"].items(), key=lambda t: t[0]))
                        indent += "  "

                        for argument in sorted_arguments:
                            output.write(indent + argument + ":\n")
                            indent += "  "
                            this_argument = sorted_arguments[argument]

                            for arg_detail in this_argument:
                                # check for special kinds of queryParameters
                                this_arg_detail = this_argument[arg_detail]

                                if this_arg_detail != None:

                                    if arg_detail == "default":
                                        default = str(this_arg_detail)
                                        if default == "True" or default == "False":
                                            default = default.lower()
                                        output.write(indent + arg_detail + ": " + default + "\n")
                                    elif arg_detail == "description":
                                        output.write(indent + "description: " + this_arg_detail + "\n")
                                    elif arg_detail == "type":
                                        if pale_argument_type_map.get(this_arg_detail) != None:
                                            if pale_argument_type_map[this_arg_detail] == "array":
                                                # @TODO set the items dynamically
                                                output.write(indent + "type: array\n")
                                                output.write(indent + "items: string\n")
                                            else:
                                                output.write(indent + "type: " + pale_argument_type_map[this_arg_detail].replace(" ", "_") + "\n")
                                        else:
                                            output.write(indent + "type: " + this_arg_detail.replace(" ", "_") + "\n")
                                    elif arg_detail == "required":
                                        output.write(indent + "required" + ": " + str(this_arg_detail).lower() + "\n")
                                    elif arg_detail == "min_length":
                                        output.write(indent + "minLength: " + str(this_arg_detail) + "\n")
                                    elif arg_detail == "max_length":
                                        output.write(indent + "maxLength: " + str(this_arg_detail) + "\n")
                                    elif arg_detail == "min_value":
                                        output.write(indent + "minimum: " + str(this_arg_detail) + "\n")
                                    elif arg_detail == "max_value":
                                        output.write(indent + "maximum: " + str(this_arg_detail) + "\n")

                            indent = indent[:-2]    # reset indent after arg_detail

                        indent = indent[:-2]    # reset indent after argument

                    # add the responses
                    if this_endpoint.get("returns") != None and len(this_endpoint["returns"]) > 0:
                        output.write(indent + "responses:\n")
                        this_response = this_endpoint["returns"]
                        indent += "  "

                        # @TODO refactor the endpoints so that they use a variable called _success
                        # to determine the HTTP code for a successful response
                        # (see changes made to history.py)

                        if this_response.get("success") != None:
                            output.write(indent + str(this_response["success"]) + ":\n")
                        else:
                            output.write(indent + "200:\n")
                        indent += "  "
                        output.write(indent + "body:\n")
                        indent += "  "

                        for res_detail in this_response:
                            if res_detail != "success":
                                if res_detail == "resource_type":
                                    output.write(indent + "type: " + this_endpoint["returns"][res_detail].replace(" ", "_") + "\n")
                                elif res_detail == "description":
                                    modified_description = clean_description(this_endpoint["returns"][res_detail])
                                    output.write(indent + "description: " + modified_description + "\n")

                        indent = indent [:-6]   # resent indent after responses

                    indent = indent [:-2]   # reset indent after endpoint


        # check for further branches and recurse on them
        if tree.get("path") != None and len(tree["path"]) > 0:
            # only increase the indent if we are not on root level
            if level > 0:
                # set the base indentation per the level we are at in the tree
                indent = level * "  "

            subtree = tree["path"]

            admin_user = user!= None and user.is_admin != None and user.is_admin

            for branch in subtree:

                has_public_children = check_children_for_public_endpoints(subtree[branch]).get("public") == True

                is_public_endpoint = subtree[branch].get("endpoint") != None \
                    and subtree[branch]["endpoint"].get("requires_permission") == None

                # @TODO - make this permission more granular if necessary
                # if the user is admin or this endpoint is pubic or has public children:
                if admin_user or has_public_children or is_public_endpoint:
                    # write branch name
                    output.write(indent + "/" + branch + ":\n")
                    # and continue printing the endpoints
                    print_resource_tree(subtree[branch], output, indent, user, level=level+1)



    output = StringIO()
    indent = ""

    print_resource_tree(raml_resource_doc_tree, output, indent, user)
    raml_resources = output.getvalue()
    output.close()
    return raml_resources