/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.lib.jexl.parser

import mozilla.components.lib.jexl.ast.ArrayLiteral
import mozilla.components.lib.jexl.ast.AstNode
import mozilla.components.lib.jexl.ast.ConditionalExpression
import mozilla.components.lib.jexl.ast.FilterExpression
import mozilla.components.lib.jexl.ast.ObjectLiteral
import mozilla.components.lib.jexl.ast.Transformation
import mozilla.components.lib.jexl.lexer.Token

internal val stateMachine: Map<State, StateMap> = mapOf(
    State.EXPECT_OPERAND to StateMap(
        mapOf(
            Token.Type.LITERAL to NextState(State.EXPECT_BIN_OP),
            Token.Type.IDENTIFIER to NextState(
                State.IDENTIFIER,
            ),
            Token.Type.UNARY_OP to NextState(),
            Token.Type.OPEN_PAREN to NextState(
                State.SUB_EXPRESSION,
            ),
            Token.Type.OPEN_CURL to NextState(
                State.EXPECT_OBJECT_KEY,
                ::objectStart,
            ),
            Token.Type.DOT to NextState(State.TRAVERSE),
            Token.Type.OPEN_BRACKET to NextState(
                State.ARRAY_VALUE,
                ::arrayStart,
            ),
        ),
    ),
    State.EXPECT_BIN_OP to StateMap(
        mapOf(
            Token.Type.BINARY_OP to NextState(
                State.EXPECT_OPERAND,
            ),
            Token.Type.PIPE to NextState(State.EXPECT_TRANSFORM),
            Token.Type.DOT to NextState(State.TRAVERSE),
            Token.Type.QUESTION to NextState(
                State.TERNARY_MID,
                ::ternaryStart,
            ),
        ),
        completable = true,
    ),
    State.EXPECT_TRANSFORM to StateMap(
        mapOf(
            Token.Type.IDENTIFIER to NextState(
                State.POST_TRANSFORM,
                ::transform,
            ),
        ),
    ),
    State.EXPECT_OBJECT_KEY to StateMap(
        mapOf(
            Token.Type.IDENTIFIER to NextState(
                State.EXPECT_KEY_VALUE_SEPARATOR,
                ::objectKey,
            ),
            Token.Type.CLOSE_CURL to NextState(
                State.EXPECT_BIN_OP,
            ),
        ),
    ),
    State.EXPECT_KEY_VALUE_SEPARATOR to StateMap(
        mapOf(
            Token.Type.COLON to NextState(State.OBJECT_VALUE),
        ),
    ),
    State.POST_TRANSFORM to StateMap(
        mapOf(
            Token.Type.OPEN_PAREN to NextState(
                State.ARGUMENT_VALUE,
            ),
            Token.Type.BINARY_OP to NextState(
                State.EXPECT_OPERAND,
            ),
            Token.Type.DOT to NextState(State.TRAVERSE),
            Token.Type.OPEN_BRACKET to NextState(
                State.FILTER,
            ),
            Token.Type.PIPE to NextState(State.EXPECT_TRANSFORM),
        ),
        completable = true,
    ),
    State.POST_TRANSFORM_ARGUMENTS to StateMap(
        mapOf(
            Token.Type.BINARY_OP to NextState(
                State.EXPECT_OPERAND,
            ),
            Token.Type.DOT to NextState(State.TRAVERSE),
            Token.Type.OPEN_BRACKET to NextState(
                State.FILTER,
            ),
            Token.Type.PIPE to NextState(State.EXPECT_TRANSFORM),
        ),
        completable = true,
    ),
    State.IDENTIFIER to StateMap(
        mapOf(
            Token.Type.BINARY_OP to NextState(
                State.EXPECT_OPERAND,
            ),
            Token.Type.DOT to NextState(State.TRAVERSE),
            Token.Type.OPEN_BRACKET to NextState(
                State.FILTER,
            ),
            Token.Type.PIPE to NextState(State.EXPECT_TRANSFORM),
            Token.Type.QUESTION to NextState(
                State.TERNARY_MID,
                ::ternaryStart,
            ),
        ),
        completable = true,
    ),
    State.TRAVERSE to StateMap(
        mapOf(
            Token.Type.IDENTIFIER to NextState(
                State.IDENTIFIER,
            ),
        ),
    ),
    State.FILTER to StateMap(
        subHandler = { parser, node ->
            val expressionNode = FilterExpression(
                expression = node,
                relative = parser.subParser!!.relative,
                subject = parser.cursor,
            )
            parser.placeBeforeCursor(expressionNode)
        },
        endStates = mapOf(
            Token.Type.CLOSE_BRACKET to State.IDENTIFIER,
        ),
    ),
    State.SUB_EXPRESSION to StateMap(
        subHandler = { parser, node ->
            parser.placeAtCursor(node!!)
        },
        endStates = mapOf(
            Token.Type.CLOSE_PAREN to State.EXPECT_BIN_OP,
        ),
    ),
    State.ARGUMENT_VALUE to StateMap(
        subHandler = { parser, node ->
            val cursor = parser.cursor!! as Transformation
            cursor.arguments.add(node!!)
        },
        endStates = mapOf(
            Token.Type.COMMA to State.ARGUMENT_VALUE,
            Token.Type.CLOSE_PAREN to State.EXPECT_BIN_OP,
        ),
    ),
    State.OBJECT_VALUE to StateMap(
        subHandler = { parser, node ->
            val cursor = parser.cursor as ObjectLiteral
            val properties = cursor.properties as MutableMap<String, AstNode>

            properties[parser.currentObjectKey!!] = node!!
        },
        endStates = mapOf(
            Token.Type.COMMA to State.EXPECT_OBJECT_KEY,
            Token.Type.CLOSE_CURL to State.EXPECT_BIN_OP,
        ),
    ),
    State.ARRAY_VALUE to StateMap(
        subHandler = { parser, node ->
            if (node != null) {
                (parser.cursor!! as ArrayLiteral).values.add(node)
            }
        },
        endStates = mapOf(
            Token.Type.COMMA to State.ARRAY_VALUE,
            Token.Type.CLOSE_BRACKET to State.EXPECT_BIN_OP,
        ),
    ),
    State.TERNARY_MID to StateMap(
        subHandler = { parser, node ->
            val cursor = parser.cursor!! as ConditionalExpression
            cursor.consequent = node
        },
        endStates = mapOf(
            Token.Type.COLON to State.TERNARY_END,
        ),
    ),
    State.TERNARY_END to StateMap(
        subHandler = { parser, node ->
            val cursor = parser.cursor!! as ConditionalExpression
            cursor.alternate = node
        },
        completable = true,
    ),
)

private fun objectStart(
    parser: Parser,
    @Suppress("UNUSED_PARAMETER") token: Token,
) {
    val node = ObjectLiteral(
        properties = mutableMapOf(),
    )
    parser.placeAtCursor(node)
}

private fun objectKey(parser: Parser, token: Token) {
    parser.currentObjectKey = token.value.toString()
}

private fun arrayStart(
    parser: Parser,
    @Suppress("UNUSED_PARAMETER") token: Token,
) {
    val node = ArrayLiteral()
    parser.placeAtCursor(node)
}

private fun transform(parser: Parser, token: Token) {
    val node = Transformation(
        name = token.value.toString(),
        subject = parser.cursor,
    )
    parser.placeBeforeCursor(node)
}

private fun ternaryStart(
    parser: Parser,
    @Suppress("UNUSED_PARAMETER") token: Token,
) {
    val node = ConditionalExpression(
        test = parser.tree,
    )
    parser.tree = node
    parser.cursor = node
}
