// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.embedding.engine.systemchannels;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.KeyEvent;

import java.util.HashMap;
import java.util.Map;

import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.plugin.common.BasicMessageChannel;
import io.flutter.plugin.common.JSONMessageCodec;

/**
 * TODO(mattcarroll): fill in javadoc for KeyEventChannel.
 */
public class KeyEventChannel {

  @NonNull
  public final BasicMessageChannel<Object> channel;

  public KeyEventChannel(@NonNull DartExecutor dartExecutor) {
    this.channel = new BasicMessageChannel<>(dartExecutor, "flutter/keyevent", JSONMessageCodec.INSTANCE);
  }

  public void keyUp(@NonNull FlutterKeyEvent keyEvent) {
    Map<String, Object> message = new HashMap<>();
    message.put("type", "keyup");
    message.put("keymap", "android");
    encodeKeyEvent(keyEvent, message);

    channel.send(message);
  }

  public void keyDown(@NonNull FlutterKeyEvent keyEvent) {
    Map<String, Object> message = new HashMap<>();
    message.put("type", "keydown");
    message.put("keymap", "android");
    encodeKeyEvent(keyEvent, message);

    channel.send(message);
  }

  private void encodeKeyEvent(@NonNull FlutterKeyEvent event, @NonNull Map<String, Object> message) {
    message.put("flags", event.flags);
    message.put("plainCodePoint", event.plainCodePoint);
    message.put("codePoint", event.codePoint);
    message.put("keyCode", event.keyCode);
    message.put("scanCode", event.scanCode);
    message.put("metaState", event.metaState);
    if (event.complexCharacter != null) {
      message.put("character", event.complexCharacter.toString());
    }
  }

  /**
   * Key event as defined by Flutter.
   */
  public static class FlutterKeyEvent {
    public final int flags;
    public final int plainCodePoint;
    public final int codePoint;
    public final int keyCode;
    @Nullable
    public final Character complexCharacter;
    public final int scanCode;
    public final int metaState;

    public FlutterKeyEvent(
        @NonNull KeyEvent androidKeyEvent
    ) {
      this(androidKeyEvent, null);
    }

    public FlutterKeyEvent(
        @NonNull KeyEvent androidKeyEvent,
        @Nullable Character complexCharacter
    ) {
      this(
          androidKeyEvent.getFlags(),
          androidKeyEvent.getUnicodeChar(0x0),
          androidKeyEvent.getUnicodeChar(),
          androidKeyEvent.getKeyCode(),
          complexCharacter,
          androidKeyEvent.getScanCode(),
          androidKeyEvent.getMetaState()
      );
    }

    public FlutterKeyEvent(
        int flags,
        int plainCodePoint,
        int codePoint,
        int keyCode,
        @Nullable Character complexCharacter,
        int scanCode,
        int metaState
    ) {
      this.flags = flags;
      this.plainCodePoint = plainCodePoint;
      this.codePoint = codePoint;
      this.keyCode = keyCode;
      this.complexCharacter = complexCharacter;
      this.scanCode = scanCode;
      this.metaState = metaState;
    }
  }
}

@@ -211,16 +211,41 @@
 nnels;%0A%0A
+import android.os.Build;%0A
 import a
@@ -323,16 +323,49 @@
 llable;%0A
+import android.view.InputDevice;%0A
 import a
@@ -1931,24 +1931,157 @@
 g());%0A    %7D%0A
+    message.put(%22source%22, event.source);%0A    message.put(%22vendorId%22, event.vendorId);%0A    message.put(%22productId%22, event.productId);%0A
   %7D%0A%0A  /**%0A 
@@ -2163,16 +2163,47 @@
 Event %7B%0A
+    public final int deviceId;%0A
     publ
@@ -2442,16 +2442,108 @@
 taState;
+%0A    public final int source;%0A    public final int vendorId;%0A    public final int productId;
 %0A%0A    pu
@@ -2792,16 +2792,57 @@
   this(%0A
+          androidKeyEvent.getDeviceId(),%0A
         
@@ -3111,16 +3111,55 @@
 aState()
+,%0A          androidKeyEvent.getSource()
 %0A      )
@@ -3187,32 +3187,54 @@
 lutterKeyEvent(%0A
+        int deviceId,%0A
         int flag
@@ -3397,24 +3397,76 @@
 etaState
-%0A    ) %7B
+,%0A        int source%0A    ) %7B%0A      this.deviceId = deviceId;
 %0A      t
@@ -3702,24 +3702,469 @@
  metaState;%0A
+      this.source = source;%0A      InputDevice device = InputDevice.getDevice(deviceId);%0A      if (device != null) %7B%0A        if (Build.VERSION.SDK_INT %3E= Build.VERSION_CODES.KITKAT) %7B%0A          this.vendorId = device.getVendorId();%0A          this.productId = device.getProductId();%0A        %7D else %7B%0A          this.vendorId = 0;%0A          this.productId = 0;%0A        %7D%0A      %7D else %7B%0A        this.vendorId = 0;%0A        this.productId = 0;%0A      %7D%0A
     %7D%0A  %7D%0A%7D%0A

Add the key event source, vendorId, and productId from Android (#9186)