#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
################################################################################
#
#  qooxdoo - the new era of web development
#
#  http://qooxdoo.org
#
#  Copyright:
#    2006-2010 1&1 Internet AG, Germany, http://www.1und1.de
#
#  License:
#    LGPL: http://www.gnu.org/licenses/lgpl.html
#    EPL: http://www.eclipse.org/org/documents/epl-v10.php
#    See the LICENSE file in the project's top-level directory for details.
#
#  Authors:
#    * Sebastian Werner (wpbasti)
#    * Alessandro Sala (asala)
#
################################################################################

import sys, string, re
from ecmascript.frontend import lang, Comment_2 as Comment

KEY = re.compile("^[A-Za-z0-9_$]+$")


def compileToken(name, compact=False):
    global pretty


    if name in ["INC", "DEC", "TYPEOF"]:
        pass

    elif name in ["INSTANCEOF", "IN"]:
        space()

    elif not compact and pretty:
        space()



    if name == None:
        write("=")

    elif name in ["TYPEOF", "INSTANCEOF", "IN"]:
        write(name.lower())

    else:
        for key in lang.TOKENS:
            if lang.TOKENS[key] == name:
                write(key)



    if name in ["INC", "DEC"]:
        pass

    elif name in ["TYPEOF", "INSTANCEOF", "IN"]:
        space()

    elif not compact and pretty:
        space()


def space(force=True):
    global indent
    global result
    global pretty
    global afterLine
    global afterBreak
    global afterDoc

    if not force and not pretty:
        return

    if afterDoc or afterBreak or afterLine or result[-1].endswith(" ") or result[-1].endswith("\n"):
        return

    result[-1] += " "


def write(txt=""):
    global indent
    global result
    global pretty
    global breaks
    global afterLine
    global afterBreak
    global afterDoc
    global afterDivider
    global afterArea

    # strip remaining whitespaces
    if (afterLine or afterBreak or afterDivider or afterArea) and result[-1].endswith(" "):
        result[-1] = result[-1].rstrip()

    if pretty:
        # handle new line wishes
        if afterArea:
            nr = 5
        elif afterDivider:
            nr = 5
        elif afterDoc:
            nr = 3
        elif afterBreak:
            nr = 2
        elif afterLine:
            nr = 1
        else:
            nr = 0

        while not result[-1].endswith("\n" * nr):
            result[-1] += "\n"

    elif breaks and not result[-1].endswith("\n"):
        if afterArea or afterDivider or afterDoc or afterBreak or afterLine:
            result[-1] += "\n"

    # reset
    afterLine = False
    afterBreak = False
    afterDoc = False
    afterDivider = False
    afterArea = False

    # add indent (if needed)
    if pretty and result[-1].endswith("\n"):
        #result += (" " * (INDENTSPACES * indent))
        result[-1] += (options.prettypIndentString * indent)

    # append given text
    if len(txt)<4:
        result[-1] += txt
    else:
        result.append(txt)


def area():
    global afterArea
    afterArea = True


def divide():
    global afterDivider
    afterDivider = True


def sep():
    global afterBreak
    afterBreak = True


def doc():
    global afterDoc
    afterDoc = True


def nosep():
    global afterBreak
    afterBreak = False


def line():
    global afterLine
    afterLine = True


def noline():
    global afterLine
    global afterBreak
    global afterDivider
    global afterArea
    global afterDoc

    afterLine = False
    afterBreak = False
    afterDivider = False
    afterArea = False
    afterDoc = False


def inc_indent():
    global indent
    indent += 1


def dec_indent():
    global indent
    indent -= 1


def semicolon():
    global result
    global breaks

    noline()

    if not (result[-1].endswith("\n") or result[-1].endswith(";")):
        write(";")

        if breaks:
            result[-1] += "\n"


def comma():
    global result
    global breaks

    noline()

    if not (result[-1].endswith("\n") or result[-1].endswith(",")):
        write(",")

        if breaks:
            result[-1] += "\n"


