/*
 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
 *
 * HDF is dual licensed: you can use it either under the terms of
 * the GPL, or the BSD license, at your option.
 * See the LICENSE file in the root of this repository for complete details.
 */

#include "hdf_device_desc.h"
#include "osal_mem.h"
#include "gpio_if.h"
#include "hdf_log.h"
#include "hdf_input_device_manager.h"
#include "event_hub.h"
#include "hdf_key.h"

#define CHECK_PARSER_RET(ret, str) do { \
    if ((ret) != HDF_SUCCESS) { \
        HDF_LOGE("%s: %s failed, ret = %d!", __func__, str, ret); \
        return HDF_FAILURE; \
    } \
} while (0)

int32_t KeyIrqHandle(uint16_t intGpioNum, void *data)
{
    int32_t ret;
    uint16_t gpioValue = 0;
    KeyDriver *driver = (KeyDriver *)data;
    if (driver == NULL) {
        return HDF_FAILURE;
    }
    KeyEventData *event = &driver->eventData;
    if (event == NULL) {
        return HDF_FAILURE;
    }
    ret = GpioDisableIrq(intGpioNum);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: disable irq failed, ret %d", __func__, ret);
    }

    ret = GpioRead(intGpioNum, &gpioValue);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: gpio read failed, ret %d", __func__, ret);
        return HDF_FAILURE;
    }
    uint64_t curTime = OsalGetSysTimeMs();
    driver->preStatus = gpioValue;
    driver->timeStamp = curTime;

    if (gpioValue == GPIO_VAL_LOW) {
        event->definedEvent = INPUT_KEY_DOWN;
        input_report_key(driver->inputdev, KEY_POWER, 1);
    } else if (gpioValue == GPIO_VAL_HIGH) {
        event->definedEvent = INPUT_KEY_UP;
        input_report_key(driver->inputdev, KEY_POWER, 0);
    }
    input_sync(driver->inputdev);

    GpioEnableIrq(intGpioNum);
    return HDF_SUCCESS;
}

static int32_t SetupKeyIrq(KeyDriver *keyDrv)
{
    uint16_t intGpioNum = keyDrv->keyCfg->gpioNum;
    uint16_t irqFlag = keyDrv->keyCfg->irqFlag;
    int32_t ret = GpioSetDir(intGpioNum, GPIO_DIR_IN);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: gpio set dir failed, ret %d", __func__, ret);
        return ret;
    }
    ret = GpioSetIrq(intGpioNum, irqFlag | GPIO_IRQ_USING_THREAD, KeyIrqHandle, keyDrv);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: register irq failed, ret %d", __func__, ret);
        return ret;
    }
    ret = GpioEnableIrq(intGpioNum);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: enable irq failed, ret %d", __func__, ret);
        return HDF_FAILURE;
    }
    return HDF_SUCCESS;
}

static int32_t KeyInit(KeyDriver *keyDrv)
{
    int32_t ret = SetupKeyIrq(keyDrv);
    CHECK_RETURN_VALUE(ret);
    return HDF_SUCCESS;
}

static KeyChipCfg *KeyConfigInstance(struct HdfDeviceObject *device)
{
    KeyChipCfg *keyCfg = (KeyChipCfg *)OsalMemAlloc(sizeof(KeyChipCfg));
    if (keyCfg == NULL) {
        HDF_LOGE("%s: malloc key config failed", __func__);
        return NULL;
    }
    (void)memset_s(keyCfg, sizeof(KeyChipCfg), 0, sizeof(KeyChipCfg));
    keyCfg->hdfKeyDev = device;

    if (ParseKeyConfig(device->property, keyCfg) != HDF_SUCCESS) {
        HDF_LOGE("%s: parse key config failed", __func__);
        OsalMemFree(keyCfg);
        keyCfg = NULL;
    }
    return keyCfg;
}

