package com.petar.myapplication

import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import android.widget.Button
import android.widget.TextView
import android.app.AlertDialog
import android.widget.Toast

class MainActivity : AppCompatActivity() {

    private lateinit var outputText: TextView
    private lateinit var outputPreview: TextView
    private var currentInput = StringBuilder()
    private var expression = StringBuilder()
    private var shouldResetInput = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)

        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }

        initializeViews()
        setupNumberButtons()
        setupOperatorButtons()
        setupActionButtons()
    }

    private fun showExitDialog() {
        val builder = AlertDialog.Builder(this)
        builder.setTitle("Exit App")
        builder.setMessage("Do you really want to exit?")

        builder.setPositiveButton("Yes") { dialog, which ->
            // Exit the app
            finishAffinity()
        }

        builder.setNegativeButton("No") { dialog, which ->
            dialog.dismiss()
            // Optional: Show a message or do nothing
            Toast.makeText(this, "Continue using app", Toast.LENGTH_SHORT).show()
        }

        builder.setCancelable(true) // Allows dismissing by tapping outside

        val dialog = builder.create()
        dialog.show()
    }

    override fun onBackPressed() {
        showExitDialog()
    }

    private fun initializeViews() {
        outputText = findViewById(R.id.outputText)
        outputPreview = findViewById(R.id.outputPreview)
        updateDisplay()
    }

    private fun setupNumberButtons() {
        val numberButtons = listOf(
            R.id.button0, R.id.button1, R.id.button2, R.id.button3, R.id.button4,
            R.id.button5, R.id.button6, R.id.button7, R.id.button8, R.id.button9
        )

        numberButtons.forEach { buttonId ->
            findViewById<Button>(buttonId).setOnClickListener {
                onNumberClicked((it as Button).text.toString())
            }
        }

        findViewById<Button>(R.id.buttonDot).setOnClickListener {
            onDecimalClicked()
        }
    }

    private fun setupOperatorButtons() {
        findViewById<Button>(R.id.buttonPlus).setOnClickListener {
            onOperatorClicked("+")
        }
        findViewById<Button>(R.id.buttonMinus).setOnClickListener {
            onOperatorClicked("-")
        }
        findViewById<Button>(R.id.buttonMultiply).setOnClickListener {
            onOperatorClicked("×")
        }
        findViewById<Button>(R.id.buttonDivide).setOnClickListener {
            onOperatorClicked("÷")
        }
        findViewById<Button>(R.id.buttonPercent).setOnClickListener {
            onPercentClicked()
        }
    }

    private fun setupActionButtons() {
        findViewById<Button>(R.id.buttonEquals).setOnClickListener {
            onEqualsClicked()
        }

        findViewById<Button>(R.id.buttonClear).setOnClickListener {
            onClearClicked()
        }

        findViewById<Button>(R.id.buttonBackspace).setOnClickListener {
            onBackspaceClicked()
        }
    }

    private fun onNumberClicked(number: String) {
        if (shouldResetInput) {
            currentInput.clear()
            shouldResetInput = false
        }

        if (currentInput.toString() == "0") {
            currentInput.clear()
        }

        currentInput.append(number)
        updateDisplay()
        updatePreview()
    }

    private fun onDecimalClicked() {
        if (shouldResetInput) {
            currentInput.clear()
            shouldResetInput = false
        }

        if (currentInput.isEmpty()) {
            currentInput.append("0.")
        } else if (!currentInput.contains(".")) {
            currentInput.append(".")
        }
        updateDisplay()
        updatePreview()
    }

    private fun onOperatorClicked(operator: String) {
        if (currentInput.isNotEmpty()) {
            expression.append(currentInput)
            expression.append(operator)
            currentInput.clear()
            updateDisplay()
            updatePreview()
        } else if (expression.isNotEmpty() && expression.last().toString() in listOf("+", "-", "×", "÷")) {
            expression.deleteCharAt(expression.length - 1)
            expression.append(operator)
            updateDisplay()
            updatePreview()
        }
    }

    private fun onPercentClicked() {
        if (currentInput.isNotEmpty()) {
            val number = currentInput.toString()
            expression.append(number)
            expression.append("%")
            currentInput.clear()
            updateDisplay()
            updatePreview()
        }
    }

    private fun onEqualsClicked() {
        if (expression.isNotEmpty() || currentInput.isNotEmpty()) {
            if (currentInput.isNotEmpty()) {
                expression.append(currentInput)
            }

            val result = evaluateExpression(expression.toString())

            currentInput.clear()
            currentInput.append(formatResult(result))
            expression.clear()
            shouldResetInput = true
            updateDisplay()
            outputPreview.text = ""
        }
    }

    private fun onClearClicked() {
        currentInput.clear()
        expression.clear()
        shouldResetInput = false
        updateDisplay()
        outputPreview.text = ""
    }

    private fun onBackspaceClicked() {
        if (currentInput.isNotEmpty()) {
            currentInput.deleteCharAt(currentInput.length - 1)
            updateDisplay()
            updatePreview()
        } else if (expression.isNotEmpty()) {
            val lastChar = expression.last()
            expression.deleteCharAt(expression.length - 1)
            if (lastChar.toString() in listOf("+", "-", "×", "÷", "%")) {
                updateDisplay()
                updatePreview()
            } else {
                val temp = StringBuilder()
                while (expression.isNotEmpty() && expression.last().toString() !in listOf("+", "-", "×", "÷", "%")) {
                    temp.insert(0, expression.last())
                    expression.deleteCharAt(expression.length - 1)
                }
                currentInput.append(temp)
                updateDisplay()
                updatePreview()
            }
        }
    }

    private fun evaluateExpression(expr: String): Double {
        try {
            // First, handle percentages by converting them to decimal values
            val processedExpr = processPercentages(expr)
                .replace("×", "*")
                .replace("÷", "/")

            return evaluate(processedExpr)
        } catch (e: Exception) {
            return Double.NaN
        }
    }

    private fun processPercentages(expr: String): String {
        val result = StringBuilder()
        var i = 0
        while (i < expr.length) {
            if (expr[i] == '%') {
                // Find the start of the number before %
                var j = i - 1
                while (j >= 0 && (expr[j].isDigit() || expr[j] == '.')) {
                    j--
                }
                val numberStr = expr.substring(j + 1, i)
                if (numberStr.isNotEmpty()) {
                    val number = numberStr.toDouble() / 100.0
                    result.delete(result.length - numberStr.length, result.length)
                    result.append("($number)") // Wrap in parentheses to handle precedence
                }
            } else {
                result.append(expr[i])
            }
            i++
        }
        return result.toString()
    }

    // Recursive descent parser based on stackoverflow code
    // https://stackoverflow.com/questions/71560324/parsing-arithmetic-string-operations-in-kotlin
    private fun evaluate(str: String): Double {
        data class Data(val rest: List<Char>, val value: Double)

        return object {
            fun parse(chars: List<Char>): Double {
                return getExpression(chars.filter { it != ' ' })
                    .also { if (it.rest.isNotEmpty()) throw RuntimeException("Unexpected character: ${it.rest.first()}") }
                    .value
            }

            private fun getExpression(chars: List<Char>): Data {
                var (rest, carry) = getTerm(chars)
                while (true) {
                    when {
                        rest.firstOrNull() == '+' -> rest = getTerm(rest.drop(1)).also { carry += it.value }.rest
                        rest.firstOrNull() == '-' -> rest = getTerm(rest.drop(1)).also { carry -= it.value }.rest
                        else -> return Data(rest, carry)
                    }
                }
            }

            private fun getTerm(chars: List<Char>): Data {
                var (rest, carry) = getFactor(chars)
                while (true) {
                    when {
                        rest.firstOrNull() == '*' -> rest = getFactor(rest.drop(1)).also { carry *= it.value }.rest
                        rest.firstOrNull() == '/' -> rest = getFactor(rest.drop(1)).also { carry /= it.value }.rest
                        else -> return Data(rest, carry)
                    }
                }
            }

            private fun getFactor(chars: List<Char>): Data {
                return when (val char = chars.firstOrNull()) {
                    '+' -> getFactor(chars.drop(1)).let { Data(it.rest, +it.value) }
                    '-' -> getFactor(chars.drop(1)).let { Data(it.rest, -it.value) }
                    '(' -> getParenthesizedExpression(chars.drop(1))
                    in '0'..'9', '.' -> getNumber(chars)
                    else -> throw RuntimeException("Unexpected character: $char")
                }
            }

            private fun getParenthesizedExpression(chars: List<Char>): Data {
                return getExpression(chars)
                    .also { if (it.rest.firstOrNull() != ')') throw RuntimeException("Missing closing parenthesis") }
                    .let { Data(it.rest.drop(1), it.value) }
            }

            private fun getNumber(chars: List<Char>): Data {
                val s = chars.takeWhile { it.isDigit() || it == '.' }.joinToString("")
                return Data(chars.drop(s.length), s.toDouble())
            }
        }.parse(str.toList())
    }

    private fun formatResult(result: Double): String {
        return if (result.isNaN()) {
            "Error"
        } else if (result == result.toLong().toDouble()) {
            result.toLong().toString()
        } else {
            String.format("%.8f", result).trimEnd('0').trimEnd('.')
        }
    }

    private fun updateDisplay() {
        val displayText = buildString {
            if (expression.isNotEmpty()) {
                append(expression)
            }
            if (currentInput.isNotEmpty()) {
                append(currentInput)
            }
            if (isEmpty()) {
                append("0")
            }
        }
        outputText.text = displayText
    }

    private fun updatePreview() {
        if (expression.isNotEmpty() || currentInput.isNotEmpty()) {
            try {
                val previewExpr = buildString {
                    append(expression)
                    if (currentInput.isNotEmpty()) {
                        append(currentInput)
                    }
                }.toString()

                val cleanedExpr = cleanExpressionForPreview(previewExpr)

                if (cleanedExpr.isNotEmpty() && isValidExpression(cleanedExpr)) {
                    val result = evaluateExpression(cleanedExpr)
                    outputPreview.text = "= ${formatResult(result)}"
                } else {
                    outputPreview.text = ""
                }
            } catch (e: Exception) {
                outputPreview.text = ""
            }
        } else {
            outputPreview.text = ""
        }
    }

    private fun cleanExpressionForPreview(expr: String): String {
        if (expr.isEmpty()) return expr

        var cleaned = expr

        // Remove leading operators
        while (cleaned.isNotEmpty() && cleaned.first().toString() in listOf("+", "-", "×", "÷", "%")) {
            cleaned = cleaned.substring(1)
        }

        // Remove trailing operators (except % which is part of a number)
        while (cleaned.isNotEmpty() && cleaned.last().toString() in listOf("+", "-", "×", "÷")) {
            cleaned = cleaned.substring(0, cleaned.length - 1)
        }

        return cleaned
    }

    private fun isValidExpression(expr: String): Boolean {
        if (expr.isEmpty()) return false

        val lastChar = expr.last().toString()
        if (lastChar in listOf("+", "-", "×", "÷")) {
            return false
        }

        // Check for consecutive operators
        for (i in 0 until expr.length - 1) {
            val current = expr[i].toString()
            val next = expr[i + 1].toString()
            if (current in listOf("+", "×", "÷") && next in listOf("+", "-", "×", "÷", "%")) {
                return false
            }
        }

        return true
    }
}