def commentNode(node):
    global pretty

    if not pretty:
        return

    commentText = ""
    commentIsInline = False

    commnt = node.getChild("commentsAfter", False)

    if commnt and not commnt.get("inserted", False):
        for child in commnt.children:
            if not child.isFirstChild():
                commentText += " "

            commentText += child.get("text")

            if child.get("detail") == "inline":
                commentIsInline = True

        if commentText != "":
            padding = getInlineCommentPadding(options, child.get("column"))
            if padding:
                commentText = padding + commentText.strip()
            else:
                space()
            ##space()
            write(commentText)
            #print commentText

            if commentIsInline:
                line()
            else:
                space()

            commnt.set("inserted", True)



def getInlineCommentPadding(options, keepColumn):
    global result

    padding = ""
    lineLength = -1

    # Retaining keepColumn?
    if options.prettypCommentsTrailingKeepColumn:

        # Find length of last line
        posReturn = result[-1].rfind("\n")
        if posReturn == -1:
            posReturn = 0
        lineLength = (len(result[-1]) - posReturn - 1)

        # Work out padding to keep column at same position
        if keepColumn > lineLength:
            padding = " " * (keepColumn - lineLength - 1)

    # Check if preferred comment columns are defined
    if not padding and options.prettypCommentsTrailingCommentCols:

        # Find length of last line, but only if not already done
        if lineLength == -1:
            posReturn = result[-1].rfind("\n")
            if posReturn == -1:
                posReturn = 0
            lineLength = (len(result[-1]) - posReturn - 1)

        # Work out preferred position of text
        for commentCol in options.prettypCommentsTrailingCommentCols:
            if commentCol > (lineLength + 1):   # leave room for a space
                padding = " " * (commentCol - lineLength - 1)
                break

    # If not retaining keepColumn or comment cols not defined or not far enough across then put in fixed padding
    if not padding and options.prettypCommentsInlinePadding:
        padding = options.prettypCommentsInlinePadding
    return padding



def postProcessMap(m):
    if m.get("maxKeyLength", False):
        return

    maxKeyLength = 0
    alignValues = True

    if m.hasChildren():
        for keyvalue in m.children:
            if keyvalue.type != "keyvalue":
                continue

            currKeyLength = len(keyvalue.get("key"))

            if keyvalue.get("quote", False):
                currKeyLength += 2

            if currKeyLength > maxKeyLength:
                maxKeyLength = currKeyLength

            if alignValues and keyvalue.getChild("value").isComplex():
                alignValues = False

    m.set("maxKeyLength", maxKeyLength)
    m.set("alignValues", alignValues)




def inForLoop(node):
    while node:
        if node.type in ["first", "second", "third"] and node.parent.type == "loop" and node.parent.get("loopType") == "FOR":
            return True

        if not node.hasParent():
            return False

        node = node.parent

    return False




def prettyNode(node, opts, rslt, enableBreaks=False, enableVerbose=False):
    global indent
    global result
    global pretty
    global verbose
    global breaks
    global afterLine
    global afterBreak
    global afterDoc
    global afterDivider
    global afterArea
    global options

    result = rslt

    options = opts
    opts.prettypIndentString          = eval("'" + opts.prettypIndentString + "'")
    opts.prettypCommentsInlinePadding = eval("'" + opts.prettypCommentsInlinePadding + "'")
                                                              # allow for escapes like "\t"
    # split trailing comment cols into an array
    if (opts.prettypCommentsTrailingCommentCols and
        isinstance(opts.prettypCommentsTrailingCommentCols, basestring)):
        opts.prettypCommentsTrailingCommentCols = [int(column.strip()) for column in opts.prettypCommentsTrailingCommentCols.split(",")]
        opts.prettypCommentsTrailingCommentCols.sort() # make sure they are ascending!
    # or make sure it's a list of int's
    elif (isinstance(opts.prettypCommentsTrailingCommentCols, list) and
        reduce(lambda y,z: y and z,
               [isinstance(x,int) for x in opts.prettypCommentsTrailingCommentCols],
               True)):
        opts.prettypCommentsTrailingCommentCols.sort() # make sure they are ascending!
    # or pass
    else:
        #raise TypeError, "Unsuitable type for option --pretty-print-comments-trailing-commentCols"
        pass

    if opts.prettypCommentsBlockAdd:
        Comment.fill(node)

    indent       = 0
    result       = [u""]
    pretty       = opts.prettyPrint
    verbose      = enableVerbose
    breaks       = enableBreaks
    afterLine    = False
    afterBreak   = False
    afterDoc     = False
    afterDivider = False
    afterArea    = False

    return _prettyNode(node,opts,result)


