package com.lazy.emulate.emulation.cores

import android.content.Context
import android.util.Log
import android.view.Surface
import com.lazy.emulate.emulation.ConsoleType
import com.lazy.emulate.emulation.DisplayMode
import com.lazy.emulate.emulation.EmulatorCore
import com.lazy.emulate.emulation.NativeLibretro
import com.lazy.emulate.emulation.SaveSlotInfo
import com.lazy.emulate.input.ButtonMappingManager
import com.lazy.emulate.input.GamepadButton
import java.io.File

class SnesCore(
    private val context: Context,
    private val buttonMappingManager: ButtonMappingManager? = null
) : EmulatorCore {

    override val consoleType = ConsoleType.SNES

    override var isRunning: Boolean = false
        private set

    private var buttonState: Int = 0

    private val coresDir = File(context.filesDir, "cores")
    private val systemDir = File(context.filesDir, "system")
    private val baseSavesDir = File(context.filesDir, "saves")
    private lateinit var savesDir: File

    override fun loadRom(path: String): Boolean {
        coresDir.mkdirs()
        systemDir.mkdirs()

        val romName = File(path).nameWithoutExtension
        savesDir = File(baseSavesDir, romName)
        savesDir.mkdirs()

        NativeLibretro.nativeSetSystemDir(systemDir.absolutePath)
        NativeLibretro.nativeSetSaveDir(savesDir.absolutePath)

        val corePath = findCorePath()
        if (corePath == null) {
            Log.e(TAG, "SNES core .so not found. Place snes9x_libretro_android.so in ${coresDir.absolutePath}")
            return false
        }

        if (!NativeLibretro.nativeLoadCore(corePath)) {
            Log.e(TAG, "Failed to load core: $corePath")
            return false
        }

        if (!NativeLibretro.nativeLoadGame(path)) {
            Log.e(TAG, "Failed to load game: $path")
            return false
        }

        Log.i(TAG, "Game loaded successfully: $path")
        return true
    }

    override fun start() {
        isRunning = true
        NativeLibretro.nativeStart()
    }

    override fun pause() {
        isRunning = false
        NativeLibretro.nativePause()
    }

    override fun resume() {
        isRunning = true
        NativeLibretro.nativeResume()
    }

    override fun stop() {
        isRunning = false
        NativeLibretro.nativeStop()
    }

    override fun reset() {
        NativeLibretro.nativeReset()
    }

    override fun setSurface(surface: Surface) {
        NativeLibretro.nativeSetSurface(surface)
    }

    override fun onButtonPressed(button: GamepadButton) {
        val bit = mapButtonToRetroId(button)
        if (bit >= 0) {
            buttonState = buttonState or (1 shl bit)
            NativeLibretro.nativeSetButtonState(buttonState)
        }
    }

    override fun onButtonReleased(button: GamepadButton) {
        val bit = mapButtonToRetroId(button)
        if (bit >= 0) {
            buttonState = buttonState and (1 shl bit).inv()
            NativeLibretro.nativeSetButtonState(buttonState)
        }
    }

    override fun onAnalogStick(x: Float, y: Float, stickIndex: Int) {
        // SNES has no analog sticks
    }

    override fun saveState(slot: Int): Boolean {
        val path = File(savesDir, "state_$slot.sav").absolutePath
        return NativeLibretro.nativeSaveState(path)
    }

    override fun loadState(slot: Int): Boolean {
        val path = File(savesDir, "state_$slot.sav").absolutePath
        return NativeLibretro.nativeLoadState(path)
    }

    override fun deleteState(slot: Int): Boolean {
        val state = File(savesDir, "state_$slot.sav")
        val screenshot = File(savesDir, "state_$slot.jpg")
        val deleted = state.exists() && state.delete()
        if (screenshot.exists()) screenshot.delete()
        return deleted
    }

    override fun getSaveSlotInfo(slot: Int): SaveSlotInfo {
        val file = File(savesDir, "state_$slot.sav")
        val screenshot = File(savesDir, "state_$slot.jpg")
        return SaveSlotInfo(
            slot = slot,
            exists = file.exists(),
            lastModified = if (file.exists()) file.lastModified() else 0L,
            screenshotPath = if (screenshot.exists()) screenshot.absolutePath else null
        )
    }

    override fun getScreenshotPath(slot: Int): String =
        File(savesDir, "state_$slot.jpg").absolutePath

    override fun setAudioEnabled(enabled: Boolean) {
        NativeLibretro.nativeSetAudioEnabled(enabled)
    }

    override fun setFrameSkip(skip: Int) {
        NativeLibretro.nativeSetFrameSkip(skip)
    }

    override fun setDisplayMode(mode: DisplayMode) {
        NativeLibretro.nativeSetDisplayMode(mode.nativeValue)
    }

    private fun findCorePath(): String? {
        val names = listOf(
            "snes9x_libretro_android.so",
            "snes9x_libretro.so",
            "snes9x2010_libretro_android.so",
            "snes9x2010_libretro.so",
            "snes9x2005_libretro_android.so",
            "snes9x2005_libretro.so",
        )
        // Check internal cores dir first
        for (name in names) {
            val file = File(coresDir, name)
            if (file.exists()) return file.absolutePath
        }
        // Extract bundled core from assets
        for (name in names) {
            try {
                context.assets.open("cores/$name").use { input ->
                    val dest = File(coresDir, name)
                    dest.outputStream().use { output -> input.copyTo(output) }
                    dest.setExecutable(true)
                    Log.i(TAG, "Extracted bundled core: $name")
                    return dest.absolutePath
                }
            } catch (_: Exception) {
                // Asset doesn't exist, try next
            }
        }
        // Fallback: auto-import from /sdcard/Games/cores/
        val sdcardCores = File("/sdcard/Games/cores")
        if (sdcardCores.exists()) {
            for (name in names) {
                val sdFile = File(sdcardCores, name)
                if (sdFile.exists()) {
                    val dest = File(coresDir, name)
                    try {
                        sdFile.copyTo(dest, overwrite = true)
                        dest.setExecutable(true)
                        Log.i(TAG, "Imported core from sdcard: $name")
                        return dest.absolutePath
                    } catch (e: Exception) {
                        Log.e(TAG, "Failed to import core: ${e.message}")
                    }
                }
            }
        }
        return null
    }

    private fun mapButtonToRetroId(button: GamepadButton): Int {
        if (buttonMappingManager != null) {
            return buttonMappingManager.getRetroId(consoleType, button)
        }
        return defaultMapButtonToRetroId(button)
    }

    private fun defaultMapButtonToRetroId(button: GamepadButton): Int {
        // SNES: B, A, Y, X, L, R, Select, Start, D-pad
        // Libretro joypad IDs: B=0, Y=1, Select=2, Start=3,
        // Up=4, Down=5, Left=6, Right=7, A=8, X=9, L=10, R=11
        return when (button) {
            GamepadButton.FACE_BOTTOM -> 0   // B
            GamepadButton.FACE_RIGHT -> 8    // A
            GamepadButton.FACE_LEFT -> 1     // Y
            GamepadButton.FACE_TOP -> 9      // X
            GamepadButton.L1 -> 10           // L
            GamepadButton.R1 -> 11           // R
            GamepadButton.DPAD_UP -> 4
            GamepadButton.DPAD_DOWN -> 5
            GamepadButton.DPAD_LEFT -> 6
            GamepadButton.DPAD_RIGHT -> 7
            GamepadButton.SELECT -> 2
            GamepadButton.START -> 3
            else -> -1
        }
    }

    companion object {
        private const val TAG = "SnesCore"
    }
}
