/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_NDEBUG 0
#define LOG_TAG "CAR.INPUT"

#include <string.h>
#include <sys/time.h>
#include <linux/input.h>
#include <jni.h>
#include <JNIHelp.h>
#include <android/keycodes.h>
#include <cutils/log.h>
#include <utils/Errors.h>


namespace android {

static int androidKeyCodeToLinuxKeyCode(int androidKeyCode) {
    switch (androidKeyCode) {
    case AKEYCODE_VOLUME_UP:
        return KEY_VOLUMEUP;
    case AKEYCODE_VOLUME_DOWN:
        return KEY_VOLUMEDOWN;
    case AKEYCODE_CALL:
        return KEY_SEND;
    case AKEYCODE_ENDCALL:
        return KEY_END;
    /* TODO add more keys like these:
    case AKEYCODE_MEDIA_PLAY_PAUSE:
    case AKEYCODE_MEDIA_STOP:
    case AKEYCODE_MEDIA_NEXT:
    case AKEYCODE_MEDIA_PREVIOUS:*/
    case AKEYCODE_VOICE_ASSIST:
        return KEY_MICMUTE;
    default:
        ALOGW("Unmapped android key code %d dropped", androidKeyCode);
        return 0;
    }
}

/*
 * Class:     com_android_car_CarInputService
 * Method:    nativeInjectKeyEvent
 * Signature: (IIZ)I
 */
static jint com_android_car_CarInputService_nativeInjectKeyEvent
  (JNIEnv *env, jobject /*object*/, jint fd, jint keyCode, jboolean down) {
    int linuxKeyCode = androidKeyCodeToLinuxKeyCode(keyCode);
    if (linuxKeyCode == 0) {
        return BAD_VALUE;
    }
    struct input_event ev[2];
    memset(reinterpret_cast<void*>(&ev), 0, sizeof(ev));
    struct timeval now;
    gettimeofday(&now, NULL);
    // kernel driver is not using time now, but set it to be safe.
    ev[0].time = now;
    ev[0].type = EV_KEY;
    ev[0].code = linuxKeyCode;
    ev[0].value = (down ? 1 : 0);
    // force delivery and flushing
    ev[1].time = now;
    ev[1].type = EV_SYN;
    ev[1].code = SYN_REPORT;
    ev[1].value = 0;
    ALOGI("injectKeyEvent down %d keyCode %d, value %d", down, ev[0].code, ev[0].value);
    int r = write(fd, reinterpret_cast<void*>(&ev), sizeof(ev));
    if (r != sizeof(ev)) {
        return -EIO;
    }
    return 0;
}

static JNINativeMethod gMethods[] = {
    { "nativeInjectKeyEvent", "(IIZ)I",
            (void*)com_android_car_CarInputService_nativeInjectKeyEvent },
};

int register_com_android_car_CarInputService(JNIEnv *env) {
    return jniRegisterNativeMethods(env, "com/android/car/CarInputService",
            gMethods, NELEM(gMethods));
}

} // namespace android