def _prettyNode(node, optns, result):

    global pretty
    global indent




    #####################################################################################################################
    # Recover styling
    #####################################################################################################################

    if pretty:
        # Recover exclicit breaks
        if node.get("breakBefore", False) and not node.isFirstChild(True):
            sep()

        # Additional explicit break before complex blocks
        if node.hasParent() and not node.isFirstChild(True) and node.parent.type in ["block", "file"] and node.isComplex():
            sep()



    #####################################################################################################################
    # Insert comments before
    #####################################################################################################################

    if pretty:
        if node.getChild("commentsBefore", False) != None:
            commentCounter = 0
            commentsBefore = node.getChild("commentsBefore")
            isFirst = node.isFirstChild()
            previous = node.getPreviousSibling(False, True)

            if previous and previous.type in ["case", "default"]:
                inCase = True
            else:
                inCase = False

            inOperation = node.parent.type in ["first", "second", "third"] and node.parent.parent.type == "operation"

            for child in commentsBefore.children:
                docComment = child.get("detail") in ["javadoc", "qtdoc"]
                headComment = child.get("detail") == "header"
                areaComment = child.get("detail") == "area"
                divComment = child.get("detail") == "divider"
                blockComment = child.get("detail") ==  "block"
                singleLineBlock = child.get("detail") != "inline" and child.get("multiline") == False

                if not child.isFirstChild():
                    pass

                elif inCase:
                    pass

                elif singleLineBlock:
                    if child.get("begin"):
                        sep()
                    else:
                        space()

                elif areaComment and not isFirst:
                    area()

                elif divComment and not isFirst:
                    divide()

                elif not isFirst:
                    if docComment:
                        doc()
                    else:
                        if child.get("text") in ("//", "/*"):
                             nosep() # the blank comment provides a blank line, so don't put a separator in
                             line()  # make sure there is a new line after whatever has gone before
                        else:
                             sep()
 

                elif inOperation:
                    sep()

                elif not headComment:
                    line()

                # reindenting first
                text = child.get("text")

                if child.get("detail") == "qtdoc":
                    text = Comment.Comment(text).qt2javadoc()

                #write(comment.indent(text, INDENTSPACES * indent))
                write(Comment.Text(text).indent(optns.prettypIndentString * indent))

                if singleLineBlock:
                    if docComment:
                        line()
                    elif child.get("end"):
                        sep()
                    else:
                        space()

                # separator after divider/head comments and after block comments which are not for documentation
                elif headComment or areaComment or divComment or blockComment:
                    sep()

                else:
                    line()
                    # the next two: bugzilla#454
                    #global result
                    #result += "\n"






    #####################################################################################################################
    # Opening...
    #####################################################################################################################

    #
    # OPEN: FINALLY
    ##################################

    if node.type == "finally":
        write("finally")


    #
    # OPEN: DELETE
    ##################################

    elif node.type == "delete":
        write("delete")
        space()


    #
    # OPEN: THROW
    ##################################

    elif node.type == "throw":
        write("throw")
        space()


    #
    # OPEN: NEW
    ##################################

    elif node.type == "instantiation":
        write("new")
        space()


    #
    # OPEN: RETURN
    ##################################

    elif node.type == "return":
        write("return")

        if node.hasChildren():
            space()


    #
    # OPEN: DEFINITION LIST
    ##################################

    elif node.type == "definitionList":
        write("var")
        space()


    #
    # OPEN: BREAK
    ##################################

    elif node.type == "break":
        write("break")

        if node.get("label", False):
            space()
            write(node.get("label"))


    #
    # OPEN: CONTINUE
    ##################################

    elif node.type == "continue":
        write("continue")

        if node.get("label", False):
            space()
            write(node.get("label", False))


    #
    # OPEN: FUNCTION
    ##################################

    elif node.type == "function":
        write("function")

        if node.getChild("identifier", False):
            functionName = node.getChild("identifier", False).get("value")
            space()
            write(functionName)


    #
    # OPEN: IDENTIFIER
    ##################################

    elif node.type == "identifier":
        name = node.get("value", False)
        if name:
            write(name)


    #
    # OPEN: DEFINITION
    ##################################

    elif node.type == "definition":
        if node.parent.type != "definitionList":
            write("var")
            space()

        write(node.get("identifier"))


    #
    # OPEN: CONSTANT
    ##################################

    elif node.type == "constant":
        if node.get("constantType") == "string":
            if node.get("detail") == "singlequotes":
                write("'")
            else:
                write('"')

            write(node.get("value"))

            if node.get("detail") == "singlequotes":
                write("'")
            else:
                write('"')

        else:
            write(node.get("value"))


    #
    # OPEN: COMMENT
    ##################################

    elif node.type == "comment":
        if pretty:
            commentText = node.get("text")
            # insert appropriate spaces before and no newline in the case of after comments
            if node.get("connection") == "after":
                noline()
                padding = getInlineCommentPadding(optns, node.get("column"))
                if padding:
                    commentText = padding + commentText.strip()
                else:
                    space()
                ##space()

            ##write(node.get("text"))
            write(commentText)

            # new line after inline comment (for example for syntactical reasons)
            #if (node.get("detail") == "inline") or (node.get("multiline") == False):
            if (node.get("detail") == "inline") or (node.get("end") == True):
                line()
            else:
                space()


    #
    # OPEN: RIGHT
    ##################################

    elif node.type == "right":
        if node.parent.type == "accessor":
            write(".")






    #
    # OPEN: ASSIGNMENT
    ##################################

    elif node.type == "assignment":
        if node.parent.type == "definition":
            oper = node.get("operator", False)
            oper = None if oper == False else oper

            # be compact in for-loops
            compact = inForLoop(node)
            compileToken(oper, compact)





    #
    # OPEN: KEY
    ##################################

    elif node.type == "key":
        if node.parent.type == "accessor":
            write("[")


    #
    # OPEN: GROUP
    ##################################

    elif node.type == "group":
        write("(")


    #
    # OPEN: VOID
    ##################################

    elif node.type == "void":
        write("void")
        write("(")


    #
    # OPEN: ARRAY
    ##################################

    elif node.type == "array":
        write("[")

        if node.hasChildren(True):
            space(False)


    #
    # OPEN: PARAMS
    ##################################

    elif node.type in ("params", "arguments"):
        noline()
        write("(")








    #
    # OPEN: CASE
    ##################################

    elif node.type == "case":
        if pretty:
            # force double new lines
            if not node.isFirstChild():
                sep()
                dec_indent()

            line()

        write("case")
        space()


    #
    # OPEN: DEFAULT
    ##################################

    elif node.type == "default":
        if pretty:
            dec_indent()

            # force double new lines
            if (node.getPreviousSibling(False) and 
                not node.getPreviousSibling(True).type == "case"):
                sep()

        write("default")
        write(":")

        if pretty:
            inc_indent()
            line()






    #
    # OPEN: TRY
    ##################################

    elif node.type == "switch":
        # Additional new line before each switch/try
        if not node.isFirstChild(True) and not node.getChild("commentsBefore", False):
            prev = node.getPreviousSibling(False, True)

            # No separation after case statements
            if prev != None and prev.type in ["case", "default"]:
                pass
            else:
                sep()

        if node.get("switchType") == "catch":
            write("try")
        elif node.get("switchType") == "case":
            write("switch")


    #
    # OPEN: CATCH
    ##################################

    elif node.type == "catch":
        if pretty:
            # If this statement block or the previous try were not complex, be not complex here, too
            if not node.getChild("statement").getChild("block").isComplex() and not node.parent.getChild("statement").getChild("block").isComplex():
                noline()
                space()
            # the next two: bugzilla#454
            if node.getFirstChild().type == "commentsBefore" and not result[-1].endswith("\n"):
                line()

        write("catch")







    #
    # OPEN: MAP
    ##################################

    elif node.type == "map":
        par = node.parent

        if pretty:
            postProcessMap(node)

        if pretty:
            # No break before return statement
            if node.hasParent() and node.parent.type == "expression" and node.parent.parent.type == "return":
                pass

            elif ((node.isComplex() and not (optns.prettypOpenCurlyNewlineBefore in "nN"))
                  or (optns.prettypOpenCurlyNewlineBefore in "aA")):
                line()
                if optns.prettypOpenCurlyIndentBefore:
                    inc_indent()

        write("{")

        if pretty:
            if node.isComplex():
                line()
                if not optns.prettypAlignBlockWithCurlies:
                    inc_indent()

            elif node.hasChildren(True):
                space()


    #
    # OPEN: KEYVALUE
    ##################################

    elif node.type == "keyvalue":
        keyString = node.get("key")
        keyQuote = node.get("quote", False)

        if keyQuote:
            # print "USE QUOTATION"
            if keyQuote == "doublequotes":
                keyString = '"' + keyString + '"'
            else:
                keyString = "'" + keyString + "'"

        elif keyString in lang.RESERVED or not KEY.match(keyString):
            print "Warning: Auto protect key: %s" % keyString
            keyString = "\"" + keyString + "\""

        if pretty and not node.isFirstChild(True) and not node.hasChild("commentsBefore") and node.getChild("value").isComplex():
            sep()

        write(keyString)
        space(False)

        # Fill with spaces
        # Do this only if the parent is complex (many entries)
        # But not if the value itself is complex
        if pretty and node.parent.isComplex() and node.parent.get("alignValues"):
            write(" " * (node.parent.get("maxKeyLength") - len(keyString)))

        write(":")
        space(False)







    #
    # OPEN: BLOCK
    ##################################

    elif node.type == "block":
        if pretty:
            if ((node.isComplex() and not (optns.prettypOpenCurlyNewlineBefore in "nN"))
                or (optns.prettypOpenCurlyNewlineBefore in "aA")):
                line()
                nl=True
                if optns.prettypOpenCurlyIndentBefore:
                    inc_indent()
            else:
                space()
                nl=False

        write("{")

        if pretty:
            if node.hasChildren():
                line()
                if (not nl) or (nl and not optns.prettypAlignBlockWithCurlies):
                    inc_indent()


    #
    # OPEN: LOOP
    ##################################

    elif node.type == "loop":
        # Additional new line before each loop
        if not node.isFirstChild(True) and not node.getChild("commentsBefore", False):
            prev = node.getPreviousSibling(False, True)

            # No separation after case statements
            if prev != None and prev.type in ["case", "default"]:
                pass
            elif node.hasChild("elseStatement") or node.getChild("statement").hasBlockChildren():
                sep()
            else:
                line()

        loopType = node.get("loopType")

        if loopType == "IF":
            write("if")
            space(False)

        elif loopType == "WHILE":
            write("while")
            space(False)

        elif loopType == "FOR":
            write("for")
            space(False)

        elif loopType == "DO":
            write("do")
            space(False)

        elif loopType == "WITH":
            write("with")
            space(False)

        else:
            print "Warning: Unknown loop type: %s" % loopType



    #
    # OPEN: ELSE
    ##################################

    elif node.type == "elseStatement":
        if node.hasChild("commentsBefore"):
            pass

        elif pretty:
            if not node.hasChild("block") and not node.hasChild("loop"):
                pass

            elif not node.isComplex():
                noline()
                space()

        write("else")

        # This is a elseStatement without a block around (a set of {})
        if not node.hasChild("block"):
            space()


    #
    # OPEN: EXPRESSION
    ##################################

    elif node.type == "expression":
        if node.parent.type == "loop":
            loopType = node.parent.get("loopType")

            # only do-while loops
            if loopType == "DO":
                if pretty:
                    stmnt = node.parent.getChild("statement")
                    compact = stmnt.hasChild("block") and not stmnt.getChild("block").isComplex()

                    if compact:
                        noline()
                        space()

                write("while")

                if pretty:
                    space()

            # open expression block of IF/WHILE/DO-WHILE/FOR statements
            write("(")

        elif node.parent.type == "catch":
            # open expression block of CATCH statement
            write("(")

        elif node.parent.type == "switch" and node.parent.get("switchType") == "case":
            # open expression block of SWITCH statement
            write("(")


    #
    # OPEN: FIRST
    ##################################

    elif node.type == "first":
        # for loop
        if node.parent.type == "loop" and node.parent.get("loopType") == "FOR":
            write("(")

        # operation
        elif node.parent.type == "operation":
            # operation (var a = -1)
            if node.parent.get("left", False) == True:
                compileToken(node.parent.get("operator"), True)



    #
    # OPEN: SECOND
    ##################################

    elif node.type == "second":
        # for loop
        if node.parent.type == "loop" and node.parent.get("loopType") == "FOR":
            if not node.parent.hasChild("first"):
                write("(;")

        # operation
        elif node.parent.type == "operation":
            if node.isComplex():
                # (?: hook operation)
                if node.parent.get("operator") == "HOOK":
                    sep()
                else:
                    line()





    #
    # OPEN: THIRD
    ##################################

    elif node.type == "third":
        # for loop
        if node.parent.type == "loop" and node.parent.get("loopType") == "FOR":
            if not node.parent.hasChild("second"):
                if node.parent.hasChild("first"):
                    write(";")
                    space(False)
                else:
                    write("(;;")

        # operation
        elif node.parent.type == "operation":
            # (?: hook operation)
            if node.parent.get("operator") == "HOOK":
                if node.isComplex():
                    sep()


    #
    # OPEN: STATEMENT
    ##################################

    elif node.type == "statement":
        # for loop
        if node.parent.type == "loop" and node.parent.get("loopType") == "FOR":
            if node.parent.get("forVariant") == "iter":
                if not node.parent.hasChild("first") and not node.parent.hasChild("second") and not node.parent.hasChild("third"):
                    write("(;;");

                elif not node.parent.hasChild("second") and not node.parent.hasChild("third"):
                    write(";")

            write(")")

            if not node.hasChild("block"):
                space(False)













    #####################################################################################################################
    # Children content
    #####################################################################################################################

    if node.hasChildren():
        for child in node.children:
            if not node.type in ["commentsBefore", "commentsAfter"]:
                _prettyNode(child,optns,result)









    #####################################################################################################################
    # Closing node
    #####################################################################################################################

    #
    # CLOSE: IDENTIFIER
    ##################################

    if node.type == "identifier":
        if node.hasParent() and node.parent.type == "variable" and not node.isLastChild(True):
            write(".")
        elif node.hasParent() and node.parent.type == "label":
            write(":")


    #
    # CLOSE: ACCESSOR
    ##################################

    elif node.type == "accessor":
        if node.hasParent() and node.parent.type == "variable" and not node.isLastChild(True):
            write(".")


    #
    # CLOSE: KEYVALUE
    ##################################

    elif node.type == "keyvalue":
        if node.hasParent() and node.parent.type == "map" and not node.isLastChild(True):
            noline()
            comma()

            if pretty:
                commentNode(node)

                if node.getChild("value").isComplex():
                    sep()
                elif node.parent.isComplex():
                    line()
                else:
                    space()


    #
    # CLOSE: DEFINITION
    ##################################

    elif node.type == "definition":
        if node.hasParent() and node.parent.type == "definitionList" and not node.isLastChild(True):
            comma()

            if pretty:
                commentNode(node)

                if node.hasComplexChildren():
                    line()
                else:
                    space()


    #
    # CLOSE: LEFT
    ##################################

    elif node.type == "left":
        if node.hasParent() and node.parent.type == "assignment":
            oper = node.parent.get("operator", False)
            oper = None if oper == False else oper

            # be compact in for-loops
            compact = inForLoop(node)
            compileToken(oper, compact)






    #
    # CLOSE: KEY
    ##################################

    elif node.type == "key":
        if node.hasParent() and node.parent.type == "accessor":
            write("]")


    #
    # CLOSE: GROUP
    ##################################

    elif node.type == "group":
        if node.getChildrenLength(True) == 1:
            #noline()  # commented out due to bug#811
            pass

        write(")")


    #
    # CLOSE: VOID
    ##################################

    elif node.type == "void":
        if node.getChildrenLength(True) == 1:
            noline()

        write(")")


    #
    # CLOSE: ARRAY
    ##################################

    elif node.type == "array":
        if node.hasChildren(True):
            space(False)

        write("]")


    #
    # CLOSE: PARAMS
    ##################################

    elif node.type in ("params", "arguments"):
        write(")")


    #
    # CLOSE: MAP
    ##################################

    elif node.type == "map":
        if pretty:
            if node.isComplex():
                line()
                if not optns.prettypAlignBlockWithCurlies:
                    dec_indent()

            elif node.hasChildren(True):
                space()

        write("}")

        if pretty:
            #if node.isComplex() and optns.prettypOpenCurlyIndentBefore:
            #    dec_indent()
            if node.hasParent() and node.parent.type == "expression" and node.parent.parent.type == "return":
                pass

            elif ((node.isComplex() and not (optns.prettypOpenCurlyNewlineBefore in "nN")) # means: opening "{" on new line
                or (optns.prettypOpenCurlyNewlineBefore in "aA")):
                if optns.prettypOpenCurlyIndentBefore:
                    dec_indent()




    #
    # CLOSE: SWITCH
    ##################################

    elif node.type == "switch":
        if node.get("switchType") == "case":
            if pretty:
                dec_indent()
                if not options.prettypAlignBlockWithCurlies:
                    dec_indent()
                line()

            write("}")

            if pretty:
                commentNode(node)
                line()
                if optns.prettypOpenCurlyIndentBefore:
                    dec_indent()

        # Force a additinal line feed after each switch/try
        if pretty and not node.isLastChild():
            sep()


    #
    # CLOSE: CASE
    ##################################

    elif node.type == "case":
        write(":")

        if pretty:
            commentNode(node)
            inc_indent()
            line()








    #
    # CLOSE: BLOCK
    ##################################

    elif node.type == "block":
        if pretty and node.hasChildren():
            line()
            if not optns.prettypAlignBlockWithCurlies:
                dec_indent()

        write("}")

        if pretty:
            commentNode(node)

            if node.hasChildren():
                # Newline afterwards
                if node.parent.type == "body" and node.parent.parent.type == "function":

                    # But only when this isn't a function block inside a assignment
                    if node.parent.parent.parent.type in ["right", "params", "arguments"]:
                        pass

                    elif node.parent.parent.parent.type == "value" and node.parent.parent.parent.parent.type == "keyvalue":
                        pass

                    else:
                        line()

                else:
                    line()

            # to get the next statement after the block aligned with the parent of the block, you have to
            # unindent after "}" if:
            # - the opening "{" was on a new line AND was indented
            #   OR
            # - the opending "{" was inline AND align-with-curlies is active
            if ((node.isComplex()  and not (optns.prettypOpenCurlyNewlineBefore in "nN")) # means: opening "{" on new line
                or (optns.prettypOpenCurlyNewlineBefore in "aA")):
                if optns.prettypOpenCurlyIndentBefore:
                    dec_indent()
            else :
                if optns.prettypAlignBlockWithCurlies:
                    dec_indent()


    #
    # CLOSE: LOOP
    ##################################

    elif node.type == "loop":
        if node.get("loopType") == "DO":
            semicolon()

        if pretty:
            commentNode(node)

            # Force a additinal line feed after each loop
            if not node.isLastChild():
                if node.hasChild("elseStatement"):
                    sep()
                elif node.getChild("statement").hasBlockChildren():
                    sep()
                else:
                    line()


    #
    # CLOSE: FUNCTION
    ##################################

    elif node.type == "function":
        if pretty:
            #commentNode(node) # commented out due to bug#942

            if not node.isLastChild() and node.hasParent() and node.parent.type in ["block", "file"]:
                sep()


    #
    # CLOSE: EXPRESSION
    ##################################

    elif node.type == "expression":
        if node.parent.type == "loop":
            write(")")

            # e.g. a if-construct without a block {}
            if node.parent.getChild("statement").hasChild("block"):
                pass

            elif node.parent.getChild("statement").hasChild("emptyStatement"):
                pass

            elif node.parent.type == "loop" and node.parent.get("loopType") == "DO":
                pass

            else:
                space(False)

        elif node.parent.type == "catch":
            write(")")

        elif node.parent.type == "switch" and node.parent.get("switchType") == "case":
            write(")")

            if pretty:
                commentNode(node)
                line()
                if optns.prettypOpenCurlyIndentBefore:
                    inc_indent()


            write("{")

            if pretty:
                if not options.prettypAlignBlockWithCurlies:
                    inc_indent()


    #
    # CLOSE: FIRST
    ##################################

    elif node.type == "first":
        # for loop
        if node.parent.type == "loop" and node.parent.get("loopType") == "FOR":
            if node.parent.get("forVariant") == "iter":
                write(";")

                if node.parent.hasChild("second"):
                    space(False)

        # operation
        elif node.parent.type == "operation" and node.parent.get("left", False) != True:
            oper = node.parent.get("operator")
            oper = None if oper == False else oper

            # be compact in for loops
            compact = inForLoop(node)
            compileToken(oper, compact)


    #
    # CLOSE: SECOND
    ##################################

    elif node.type == "second":
        # for loop
        if node.parent.type == "loop" and node.parent.get("loopType") == "FOR":
            write(";")

            if node.parent.hasChild("third"):
                space(False)

        # operation
        elif node.parent.type == "operation":
            # (?: hook operation)
            if node.parent.get("operator") == "HOOK":
                #noline()  # commented out due to bug#3415
                space(False)
                write(":")
                space(False)



    #
    # CLOSE: OTHER
    ##################################

    if node.hasParent() and not node.type in ["comment", "commentsBefore", "commentsAfter"]:

        # Add comma dividers between statements in these parents
        if node.parent.type in ["array", "params", "arguments", "expressionList"]:
            if not node.isLastChild(True):
                comma()

                if pretty:
                    commentNode(node)

                    if node.isComplex():
                        line()
                    else:
                        space()

        # Semicolon handling
        elif node.type in ["group", "block", "assignment", "call", "operation", "definitionList", "return", "break", "continue", "delete", "accessor", "instantiation", "throw", "variable", "emptyStatement"]:

            # Default semicolon handling
            if node.parent.type in ["block", "file"]:
                semicolon()

                if pretty:
                    commentNode(node)
                    line()

                    if node.isComplex() and not node.isLastChild():
                        sep()

            # Special handling for switch statements
            elif node.parent.type == "statement" and node.parent.parent.type == "switch" and node.parent.parent.get("switchType") == "case":
                semicolon()

                if pretty:
                    commentNode(node)
                    line()

                    if node.isComplex() and not node.isLastChild():
                        sep()

            # Special handling for loops (e.g. if) without blocks {}
            elif (
                node.parent.type in ["statement", "elseStatement"] and
                not node.parent.hasChild("block") and
                node.parent.parent.type == "loop"
            ):
                semicolon()

                if pretty:
                    commentNode(node)
                    line()

                    if node.isComplex() and not node.isLastChild():
                        sep()


    #
    # CLOSE: OTHER
    ##################################

    if pretty:
        # Rest of the after comments (not inserted previously)
        commentNode(node)


    return result

def defaultOptions(optns):
    optns.prettyPrint = True  # turn on pretty-printing
    optns.prettypCommentsBlockAdd  = True  # comment filling
    optns.prettypIndentString      = "  "   # general indent string
    optns.prettypOpenCurlyNewlineBefore = 'm'  # mixed, dep. on complexity
    optns.prettypOpenCurlyIndentBefore  = False  # indent curly on a new line
    optns.prettypAlignBlockWithCurlies  = False  # put block on same column as curlies
    optns.prettypCommentsTrailingCommentCols = ''  # put trailing comments on fixed columns
    optns.prettypCommentsTrailingKeepColumn  = False  # fix trailing comment on column of original text
    optns.prettypCommentsInlinePadding  = '  '   # space between end of code and beginning of comment
    return optns