static KeyDriver *KeyDriverInstance(KeyChipCfg *keyCfg)
{
    KeyDriver *keyDrv = (KeyDriver *)OsalMemAlloc(sizeof(KeyDriver));
    if (keyDrv == NULL) {
        HDF_LOGE("%s: malloc key driver failed", __func__);
        return NULL;
    }
    (void)memset_s(keyDrv, sizeof(KeyDriver), 0, sizeof(KeyDriver));

    keyDrv->devType = keyCfg->devType;
    keyDrv->keyCfg = keyCfg;

    return keyDrv;
}

static InputDevice *InputDeviceInstance(KeyDriver *keyDrv)
{
    InputDevice *inputDev = (InputDevice *)OsalMemAlloc(sizeof(InputDevice));
    if (inputDev == NULL) {
        HDF_LOGE("%s: malloc input device failed", __func__);
        return NULL;
    }
    (void)memset_s(inputDev, sizeof(InputDevice), 0, sizeof(InputDevice));

    inputDev->pvtData = (void *)keyDrv;
    inputDev->devType = keyDrv->devType;
    inputDev->devName = keyDrv->keyCfg->keyName;
    inputDev->hdfDevObj = keyDrv->keyCfg->hdfKeyDev;
    keyDrv->inputdev = inputDev;

    return inputDev;
}

static int32_t RegisterKeyDevice(KeyChipCfg *keyCfg)
{
    int32_t ret;
    KeyDriver *keyDrv = KeyDriverInstance(keyCfg);
    if (keyDrv == NULL) {
        HDF_LOGE("%s: instance key config failed", __func__);
        return HDF_ERR_MALLOC_FAIL;
    }

    ret = KeyInit(keyDrv);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: key driver init failed, ret %d", __func__, ret);
        goto EXIT;
    }

    InputDevice *inputDev = InputDeviceInstance(keyDrv);
    if (inputDev == NULL) {
        HDF_LOGE("%s: instance input device failed", __func__);
        goto EXIT;
    }

    ret = RegisterInputDevice(inputDev);
    if (ret != HDF_SUCCESS) {
        goto EXIT1;
    }
    return HDF_SUCCESS;

EXIT1:
    OsalMemFree(inputDev->pkgBuf);
    OsalMemFree(inputDev);
EXIT:
    OsalMemFree(keyDrv);
    HDF_LOGE("%s: exit failed", __func__);
    return HDF_FAILURE;
}

static int32_t HdfKeyDriverInit(struct HdfDeviceObject *device)
{
    int32_t ret;
    HDF_LOGI("%s: enter", __func__);
    if (device == NULL) {
        return HDF_ERR_INVALID_PARAM;
    }

    KeyChipCfg *keyCfg = KeyConfigInstance(device);
    if (keyCfg == NULL) {
        HDF_LOGE("%s: instance key config failed", __func__);
        return HDF_ERR_MALLOC_FAIL;
    }

    ret = RegisterKeyDevice(keyCfg);
    if (ret != HDF_SUCCESS) {
        goto EXIT;
    }
    HDF_LOGI("%s: exit succ!", __func__);
    return HDF_SUCCESS;

EXIT:
    OsalMemFree(keyCfg);
    return HDF_FAILURE;
}

static int32_t HdfKeyDispatch(struct HdfDeviceIoClient *client, int cmd, struct HdfSBuf *data, struct HdfSBuf *reply)
{
    (void)cmd;
    if (client == NULL || data == NULL || reply == NULL) {
        HDF_LOGE("%s: param is null", __func__);
        return HDF_FAILURE;
    }
    return HDF_SUCCESS;
}

static int32_t HdfKeyDriverBind(struct HdfDeviceObject *device)
{
    if (device == NULL) {
        return HDF_ERR_INVALID_PARAM;
    }
    static struct IDeviceIoService keyService = {
        .object.objectId = 1,
        .Dispatch = HdfKeyDispatch,
    };
    device->service = &keyService;
    return HDF_SUCCESS;
}

struct HdfDriverEntry g_hdfKeyEntry = {
    .moduleVersion = 1,
    .moduleName = "HDF_KEY",
    .Bind = HdfKeyDriverBind,
    .Init = HdfKeyDriverInit,
};

HDF_INIT(g_hdfKeyEntry);