package com.pixelmintdrop.game

import android.os.SystemClock
import android.view.InputDevice
import android.view.KeyEvent
import android.view.MotionEvent
import android.util.Log
import android.content.Context
import android.os.Build
import android.os.VibrationEffect
import android.os.Handler
import android.os.Looper

/**
 * GamepadController handles gamepad input for the pixelmintdrop game.
 * Supports multiple gamepad types including:
 * - Microsoft Xbox controllers
 * - Sony PlayStation controllers
 * - Nintendo Switch controllers
 * - Backbone controllers
 */
class GamepadController(
    private val gameView: GameView
) {
    companion object {
        private const val TAG = "GamepadController"
        
        // Deadzone for analog sticks (normalized value 0.0-1.0)
        private const val STICK_DEADZONE = 0.30f
        
        // Cooldown times for responsive input without repeating too quickly
        private const val MOVE_COOLDOWN_MS = 100L
        private const val ROTATION_COOLDOWN_MS = 150L
        private const val HARD_DROP_COOLDOWN_MS = 200L
        private const val HOLD_COOLDOWN_MS = 250L
        
        // Continuous movement repeat delay
        private const val CONTINUOUS_MOVEMENT_DELAY_MS = 100L
        
        // Rumble patterns
        private const val RUMBLE_MOVE_DURATION_MS = 20L
        private const val RUMBLE_ROTATE_DURATION_MS = 30L
        private const val RUMBLE_HARD_DROP_DURATION_MS = 100L
        private const val RUMBLE_LINE_CLEAR_DURATION_MS = 150L
        
        // Check if device is a gamepad
        fun isGamepad(device: InputDevice?): Boolean {
            if (device == null) return false
            
            // Check for gamepad via input device sources
            val sources = device.sources
            return (sources and InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD ||
                   (sources and InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK
        }
        
        // Get a list of all connected gamepads
        fun getGamepads(): List<InputDevice> {
            val gamepads = mutableListOf<InputDevice>()
            val deviceIds = InputDevice.getDeviceIds()
            
            for (deviceId in deviceIds) {
                val device = InputDevice.getDevice(deviceId)
                if (device != null && isGamepad(device)) {
                    gamepads.add(device)
                }
            }
            
            return gamepads
        }
        
        // Check if any gamepad is connected
        fun isGamepadConnected(): Boolean {
            return getGamepads().isNotEmpty()
        }
        
        // Get the name of the first connected gamepad
        fun getConnectedGamepadName(): String? {
            val gamepads = getGamepads()
            if (gamepads.isEmpty()) return null
            return gamepads.first().name
        }
        
        // Get information about all connected gamepads
        fun getConnectedGamepadsInfo(): List<String> {
            return getGamepads().map { it.name }
        }
        
        // Check if device supports vibration
        fun supportsVibration(device: InputDevice?): Boolean {
            if (device == null) return false
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return false
            
            return device.vibratorManager?.vibratorIds?.isNotEmpty() ?: false
        }
    }
    
    // Timestamps for cooldowns
    private var lastMoveTime = 0L
    private var lastRotationTime = 0L
    private var lastHardDropTime = 0L
    private var lastHoldTime = 0L
    
    // Track current directional input state
    private var isMovingLeft = false
    private var isMovingRight = false
    private var isMovingDown = false
    
    // Handler for continuous movement
    private val handler = Handler(Looper.getMainLooper())
    private val moveLeftRunnable = object : Runnable {
        override fun run() {
            if (isMovingLeft && gameView.isActive()) {
                gameView.moveLeft()
                vibrateForPieceMove()
                handler.postDelayed(this, CONTINUOUS_MOVEMENT_DELAY_MS)
            }
        }
    }
    
    private val moveRightRunnable = object : Runnable {
        override fun run() {
            if (isMovingRight && gameView.isActive()) {
                gameView.moveRight()
                vibrateForPieceMove()
                handler.postDelayed(this, CONTINUOUS_MOVEMENT_DELAY_MS)
            }
        }
    }
    
    private val moveDownRunnable = object : Runnable {
        override fun run() {
            if (isMovingDown && gameView.isActive()) {
                gameView.softDrop()
                vibrateForPieceMove()
                handler.postDelayed(this, CONTINUOUS_MOVEMENT_DELAY_MS)
            }
        }
    }
    
    // Callback interfaces
    interface GamepadConnectionListener {
        fun onGamepadConnected(gamepadName: String)
        fun onGamepadDisconnected(gamepadName: String)
    }
    
    interface GamepadMenuListener {
        fun onPauseRequested()
    }
    
    interface GamepadNavigationListener {
        fun onMenuUp()
        fun onMenuDown()
        fun onMenuSelect()
        fun onMenuLeft()
        fun onMenuRight()
    }
    
    // Listeners
    private var connectionListener: GamepadConnectionListener? = null
    private var menuListener: GamepadMenuListener? = null
    private var navigationListener: GamepadNavigationListener? = null
    
    // Currently active gamepad for rumble
    private var activeGamepad: InputDevice? = null
    
    /**
     * Set a listener for gamepad connection events
     */
    fun setGamepadConnectionListener(listener: GamepadConnectionListener) {
        connectionListener = listener
    }
    
    /**
     * Set a listener for gamepad menu events (pause/start button)
     */
    fun setGamepadMenuListener(listener: GamepadMenuListener) {
        menuListener = listener
    }
    
    /**
     * Set a listener for gamepad navigation events
     */
    fun setGamepadNavigationListener(listener: GamepadNavigationListener) {
        navigationListener = listener
    }
    
    /**
     * Function to check for newly connected gamepads.
     * Call this periodically from the activity to detect connection changes.
     */
    fun checkForGamepadChanges(context: Context) {
        // Implementation would track previous and current gamepads
        // and notify through the connectionListener
        // This would be called from the activity's onResume or via a handler
    }
    
    /**
     * Vibrate the gamepad if supported
     */
    fun vibrateGamepad(durationMs: Long, amplitude: Int) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return
        
        val gamepad = activeGamepad ?: return
        
        if (supportsVibration(gamepad)) {
            try {
                val vibrator = gamepad.vibratorManager
                val vibratorIds = vibrator.vibratorIds
                
                if (vibratorIds.isNotEmpty()) {
                    val effect = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        VibrationEffect.createOneShot(durationMs, amplitude)
                    } else {
                        // For older devices, fall back to a simple vibration
                        VibrationEffect.createOneShot(durationMs, VibrationEffect.DEFAULT_AMPLITUDE)
                    }
                    
                    // Create combined vibration for Android S+
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                        val combinedVibration = android.os.CombinedVibration.createParallel(effect)
                        vibrator.vibrate(combinedVibration)
                    }
                }
            } catch (e: Exception) {
                Log.e(TAG, "Error vibrating gamepad", e)
            }
        }
    }
    
    /**
     * Vibrate for piece movement
     */
    fun vibrateForPieceMove() {
        vibrateGamepad(RUMBLE_MOVE_DURATION_MS, 50)
    }
    
    /**
     * Vibrate for piece rotation
     */
    fun vibrateForPieceRotation() {
        vibrateGamepad(RUMBLE_ROTATE_DURATION_MS, 80)
    }
    
    /**
     * Vibrate for hard drop
     */
    fun vibrateForHardDrop() {
        vibrateGamepad(RUMBLE_HARD_DROP_DURATION_MS, 150)
    }
    
    /**
     * Vibrate for line clear
     */
    fun vibrateForLineClear(lineCount: Int) {
        val amplitude = when (lineCount) {
            1 -> 100
            2 -> 150
            3 -> 200
            else -> 255 // For quad (4 lines)
        }
        vibrateGamepad(RUMBLE_LINE_CLEAR_DURATION_MS, amplitude)
    }
    
    /**
     * Process a key event from a gamepad
     * @return true if the event was handled, false otherwise
     */
    fun handleKeyEvent(keyCode: Int, event: KeyEvent): Boolean {
        // Skip if game is not active but handle menu navigation
        if (!gameView.isActive()) {
            // Handle menu navigation even when game is not active
            if (event.action == KeyEvent.ACTION_DOWN) {
                when (keyCode) {
                    KeyEvent.KEYCODE_DPAD_UP -> {
                        navigationListener?.onMenuUp()
                        return true
                    }
                    KeyEvent.KEYCODE_DPAD_DOWN -> {
                        navigationListener?.onMenuDown()
                        return true
                    }
                    KeyEvent.KEYCODE_DPAD_LEFT -> {
                        navigationListener?.onMenuLeft()
                        return true
                    }
                    KeyEvent.KEYCODE_DPAD_RIGHT -> {
                        navigationListener?.onMenuRight()
                        return true
                    }
                    KeyEvent.KEYCODE_BUTTON_A, 
                    KeyEvent.KEYCODE_DPAD_CENTER -> {
                        navigationListener?.onMenuSelect()
                        return true
                    }
                    KeyEvent.KEYCODE_BUTTON_B -> {
                        // B button can be used to go back/cancel
                        menuListener?.onPauseRequested()
                        return true
                    }
                }
            }
            
            // Handle menu/start button for pause menu
            if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_BUTTON_START) {
                menuListener?.onPauseRequested()
                return true
            }
            
            return false
        }
        
        val device = event.device
        if (!isGamepad(device)) return false
        
        // Update active gamepad for rumble
        activeGamepad = device
        
        val currentTime = SystemClock.uptimeMillis()
        
        when (event.action) {
            KeyEvent.ACTION_DOWN -> {
                when (keyCode) {
                    // D-pad and analog movement
                    KeyEvent.KEYCODE_DPAD_LEFT -> {
                        if (!isMovingLeft) {
                            gameView.moveLeft()
                            vibrateForPieceMove()
                            lastMoveTime = currentTime
                            isMovingLeft = true
                            // Start continuous movement after initial input
                            handler.postDelayed(moveLeftRunnable, CONTINUOUS_MOVEMENT_DELAY_MS)
                            return true
                        }
                    }
                    KeyEvent.KEYCODE_DPAD_RIGHT -> {
                        if (!isMovingRight) {
                            gameView.moveRight()
                            vibrateForPieceMove()
                            lastMoveTime = currentTime
                            isMovingRight = true
                            // Start continuous movement after initial input
                            handler.postDelayed(moveRightRunnable, CONTINUOUS_MOVEMENT_DELAY_MS)
                            return true
                        }
                    }
                    KeyEvent.KEYCODE_DPAD_DOWN -> {
                        if (!isMovingDown) {
                            gameView.softDrop()
                            vibrateForPieceMove()
                            lastMoveTime = currentTime
                            isMovingDown = true
                            // Start continuous movement after initial input
                            handler.postDelayed(moveDownRunnable, CONTINUOUS_MOVEMENT_DELAY_MS)
                            return true
                        }
                    }
                    KeyEvent.KEYCODE_DPAD_UP -> {
                        if (currentTime - lastHardDropTime > HARD_DROP_COOLDOWN_MS) {
                            gameView.hardDrop()
                            vibrateForHardDrop()
                            lastHardDropTime = currentTime
                            return true
                        }
                    }
                    // Start button (pause)
                    KeyEvent.KEYCODE_BUTTON_START -> {
                        menuListener?.onPauseRequested()
                        return true
                    }
                    // Rotation buttons - supporting multiple buttons for different controllers
                    KeyEvent.KEYCODE_BUTTON_A,
                    KeyEvent.KEYCODE_BUTTON_X -> {
                        if (currentTime - lastRotationTime > ROTATION_COOLDOWN_MS) {
                            gameView.rotate()
                            vibrateForPieceRotation()
                            lastRotationTime = currentTime
                            return true
                        }
                    }
                    KeyEvent.KEYCODE_BUTTON_B -> {
                        if (currentTime - lastRotationTime > ROTATION_COOLDOWN_MS) {
                            gameView.rotateCounterClockwise()
                            vibrateForPieceRotation()
                            lastRotationTime = currentTime
                            return true
                        }
                    }
                    // Hold piece buttons
                    KeyEvent.KEYCODE_BUTTON_Y,
                    KeyEvent.KEYCODE_BUTTON_L1,
                    KeyEvent.KEYCODE_BUTTON_R1 -> {
                        if (currentTime - lastHoldTime > HOLD_COOLDOWN_MS) {
                            gameView.holdPiece()
                            vibrateForPieceRotation()
                            lastHoldTime = currentTime
                            return true
                        }
                    }
                }
            }
            KeyEvent.ACTION_UP -> {
                when (keyCode) {
                    KeyEvent.KEYCODE_DPAD_LEFT -> {
                        isMovingLeft = false
                        handler.removeCallbacks(moveLeftRunnable)
                        return true
                    }
                    KeyEvent.KEYCODE_DPAD_RIGHT -> {
                        isMovingRight = false
                        handler.removeCallbacks(moveRightRunnable)
                        return true
                    }
                    KeyEvent.KEYCODE_DPAD_DOWN -> {
                        isMovingDown = false
                        handler.removeCallbacks(moveDownRunnable)
                        return true
                    }
                }
            }
        }
        
        return false
    }
    
    /**
     * Process generic motion events (for analog sticks)
     * @return true if the event was handled, false otherwise
     */
    fun handleMotionEvent(event: MotionEvent): Boolean {
        // Skip if game is not active
        if (!gameView.isActive()) return false
        
        val device = event.device
        if (!isGamepad(device)) return false
        
        // Update active gamepad for rumble
        activeGamepad = device
        
        val currentTime = SystemClock.uptimeMillis()
        
        // Process left analog stick
        val axisX = event.getAxisValue(MotionEvent.AXIS_X)
        val axisY = event.getAxisValue(MotionEvent.AXIS_Y)
        
        // Apply deadzone
        if (Math.abs(axisX) > STICK_DEADZONE) {
            if (axisX > 0 && !isMovingRight) {
                gameView.moveRight()
                vibrateForPieceMove()
                lastMoveTime = currentTime
                isMovingRight = true
                isMovingLeft = false
                
                // Start continuous movement after initial input
                handler.removeCallbacks(moveLeftRunnable)
                handler.postDelayed(moveRightRunnable, CONTINUOUS_MOVEMENT_DELAY_MS)
                return true
            } else if (axisX < 0 && !isMovingLeft) {
                gameView.moveLeft()
                vibrateForPieceMove()
                lastMoveTime = currentTime
                isMovingLeft = true
                isMovingRight = false
                
                // Start continuous movement after initial input
                handler.removeCallbacks(moveRightRunnable)
                handler.postDelayed(moveLeftRunnable, CONTINUOUS_MOVEMENT_DELAY_MS)
                return true
            }
        } else {
            // Reset horizontal movement flags when stick returns to center
            isMovingLeft = false
            isMovingRight = false
            handler.removeCallbacks(moveLeftRunnable)
            handler.removeCallbacks(moveRightRunnable)
        }
        
        if (Math.abs(axisY) > STICK_DEADZONE) {
            if (axisY > 0 && !isMovingDown) {
                gameView.softDrop()
                vibrateForPieceMove()
                lastMoveTime = currentTime
                isMovingDown = true
                
                // Start continuous movement after initial input
                handler.postDelayed(moveDownRunnable, CONTINUOUS_MOVEMENT_DELAY_MS)
                return true
            }
        } else {
            // Reset vertical movement flag when stick returns to center
            isMovingDown = false
            handler.removeCallbacks(moveDownRunnable)
        }
        
        // Check right analog stick for rotation
        val axisZ = event.getAxisValue(MotionEvent.AXIS_Z)
        val axisRZ = event.getAxisValue(MotionEvent.AXIS_RZ)
        
        if (Math.abs(axisZ) > STICK_DEADZONE || Math.abs(axisRZ) > STICK_DEADZONE) {
            if (currentTime - lastRotationTime > ROTATION_COOLDOWN_MS) {
                gameView.rotate()
                vibrateForPieceRotation()
                lastRotationTime = currentTime
                return true
            }
        }
        
        return false
    }
} 