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


package net.sourceforge.pmd.lang.java.ast

import net.sourceforge.pmd.lang.test.ast.shouldBe
import net.sourceforge.pmd.lang.java.types.JPrimitiveType.PrimitiveTypeKind.INT

class ASTStatementsTest : ParserTestSpec({
    parserTestContainer("Foreach loop") {
        inContext(StatementParsingCtx) {
            """
                 for (Integer i : new Iter<>()) 
                    loop();
            """ should parseAs {
                foreachLoop {
                    it::getVarId shouldBe fromChild<ASTLocalVariableDeclaration, ASTVariableId> {
                        it::getModifiers shouldBe modifiers {}

                        it::getTypeNode shouldBe classType("Integer")
                        fromChild<ASTVariableDeclarator, ASTVariableId> {
                            variableId("i") {
                                it::isLocalVariable shouldBe false
                                it::isForLoopVariable shouldBe false
                                it::isForeachVariable shouldBe true
                            }
                        }
                    }

                    it::getIterableExpr shouldBe constructorCall()

                    exprStatement {
                        methodCall("loop")
                    }
                }
            }

            """
                 for (@Nullable final Integer i : new Iter<>()) {
                 
                    continue;
                 }
            """ should parseAs {
                foreachLoop {
                    val foreach = it

                    localVarDecl {
                        it::getModifiers shouldBe modifiers {
                            annotation("Nullable")
                        }
                        classType("Integer")
                        variableDeclarator("i")
                    }

                    it.varId::isFinal shouldBe true

                    it::getIterableExpr shouldBe constructorCall()

                    block {
                        continueStatement {
                            it::getTarget shouldBe foreach
                        }
                    }
                }
            }
        }
    }

    parserTestContainer("For loop") {
        inContext(StatementParsingCtx) {
            "for (;;) {}" should parseAs {
                forLoop {
                    it::getInit shouldBe null
                    it::getCondition shouldBe null
                    it::getUpdate shouldBe null

                    block { }
                }
            }

            "for (int i = 0; i < 2; i++);" should parseAs {
                forLoop {
                    it::getInit shouldBe forInit {
                        localVarDecl {
                            localVarModifiers()
                            primitiveType(INT)
                            varDeclarator {
                                variableId("i") {
                                    it::isLocalVariable shouldBe true // different from foreach too
                                    it::isForLoopVariable shouldBe true
                                    it::isForeachVariable shouldBe false
                                }
                                int(0)
                            }
                        }
                    }
                    it::getCondition shouldBe infixExpr(BinaryOp.LT) {
                        variableAccess("i")
                        int(2)
                    }
                    it::getUpdate shouldBe forUpdate {
                        statementExprList {
                            unspecifiedChild()
                        }
                    }
                    emptyStatement()
                }
            }

            "for (i = 1, j = 1; ; i *= 2, j += 2);" should parseAs {
                forLoop {
                    it::getInit shouldBe forInit {
                        statementExprList {
                            assignmentExpr(AssignmentOp.ASSIGN)
                            assignmentExpr(AssignmentOp.ASSIGN)
                        }
                    }
                    it::getCondition shouldBe null
                    it::getUpdate shouldBe forUpdate {
                        statementExprList {
                            assignmentExpr(AssignmentOp.MUL_ASSIGN)
                            assignmentExpr(AssignmentOp.ADD_ASSIGN)
                        }
                    }

                    emptyStatement()
                }
            }
        }
    }

    parserTestContainer("Blocks") {
        inContext(StatementParsingCtx) {
            """
               {
                 for (;;) {}
                 for (Integer i : new Iter<>()) loop();
                 a = 0;
               }
            """ should parseAs {
                block {
                    forLoop()
                    foreachLoop()
                    exprStatement()
                }
            }
        }
    }

    parserTestContainer("Labeled statements") {
        inContext(StatementParsingCtx) {
            """
               {
                 l: for (;;) {
                    i: for (;;)
                        if (true || false)
                            break l;
                        else
                            continue i;
                 }

                 for (Integer i : new Iter<>())
                    break l;
               }
            """ should parseAs {
                block {
                    labeledStatement("l") {
                        val labeledL = it
                        forLoop {
                            block {
                                labeledStatement("i") {
                                    val labeledI = it
                                    forLoop {
                                        ifStatement {
                                            it::getCondition shouldBe unspecifiedChild()
                                            it::getThenBranch shouldBe breakStatement("l") {
                                                it::getTarget shouldBe labeledL
                                            }
                                            it::getElseBranch shouldBe continueStatement("i") {
                                                it::getTarget shouldBe labeledI
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    foreachLoop {
                        unspecifiedChildren(2)
                        breakStatement("l") {
                            it::getTarget shouldBe null // invalid code
                        }
                    }
                }
            }
        }
    }
})
