package de.ladkau.schwertundmagieonpebblecompanionapp

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View

/**
 * A key can send one or more C64 matrix codes simultaneously.
 * The two-code variant is used for shifted cursor keys (e.g. ↑ = LSHIFT + CUR_UD).
 */
data class C64Key(
    val label: String,
    val codes: List<Int>,
    val widthWeight: Float = 1f
) {
    constructor(label: String, code: Int, widthWeight: Float = 1f)
        : this(label, listOf(code), widthWeight)
}

typealias KeyEventListener = (key: C64Key, pressed: Boolean) -> Unit

class C64KeyboardView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : View(context, attrs) {

    var onKeyEvent: KeyEventListener? = null

    private val rows: List<List<C64Key>> = listOf(
        // Row 1 — function keys + DEL / HOME / RUN·STOP
        listOf(
            C64Key("F1",   C64Engine.KEY_F1),
            C64Key("F3",   C64Engine.KEY_F3),
            C64Key("F5",   C64Engine.KEY_F5),
            C64Key("F7",   C64Engine.KEY_F7),
            C64Key("DEL",  C64Engine.KEY_DEL),
            C64Key("HOME", C64Engine.KEY_HOME),
            C64Key("STOP", C64Engine.KEY_RUNSTOP, 1.5f),
        ),
        // Row 2 — numbers
        listOf(
            C64Key("1", C64Engine.KEY_1), C64Key("2", C64Engine.KEY_2),
            C64Key("3", C64Engine.KEY_3), C64Key("4", C64Engine.KEY_4),
            C64Key("5", C64Engine.KEY_5), C64Key("6", C64Engine.KEY_6),
            C64Key("7", C64Engine.KEY_7), C64Key("8", C64Engine.KEY_8),
            C64Key("9", C64Engine.KEY_9), C64Key("0", C64Engine.KEY_0),
        ),
        // Row 3 — Q … P
        listOf(
            C64Key("Q", C64Engine.KEY_Q), C64Key("W", C64Engine.KEY_W),
            C64Key("E", C64Engine.KEY_E), C64Key("R", C64Engine.KEY_R),
            C64Key("T", C64Engine.KEY_T), C64Key("Y", C64Engine.KEY_Y),
            C64Key("U", C64Engine.KEY_U), C64Key("I", C64Engine.KEY_I),
            C64Key("O", C64Engine.KEY_O), C64Key("P", C64Engine.KEY_P),
        ),
        // Row 4 — A … L + RETURN
        listOf(
            C64Key("A", C64Engine.KEY_A), C64Key("S", C64Engine.KEY_S),
            C64Key("D", C64Engine.KEY_D), C64Key("F", C64Engine.KEY_F),
            C64Key("G", C64Engine.KEY_G), C64Key("H", C64Engine.KEY_H),
            C64Key("J", C64Engine.KEY_J), C64Key("K", C64Engine.KEY_K),
            C64Key("L", C64Engine.KEY_L),
            C64Key("RET", C64Engine.KEY_RETURN, 1.5f),
        ),
        // Row 5 — SHIFT + Z … M + SHIFT
        listOf(
            C64Key("SHF", C64Engine.KEY_LSHIFT, 1.5f),
            C64Key("Z", C64Engine.KEY_Z), C64Key("X", C64Engine.KEY_X),
            C64Key("C", C64Engine.KEY_C), C64Key("V", C64Engine.KEY_V),
            C64Key("B", C64Engine.KEY_B), C64Key("N", C64Engine.KEY_N),
            C64Key("M", C64Engine.KEY_M),
            C64Key("SHF", C64Engine.KEY_RSHIFT, 1.5f),
        ),
        // Row 6 — modifiers + SPACE + cursor keys
        // ↑ and ← are composite: LSHIFT + the cursor key
        listOf(
            C64Key("CTRL", C64Engine.KEY_CTRL),
            C64Key("C=",   C64Engine.KEY_CBM),
            C64Key("SPACE", C64Engine.KEY_SPACE, 3f),
            C64Key("←", listOf(C64Engine.KEY_LSHIFT, C64Engine.KEY_CUR_LR)),
            C64Key("→", C64Engine.KEY_CUR_LR),
            C64Key("↑", listOf(C64Engine.KEY_LSHIFT, C64Engine.KEY_CUR_UD)),
            C64Key("↓", C64Engine.KEY_CUR_UD),
        ),
    )

