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 Ps1Core(
    private val context: Context,
    private val buttonMappingManager: ButtonMappingManager? = null
) : EmulatorCore {

    override val consoleType = ConsoleType.PS1

    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()

        // Per-game save directory based on ROM filename
        val romName = File(path).nameWithoutExtension
        savesDir = File(baseSavesDir, romName)
        savesDir.mkdirs()

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

        // Look for core .so
        val corePath = findCorePath()
        if (corePath == null) {
            Log.e(TAG, "PS1 core .so not found. Place pcsx_rearmed_libretro_android.so in ${coresDir.absolutePath}")
            return false
        }

        // Check BIOS (optional for some cores, but recommended)
        val biosFile = File(systemDir, "scph1001.bin")
        if (!biosFile.exists()) {
            Log.w(TAG, "BIOS not found at ${biosFile.absolutePath} — core will use HLE BIOS")
        }

        // Set core options BEFORE loading core - pcsx_rearmed reads them during retro_init
        NativeLibretro.nativeSetCoreOption("pcsx_rearmed_drc", "enabled")
        NativeLibretro.nativeSetCoreOption("pcsx_rearmed_pad1type", "analog")
        NativeLibretro.nativeSetCoreOption("pcsx_rearmed_frameskip_type", "disabled")
        NativeLibretro.nativeSetCoreOption("pcsx_rearmed_region", "auto")
        NativeLibretro.nativeSetCoreOption("pcsx_rearmed_memcard2", "disabled")

        // Pre-set the controller device so it gets applied INSIDE loadCore at the right time
        // (between retro_init and retro_get_system_info, which is what pcsx_rearmed expects)
        NativeLibretro.nativeSetPendingControllerDevice(5) // RETRO_DEVICE_ANALOG

        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) {
        // Convert -1.0..1.0 float to -32768..32767 int16 range
        val ix = (x * 32767).toInt().coerceIn(-32768, 32767).toShort()
        val iy = (y * 32767).toInt().coerceIn(-32768, 32767).toShort()
        NativeLibretro.nativeSetAnalogState(stickIndex, ix, iy)
    }

    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)
    }

    fun releaseSurface() {
        NativeLibretro.nativeReleaseSurface()
    }

    private fun findCorePath(): String? {
        val names = listOf(
            "pcsx_rearmed_libretro_android.so",
            "pcsx_rearmed_libretro.so",
            "mednafen_psx_libretro_android.so",
            "mednafen_psx_libretro.so",
            "swanstation_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 {
        return when (button) {
            GamepadButton.FACE_BOTTOM -> 0   // Cross -> B
            GamepadButton.FACE_RIGHT -> 8    // Circle -> A
            GamepadButton.FACE_LEFT -> 1     // Square -> Y
            GamepadButton.FACE_TOP -> 9      // Triangle -> X
            GamepadButton.DPAD_UP -> 4
            GamepadButton.DPAD_DOWN -> 5
            GamepadButton.DPAD_LEFT -> 6
            GamepadButton.DPAD_RIGHT -> 7
            GamepadButton.L1 -> 10
            GamepadButton.R1 -> 11
            GamepadButton.L2 -> 12
            GamepadButton.R2 -> 13
            GamepadButton.L3 -> 14
            GamepadButton.R3 -> 15
            GamepadButton.SELECT -> 2
            GamepadButton.START -> 3
            else -> -1
        }
    }

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