/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.react.uimanager.events

import android.view.KeyEvent as AndroidKeyEvent
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap

internal abstract class KeyEvent(
    surfaceId: Int,
    viewTag: Int,
    keyEvent: AndroidKeyEvent,
) : Event<KeyEvent>(surfaceId, viewTag) {

  // Extract all needed data from keyEvent immediately to avoid storing the AndroidKeyEvent itself
  private val keyCode: Int = keyEvent.keyCode
  private val unicodeChar: Int = keyEvent.unicodeChar
  private val isAltPressed: Boolean = keyEvent.isAltPressed
  private val isCtrlPressed: Boolean = keyEvent.isCtrlPressed
  private val isMetaPressed: Boolean = keyEvent.isMetaPressed
  private val isShiftPressed: Boolean = keyEvent.isShiftPressed

  override fun canCoalesce(): Boolean = false

  override fun getEventCategory(): Int = EventCategoryDef.DISCRETE

  override fun getEventData(): WritableMap {
    val eventData = Arguments.createMap()

    eventData.putInt("target", viewTag)

    // W3C KeyboardEvent properties
    eventData.putString("key", getKeyString())
    eventData.putString("code", getCodeString())

    // Modifier keys
    eventData.putBoolean("altKey", isAltPressed)
    eventData.putBoolean("ctrlKey", isCtrlPressed)
    eventData.putBoolean("metaKey", isMetaPressed)
    eventData.putBoolean("shiftKey", isShiftPressed)

    // Additional properties
    eventData.putDouble("timestamp", timestampMs.toDouble())

    return eventData
  }

  private fun getKeyString(): String {
    return when {
      unicodeChar != 0 && !Character.isISOControl(unicodeChar) -> unicodeChar.toChar().toString()
      else -> KEY_NAME_MAP[keyCode] ?: UNIDENTIFIED
    }
  }

  private fun getCodeString(): String {
    return CODE_MAP[keyCode] ?: UNIDENTIFIED
  }

  internal companion object {
    private const val UNIDENTIFIED = "Unidentified"

    private val CODE_MAP: Map<Int, String> by
        lazy(LazyThreadSafetyMode.PUBLICATION) {
          mapOf(
              // Letter keys
              AndroidKeyEvent.KEYCODE_A to "KeyA",
              AndroidKeyEvent.KEYCODE_B to "KeyB",
              AndroidKeyEvent.KEYCODE_C to "KeyC",
              AndroidKeyEvent.KEYCODE_D to "KeyD",
              AndroidKeyEvent.KEYCODE_E to "KeyE",
              AndroidKeyEvent.KEYCODE_F to "KeyF",
              AndroidKeyEvent.KEYCODE_G to "KeyG",
              AndroidKeyEvent.KEYCODE_H to "KeyH",
              AndroidKeyEvent.KEYCODE_I to "KeyI",
              AndroidKeyEvent.KEYCODE_J to "KeyJ",
              AndroidKeyEvent.KEYCODE_K to "KeyK",
              AndroidKeyEvent.KEYCODE_L to "KeyL",
              AndroidKeyEvent.KEYCODE_M to "KeyM",
              AndroidKeyEvent.KEYCODE_N to "KeyN",
              AndroidKeyEvent.KEYCODE_O to "KeyO",
              AndroidKeyEvent.KEYCODE_P to "KeyP",
              AndroidKeyEvent.KEYCODE_Q to "KeyQ",
              AndroidKeyEvent.KEYCODE_R to "KeyR",
              AndroidKeyEvent.KEYCODE_S to "KeyS",
              AndroidKeyEvent.KEYCODE_T to "KeyT",
              AndroidKeyEvent.KEYCODE_U to "KeyU",
              AndroidKeyEvent.KEYCODE_V to "KeyV",
              AndroidKeyEvent.KEYCODE_W to "KeyW",
              AndroidKeyEvent.KEYCODE_X to "KeyX",
              AndroidKeyEvent.KEYCODE_Y to "KeyY",
              AndroidKeyEvent.KEYCODE_Z to "KeyZ",
              // Digit keys
              AndroidKeyEvent.KEYCODE_0 to "Digit0",
              AndroidKeyEvent.KEYCODE_1 to "Digit1",
              AndroidKeyEvent.KEYCODE_2 to "Digit2",
              AndroidKeyEvent.KEYCODE_3 to "Digit3",
              AndroidKeyEvent.KEYCODE_4 to "Digit4",
              AndroidKeyEvent.KEYCODE_5 to "Digit5",
              AndroidKeyEvent.KEYCODE_6 to "Digit6",
              AndroidKeyEvent.KEYCODE_7 to "Digit7",
              AndroidKeyEvent.KEYCODE_8 to "Digit8",
              AndroidKeyEvent.KEYCODE_9 to "Digit9",
              // Special keys
              AndroidKeyEvent.KEYCODE_ENTER to "Enter",
              AndroidKeyEvent.KEYCODE_SPACE to "Space",
              AndroidKeyEvent.KEYCODE_TAB to "Tab",
              AndroidKeyEvent.KEYCODE_DEL to "Backspace",
              AndroidKeyEvent.KEYCODE_ESCAPE to "Escape",
              // Modifier keys
              AndroidKeyEvent.KEYCODE_SHIFT_LEFT to "ShiftLeft",
              AndroidKeyEvent.KEYCODE_SHIFT_RIGHT to "ShiftRight",
              AndroidKeyEvent.KEYCODE_CTRL_LEFT to "ControlLeft",
              AndroidKeyEvent.KEYCODE_CTRL_RIGHT to "ControlRight",
              AndroidKeyEvent.KEYCODE_ALT_LEFT to "AltLeft",
              AndroidKeyEvent.KEYCODE_ALT_RIGHT to "AltRight",
              AndroidKeyEvent.KEYCODE_META_LEFT to "MetaLeft",
              AndroidKeyEvent.KEYCODE_META_RIGHT to "MetaRight",
              // Arrow keys
              AndroidKeyEvent.KEYCODE_DPAD_UP to "ArrowUp",
              AndroidKeyEvent.KEYCODE_DPAD_DOWN to "ArrowDown",
              AndroidKeyEvent.KEYCODE_DPAD_LEFT to "ArrowLeft",
              AndroidKeyEvent.KEYCODE_DPAD_RIGHT to "ArrowRight",
              AndroidKeyEvent.KEYCODE_DPAD_CENTER to "Enter",
          )
        }

    private val KEY_NAME_MAP: Map<Int, String> by
        lazy(LazyThreadSafetyMode.PUBLICATION) {
          mapOf(
              AndroidKeyEvent.KEYCODE_ENTER to "Enter",
              AndroidKeyEvent.KEYCODE_DPAD_CENTER to "Enter",
              AndroidKeyEvent.KEYCODE_SPACE to " ",
              AndroidKeyEvent.KEYCODE_TAB to "Tab",
              AndroidKeyEvent.KEYCODE_DEL to "Backspace",
              AndroidKeyEvent.KEYCODE_ESCAPE to "Escape",
              AndroidKeyEvent.KEYCODE_SHIFT_LEFT to "Shift",
              AndroidKeyEvent.KEYCODE_SHIFT_RIGHT to "Shift",
              AndroidKeyEvent.KEYCODE_CTRL_LEFT to "Control",
              AndroidKeyEvent.KEYCODE_CTRL_RIGHT to "Control",
              AndroidKeyEvent.KEYCODE_ALT_LEFT to "Alt",
              AndroidKeyEvent.KEYCODE_ALT_RIGHT to "Alt",
              AndroidKeyEvent.KEYCODE_META_LEFT to "Meta",
              AndroidKeyEvent.KEYCODE_META_RIGHT to "Meta",
              AndroidKeyEvent.KEYCODE_DPAD_UP to "ArrowUp",
              AndroidKeyEvent.KEYCODE_DPAD_DOWN to "ArrowDown",
              AndroidKeyEvent.KEYCODE_DPAD_LEFT to "ArrowLeft",
              AndroidKeyEvent.KEYCODE_DPAD_RIGHT to "ArrowRight",
          )
        }
  }
}