    private val heldKeys = mutableMapOf<Int, C64Key>()

    /* Sticky Shift: tapping either SHF key toggles LSHIFT on/off.
     * When true, the SHF keys are drawn highlighted and LSHIFT stays held. */
    private var shiftActive = false
    private val lshiftKey   = C64Key("SHF", C64Engine.KEY_LSHIFT)

    private fun isShiftKey(key: C64Key) =
        key.codes.size == 1 &&
        (key.codes[0] == C64Engine.KEY_LSHIFT || key.codes[0] == C64Engine.KEY_RSHIFT)

    private val keyPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.parseColor("#2C2C6C")
    }
    private val pressedPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.parseColor("#6464C0")
    }
    private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.WHITE
        textAlign = Paint.Align.CENTER
    }

    private var keyRects: List<List<Pair<RectF, C64Key>>> = emptyList()

    override fun onSizeChanged(w: Int, h: Int, oldW: Int, oldH: Int) {
        super.onSizeChanged(w, h, oldW, oldH)
        buildKeyRects(w.toFloat(), h.toFloat())
    }

    private fun buildKeyRects(totalW: Float, totalH: Float) {
        val rowH = totalH / rows.size
        val pad  = 3f
        keyRects = rows.mapIndexed { rowIdx, row ->
            val totalUnits = row.sumOf { it.widthWeight.toDouble() }.toFloat()
            val unitW = totalW / totalUnits
            var x = 0f
            row.map { key ->
                val kw   = key.widthWeight * unitW
                val rect = RectF(x + pad, rowIdx * rowH + pad, x + kw - pad, (rowIdx + 1) * rowH - pad)
                x += kw
                rect to key
            }
        }
    }

    override fun onDraw(canvas: Canvas) {
        val held = heldKeys.values.toSet()
        textPaint.textSize = (height / rows.size) * 0.38f

        for (row in keyRects) {
            for ((rect, key) in row) {
                val pressed = key in held || (shiftActive && isShiftKey(key))
                canvas.drawRoundRect(rect, 5f, 5f, if (pressed) pressedPaint else keyPaint)
                drawKeyLabel(canvas, rect, key.label)
            }
        }
    }

    private fun drawKeyLabel(canvas: Canvas, rect: RectF, label: String) {
        val lines = label.split("\n")
        val lineH = textPaint.textSize * 1.15f
        val totalTextH = lineH * lines.size
        val startY = rect.centerY() - totalTextH / 2f + textPaint.textSize
        lines.forEachIndexed { i, line ->
            canvas.drawText(line, rect.centerX(), startY + i * lineH, textPaint)
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        val idx = event.actionIndex
        val pid = event.getPointerId(idx)
        val ex  = event.getX(idx)
        val ey  = event.getY(idx)

        when (event.actionMasked) {
            MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
                val key = keyAt(ex, ey) ?: return true
                if (isShiftKey(key)) {
                    // Toggle sticky shift; always use LSHIFT so there's no phantom held key.
                    shiftActive = !shiftActive
                    onKeyEvent?.invoke(lshiftKey, shiftActive)
                } else {
                    heldKeys[pid] = key
                    onKeyEvent?.invoke(key, true)
                }
                invalidate()
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP,
            MotionEvent.ACTION_CANCEL -> {
                val key = heldKeys.remove(pid) ?: return true
                onKeyEvent?.invoke(key, false)
                invalidate()
            }
        }
        return true
    }

    private fun keyAt(x: Float, y: Float): C64Key? {
        for (row in keyRects) {
            for ((rect, key) in row) {
                if (rect.contains(x, y)) return key
            }
        }
        return null
    }
}
