package org.openrs2.deob.ast.transform

import com.github.javaparser.ast.CompilationUnit
import com.github.javaparser.ast.expr.AssignExpr
import com.github.javaparser.ast.expr.BinaryExpr
import com.github.javaparser.ast.expr.Expression
import com.github.javaparser.ast.expr.IntegerLiteralExpr
import com.github.javaparser.ast.expr.LongLiteralExpr
import com.github.javaparser.ast.expr.UnaryExpr
import com.github.javaparser.ast.stmt.ExpressionStmt
import org.openrs2.deob.ast.Library
import org.openrs2.deob.ast.LibraryGroup
import org.openrs2.deob.ast.util.walk
import javax.inject.Singleton

@Singleton
public class IdentityTransformer : Transformer() {
    override fun transformUnit(group: LibraryGroup, library: Library, unit: CompilationUnit) {
        unit.walk { expr: BinaryExpr ->
            @Suppress("NON_EXHAUSTIVE_WHEN")
            when (expr.operator) {
                BinaryExpr.Operator.PLUS -> {
                    if (expr.left.isZero()) {
                        // 0 + x => x
                        expr.replace(expr.right)
                    } else if (expr.right.isZero()) {
                        // x + 0 => x
                        expr.replace(expr.left)
                    }
                }
                BinaryExpr.Operator.MINUS -> {
                    if (expr.left.isZero()) {
                        // 0 - x => -x
                        expr.replace(UnaryExpr(expr.right, UnaryExpr.Operator.MINUS))
                    } else if (expr.right.isZero()) {
                        // x - 0 => x
                        expr.replace(expr.left)
                    }
                }
                BinaryExpr.Operator.MULTIPLY -> {
                    if (expr.left.isOne()) {
                        // 1 * x => x
                        expr.replace(expr.right)
                    } else if (expr.right.isOne()) {
                        // x * 1 => x
                        expr.replace(expr.left)
                    }
                }
                BinaryExpr.Operator.DIVIDE -> {
                    if (expr.right.isOne()) {
                        // x / 1 => x
                        expr.replace(expr.left)
                    }
                }
            }
        }

        unit.walk { expr: AssignExpr ->
            val identity = when (expr.operator) {
                // x += 0, x -= 0
                AssignExpr.Operator.PLUS, AssignExpr.Operator.MINUS -> expr.value.isZero()
                // x *= 1, x /= 1
                AssignExpr.Operator.MULTIPLY, AssignExpr.Operator.DIVIDE -> expr.value.isOne()
                else -> false
            }

            if (!identity) {
                return@walk
            }

            expr.parentNode.ifPresent { parent ->
                if (parent is ExpressionStmt) {
                    parent.remove()
                } else {
                    expr.replace(expr.target.clone())
                }
            }
        }
    }

    private fun Expression.isZero(): Boolean {
        return when (this) {
            is IntegerLiteralExpr -> asNumber() == 0
            is LongLiteralExpr -> asNumber() == 0L
            else -> false
        }
    }

    private fun Expression.isOne(): Boolean {
        return when (this) {
            is IntegerLiteralExpr -> asNumber() == 1
            is LongLiteralExpr -> asNumber() == 1L
            else -> false
        }
    }
}
