/**
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
 */

package net.sourceforge.pmd.lang.ecmascript.ast;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

import net.sourceforge.pmd.util.StringUtil;

public class DumpFacade {

    private PrintWriter writer;
    private boolean recurse;

    public void initializeWith(Writer writer, String prefix, boolean recurse, EcmascriptNode<?> node) {
        this.writer = writer instanceof PrintWriter ? (PrintWriter) writer : new PrintWriter(writer);
        this.recurse = recurse;
        this.dump(node, prefix);
        try {
            writer.flush();
        } catch (IOException e) {
            throw new RuntimeException("Problem flushing PrintWriter.", e);
        }
    }

    public Object visit(EcmascriptNode<?> node, Object data) {
        dump(node, (String) data);
        if (recurse) {
            for (int i = 0; i < node.jjtGetNumChildren(); i++) {
                visit((EcmascriptNode<?>) node.jjtGetChild(i), data + " ");
            }
            return data;
        } else {
            return data;
        }
    }

    private void dump(EcmascriptNode<?> node, String prefix) {
        //
        // Dump format is generally composed of the following items...
        //

        // 1) Dump prefix
        writer.print(prefix);

        // 2) JJT Name of the Node
        writer.print(node.toString());

        //
        // If there are any additional details, then:
        // 1) A colon
        // 2) The Node.getImage() if it is non-empty
        // 3) Extras in parentheses
        //

        // Standard image handling
        String image = node.getImage();

        // Special image handling (e.g. Nodes with normally null images)
        image = StringUtil.escapeWhitespace(image);

        // Extras
        List<String> extras = new ArrayList<>();

        // Standard DestructuringNode extras
        if (node instanceof DestructuringNode) {
            if (((DestructuringNode) node).isDestructuring()) {
                extras.add("destructuring");
            }
        }

        // Other extras
        if (node instanceof ASTArrayComprehension) {
            if (((ASTArrayComprehension) node).hasFilter()) {
                extras.add("has filter");
            }
        } else if (node instanceof ASTBreakStatement) {
            if (((ASTBreakStatement) node).hasLabel()) {
                extras.add("has label");
            }
        } else if (node instanceof ASTCatchClause) {
            if (((ASTCatchClause) node).isIf()) {
                extras.add("if");
            }
        } else if (node instanceof ASTContinueStatement) {
            if (((ASTContinueStatement) node).hasLabel()) {
                extras.add("has label");
            }
        } else if (node instanceof ASTExpressionStatement) {
            if (((ASTExpressionStatement) node).hasResult()) {
                extras.add("has result");
            }
        } else if (node instanceof ASTForInLoop) {
            if (((ASTForInLoop) node).isForEach()) {
                extras.add("for each");
            }
        } else if (node instanceof ASTFunctionCall) {
            if (((ASTFunctionCall) node).hasArguments()) {
                extras.add("has arguments");
            }
        } else if (node instanceof ASTFunctionNode) {
            if (((ASTFunctionNode) node).isClosure()) {
                extras.add("closure");
            }
            if (((ASTFunctionNode) node).isGetter()) {
                extras.add("getter");
            }
            if (((ASTFunctionNode) node).isSetter()) {
                extras.add("setter");
            }
        } else if (node instanceof ASTIfStatement) {
            if (((ASTIfStatement) node).hasElse()) {
                extras.add("has else");
            }
        } else if (node instanceof ASTKeywordLiteral) {
            if (((ASTKeywordLiteral) node).isBoolean()) {
                extras.add("boolean");
            }
        } else if (node instanceof ASTLetNode) {
            if (((ASTLetNode) node).hasBody()) {
                extras.add("has body");
            }
        } else if (node instanceof ASTName) {
            if (((ASTName) node).isLocalName()) {
                extras.add("local");
            }
            if (((ASTName) node).isGlobalName()) {
                extras.add("global");
            }
        } else if (node instanceof ASTNewExpression) {
            if (((ASTNewExpression) node).hasArguments()) {
                extras.add("has arguments");
            }
            if (((ASTNewExpression) node).hasInitializer()) {
                extras.add("has initializer");
            }
        } else if (node instanceof ASTNumberLiteral) {
            extras.add("Number=" + ((ASTNumberLiteral) node).getNumber());
            extras.add("NormalizedImage=" + ((ASTNumberLiteral) node).getNormalizedImage());
        } else if (node instanceof ASTObjectProperty) {
            if (((ASTObjectProperty) node).isGetter()) {
                extras.add("getter");
            }
            if (((ASTObjectProperty) node).isSetter()) {
                extras.add("setter");
            }
        } else if (node instanceof ASTRegExpLiteral) {
            extras.add("Flags=" + ((ASTRegExpLiteral) node).getFlags());
        } else if (node instanceof ASTReturnStatement) {
            if (((ASTReturnStatement) node).hasResult()) {
                extras.add("has result");
            }
        } else if (node instanceof ASTStringLiteral) {
            if (((ASTStringLiteral) node).isSingleQuoted()) {
                extras.add("single quoted");
            }
            if (((ASTStringLiteral) node).isDoubleQuoted()) {
                extras.add("double quoted");
            }
        } else if (node instanceof ASTSwitchCase) {
            if (((ASTSwitchCase) node).isDefault()) {
                extras.add("default");
            }
        } else if (node instanceof ASTTryStatement) {
            if (((ASTTryStatement) node).hasCatch()) {
                extras.add("catch");
            }
            if (((ASTTryStatement) node).hasFinally()) {
                extras.add("finally");
            }
        } else if (node instanceof ASTUnaryExpression) {
            if (((ASTUnaryExpression) node).isPrefix()) {
                extras.add("prefix");
            }
            if (((ASTUnaryExpression) node).isPostfix()) {
                extras.add("postfix");
            }
        }

        // Standard EcmascriptNode extras
        if (node.hasSideEffects()) {
            extras.add("has side effects");
        }

        // Output image and extras
        if (image != null || !extras.isEmpty()) {
            writer.print(':');
            if (image != null) {
                writer.print(image);
            }
            for (String extra : extras) {
                writer.print('(');
                writer.print(extra);
                writer.print(')');
            }
        }

        writer.println();
    }

}
