/**
 * Copyright (c) Facebook, Inc. and its 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;

import android.view.KeyEvent;
import android.view.View;

import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.common.MapBuilder;

import java.util.Map;

/**
 * Responsible for dispatching events specific for hardware inputs.
 */
public class ReactAndroidHWInputDeviceHelper {

  /**
   * Contains a mapping between handled KeyEvents and the corresponding navigation event
   * that should be fired when the KeyEvent is received.
   */
  private static final Map<Integer, String> KEY_EVENTS_ACTIONS = MapBuilder.of(
    KeyEvent.KEYCODE_DPAD_CENTER,
    "select",
    KeyEvent.KEYCODE_ENTER,
    "select",
    KeyEvent.KEYCODE_SPACE,
    "select",
    KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE,
    "playPause",
    KeyEvent.KEYCODE_MEDIA_REWIND,
    "rewind",
    KeyEvent.KEYCODE_MEDIA_FAST_FORWARD,
    "fastForward"
  );

  /**
   * We keep a reference to the last focused view id
   * so that we can send it as a target for key events
   * and be able to send a blur event when focus changes.
   */
  private int mLastFocusedViewId = View.NO_ID;

  private final ReactRootView mReactRootView;

  ReactAndroidHWInputDeviceHelper(ReactRootView mReactRootView) {
    this.mReactRootView = mReactRootView;
  }

  /**
   * Called from {@link ReactRootView}.
   * This is the main place the key events are handled.
   */
  public void handleKeyEvent(KeyEvent ev) {
    int eventKeyCode = ev.getKeyCode();
    int eventKeyAction = ev.getAction();
    if (eventKeyAction == KeyEvent.ACTION_UP && KEY_EVENTS_ACTIONS.containsKey(eventKeyCode)) {
      dispatchEvent(KEY_EVENTS_ACTIONS.get(eventKeyCode), mLastFocusedViewId);
    }
  }

  /**
   * Called from {@link ReactRootView} when focused view changes.
   */
  public void onFocusChanged(View newFocusedView) {
    if (mLastFocusedViewId == newFocusedView.getId()) {
      return;
    }
    if (mLastFocusedViewId != View.NO_ID) {
      dispatchEvent("blur", mLastFocusedViewId);
    }
    mLastFocusedViewId = newFocusedView.getId();
    dispatchEvent("focus", newFocusedView.getId());
  }

  /**
   * Called from {@link ReactRootView} when the whole view hierarchy looses focus.
   */
  public void clearFocus() {
    if (mLastFocusedViewId != View.NO_ID) {
      dispatchEvent("blur", mLastFocusedViewId);
    }
    mLastFocusedViewId = View.NO_ID;
  }

  private void dispatchEvent(String eventType, int targetViewId) {
    WritableMap event = new WritableNativeMap();
    event.putString("eventType", eventType);
    if (targetViewId != View.NO_ID) {
      event.putInt("tag", targetViewId);
    }
    mReactRootView.sendEvent("onHWKeyEvent", event);
  }
}

@@ -798,16 +798,44 @@
 der.
-of(%0A    
+%3CInteger, String%3Ebuilder()%0A    .put(
 KeyE
@@ -855,28 +855,24 @@
 DPAD_CENTER,
-%0A   
  %22select%22,%0A 
@@ -860,38 +860,43 @@
 CENTER, %22select%22
-,
+)
 %0A    
+.put(
 KeyEvent.KEYCODE
@@ -902,20 +902,16 @@
 E_ENTER,
-%0A   
  %22select
@@ -903,38 +903,43 @@
 _ENTER, %22select%22
-,
+)
 %0A    
+.put(
 KeyEvent.KEYCODE
@@ -945,20 +945,16 @@
 E_SPACE,
-%0A   
  %22select
@@ -954,22 +954,27 @@
 %22select%22
-,
+)
 %0A    
+.put(
 KeyEvent
@@ -999,20 +999,16 @@
 Y_PAUSE,
-%0A   
  %22playPa
@@ -1011,22 +1011,27 @@
 ayPause%22
-,
+)
 %0A    
+.put(
 KeyEvent
@@ -1052,20 +1052,16 @@
 _REWIND,
-%0A   
  %22rewind
@@ -1061,22 +1061,27 @@
 %22rewind%22
-,
+)
 %0A    
+.put(
 KeyEvent
@@ -1108,20 +1108,16 @@
 FORWARD,
-%0A   
  %22fastFo
@@ -1122,19 +1122,207 @@
 Forward%22
+)
 %0A  
+  .put(KeyEvent.KEYCODE_DPAD_UP, %22up%22)%0A    .put(KeyEvent.KEYCODE_DPAD_RIGHT, %22right%22)%0A    .put(KeyEvent.KEYCODE_DPAD_DOWN, %22down%22)%0A    .put(KeyEvent.KEYCODE_DPAD_LEFT, %22left%22)%0A    .build(
 );%0A%0A  /*

Fix event handlers for DPad arrows on Android TV (#21143)