/*

 * Copyright (c) 2021 Huawei Device Co., Ltd.

 * 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.

 */

#include "input_manager.h"

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <limits.h>

#include <fcntl.h>

#include <malloc.h>

#include <sys/ioctl.h>

#include <securec.h>

#include "hdf_io_service_if.h"

#include "osal_time.h"

#include "input_common.h"



#define TOUCH_INDEX 1

#define PLACEHOLDER_LENGTH 2

#define PLACEHOLDER_LIMIT 10



static InputDevManager *g_devManager;

int32_t InstanceReporterHdi(InputReporter **hdi);

int32_t InstanceControllerHdi(InputController **hdi);

int32_t UpdateDevFullInfo(uint32_t devIndex);



InputDevManager *GetDevManager(void)

{

    return g_devManager;

}



static int32_t GetInputDevice(uint32_t devIndex, InputDeviceInfo **devInfo)

{

    int32_t ret;

    int32_t count = 3; // 3 : number of attempts

    DeviceInfoNode *pos = NULL;

    DeviceInfoNode *next = NULL;

    InputDevManager *manager = NULL;



    if (devIndex >= MAX_INPUT_DEV_NUM || devInfo == NULL) {

        HDF_LOGE("%s: invalid param", __func__);

        return INPUT_INVALID_PARAM;

    }



    while ((count--) > 0) {

        ret = UpdateDevFullInfo(devIndex);

        if (ret == INPUT_SUCCESS) {

            break;

        }

        OsalMSleep(10); // 10 : delay time

    }

    if (count == 0) {

        HDF_LOGE("%s: update dev info failed", __func__);

        return ret;

    }



    GET_MANAGER_CHECK_RETURN(manager);



    pthread_mutex_lock(&manager->mutex);

    DLIST_FOR_EACH_ENTRY_SAFE(pos, next, &manager->devList, DeviceInfoNode, node) {

        if (pos->payload.devIndex != devIndex) {

            continue;

        }

        *devInfo = &pos->payload;

        pthread_mutex_unlock(&manager->mutex);

        HDF_LOGI("%s: device%u get dev info succ", __func__, devIndex);

        return INPUT_SUCCESS;

    }



    pthread_mutex_unlock(&manager->mutex);

    HDF_LOGE("%s: device%u doesn't exist, can't get device info", __func__, devIndex);

    return INPUT_FAILURE;

}



static int32_t GetInputDeviceList(uint32_t *devNum, InputDeviceInfo **deviceList, uint32_t size)

{

    DeviceInfoNode *pos = NULL;

    DeviceInfoNode *next = NULL;

    InputDevManager *manager = NULL;

    uint32_t tempSize = 0;

    InputDeviceInfo **tempList = NULL;



    if (devNum == NULL || deviceList == NULL) {

        HDF_LOGE("%s: invalid param", __func__);

        return INPUT_INVALID_PARAM;

    }

    tempList = deviceList;

    GET_MANAGER_CHECK_RETURN(manager);



    pthread_mutex_lock(&manager->mutex);

    DLIST_FOR_EACH_ENTRY_SAFE(pos, next, &manager->devList, DeviceInfoNode, node) {

        if (tempSize >= size) {

            *devNum = manager->attachedDevNum;

            pthread_mutex_unlock(&manager->mutex);

            HDF_LOGE("%s: size is not enough, size = %u, devNum = %u", __func__,

                size, *devNum);

            return INPUT_FAILURE;

        }

        *tempList = &pos->payload;

        tempList++;

        tempSize++;

    }

    *devNum = manager->attachedDevNum;

    pthread_mutex_unlock(&manager->mutex);

    return INPUT_SUCCESS;

}



static int32_t CloseInputDevice(uint32_t devIndex)

{

    DeviceInfoNode *pos = NULL;

    DeviceInfoNode *next = NULL;

    InputDevManager *manager = NULL;



    GET_MANAGER_CHECK_RETURN(manager);



    pthread_mutex_lock(&manager->mutex);

    DLIST_FOR_EACH_ENTRY_SAFE(pos, next, &manager->devList, DeviceInfoNode, node) {

        if (pos->payload.devIndex != devIndex) {

            continue;

        }

        HdfIoServiceRecycle(pos->service);

        DListRemove(&pos->node);

        free(pos);

        manager->attachedDevNum--;

        pthread_mutex_unlock(&manager->mutex);

        return INPUT_SUCCESS;

    }



    pthread_mutex_unlock(&manager->mutex);

    HDF_LOGE("%s: device%u doesn't exist", __func__, devIndex);

    return INPUT_FAILURE;

}



static int32_t AddService(uint32_t index, const struct HdfIoService *service)

{

    InputDevManager *manager = NULL;

    DeviceInfoNode *device = NULL;



    GET_MANAGER_CHECK_RETURN(manager);

    device = (DeviceInfoNode *)malloc(sizeof(DeviceInfoNode));

    if (device == NULL) {

        HDF_LOGE("%s: malloc fail", __func__);

        return INPUT_NOMEM;

    }

    (void)memset_s(device, sizeof(DeviceInfoNode), 0, sizeof(DeviceInfoNode));



    device->payload.devIndex = index;

    device->service = (struct HdfIoService *)service;

    pthread_mutex_lock(&manager->mutex);

    DListInsertTail(&device->node, &manager->devList);

    manager->attachedDevNum++;

    pthread_mutex_unlock(&manager->mutex);

    return INPUT_SUCCESS;

}



static int32_t CheckIndex(uint32_t devIndex)

{

    DeviceInfoNode *pos = NULL;

    DeviceInfoNode *next = NULL;

    InputDevManager *manager = NULL;



    if (devIndex >= MAX_INPUT_DEV_NUM) {

        HDF_LOGE("%s: invalid param", __func__);

        return INPUT_INVALID_PARAM;

    }



    GET_MANAGER_CHECK_RETURN(manager);

    pthread_mutex_lock(&manager->mutex);

    DLIST_FOR_EACH_ENTRY_SAFE(pos, next, &manager->devList, DeviceInfoNode, node) {

        if (pos->payload.devIndex == devIndex) {

            pthread_mutex_unlock(&manager->mutex);

            HDF_LOGE("%s: the device%u has existed", __func__, devIndex);

            return INPUT_FAILURE;

        }

    }

    pthread_mutex_unlock(&manager->mutex);

    return INPUT_SUCCESS;

}



static int32_t OpenInputDevice(uint32_t devIndex)

{

    int32_t ret;

    int32_t len;

    struct HdfIoService *service = NULL;

    char serviceName[SERVICE_NAME_LEN] = {0};



    if (CheckIndex(devIndex) != INPUT_SUCCESS) {

        return INPUT_FAILURE;

    }



    len = (devIndex < PLACEHOLDER_LIMIT) ? 1 : PLACEHOLDER_LENGTH;

    ret = snprintf_s(serviceName, SERVICE_NAME_LEN, strlen("hdf_input_event") + len, "%s%u",

        "hdf_input_event", devIndex);

    if (ret == -1) {

        HDF_LOGE("%s: snprintf_s fail", __func__);

        return INPUT_FAILURE;

    }



    service = HdfIoServiceBind(serviceName);

    if (service == NULL) {

        HDF_LOGE("%s: fail to get io service: %s", __func__, serviceName);

        return INPUT_NULL_PTR;

    }



    if (AddService(devIndex, service) < 0) {

        HDF_LOGE("%s: add device%d failed", __func__, devIndex);

        HdfIoServiceRecycle(service);

        return INPUT_FAILURE;

    }



    HDF_LOGI("%s: open dev%u succ, service name = %s", __func__, devIndex, serviceName);

    return INPUT_SUCCESS;

}



static int32_t ScanInputDevice(InputDevDesc *staArr, uint32_t arrLen)

{

    InputDevManager *manager = NULL;

    struct HdfIoService *service = NULL;

    struct HdfSBuf *reply = NULL;

    char *data = {0};

    uint32_t count = 0;

    uint32_t replayDataSize = 0;

    int32_t ret;



    GET_MANAGER_CHECK_RETURN(manager);

    pthread_mutex_lock(&manager->mutex);

    if (manager->hostDev.service == NULL) {

        manager->hostDev.service = HdfIoServiceBind(DEV_MANAGER_SERVICE_NAME);

    }

    service = manager->hostDev.service;

    pthread_mutex_unlock(&manager->mutex);



    if (service == NULL) {

        HDF_LOGE("%s: HdfIoServiceBind failed", __func__);

        return INPUT_FAILURE;

    }

    reply = HdfSbufObtainDefaultSize();

    if (reply == NULL) {

        HDF_LOGE("%s: fail to obtain sbuf data", __func__);

        return INPUT_FAILURE;

    }



    ret = service->dispatcher->Dispatch(&service->object, 0, NULL, reply);

    if (ret != INPUT_SUCCESS) {

        HDF_LOGE("%s: dispatch fail", __func__);

        HdfSbufRecycle(reply);

        return INPUT_FAILURE;

    }



    while (count < arrLen) {

        if (!HdfSbufReadBuffer(reply, (const void **)(&data), &replayDataSize) ||

            replayDataSize != sizeof(InputDevDesc)) {

            HDF_LOGE("%s: sbuf read failed", __func__);

            break;

        }

        if (memcpy_s(&staArr[count], sizeof(InputDevDesc), data, replayDataSize) != EOK) {

            HDF_LOGE("%s: memcpy failed, line: %d", __func__, __LINE__);

            HdfSbufRecycle(reply);

            return INPUT_FAILURE;

        }

        HDF_LOGI("%s: type = %d, id =%d", __func__, staArr[count].devType, staArr[count].devIndex);

        count++;

    }

    HdfSbufRecycle(reply);

    return INPUT_SUCCESS;

}



static int32_t InstanceManagerHdi(InputManager **manager)

{

    InputManager *managerHdi = (InputManager *)malloc(sizeof(InputManager));

    if (managerHdi == NULL) {

        HDF_LOGE("%s: malloc fail", __func__);

        return INPUT_NOMEM;

    }



    (void)memset_s(managerHdi, sizeof(InputManager), 0, sizeof(InputManager));



    managerHdi->ScanInputDevice = ScanInputDevice;

    managerHdi->OpenInputDevice = OpenInputDevice;

    managerHdi->CloseInputDevice = CloseInputDevice;

    managerHdi->GetInputDevice = GetInputDevice;

    managerHdi->GetInputDeviceList = GetInputDeviceList;

    *manager = managerHdi;

    return INPUT_SUCCESS;

}



static int32_t InitDevManager(void)

{

    InputDevManager *manager = (InputDevManager *)malloc(sizeof(InputDevManager));

    if (manager == NULL) {

        HDF_LOGE("%s: malloc fail", __func__);

        return INPUT_NOMEM;

    }



    (void)memset_s(manager, sizeof(InputDevManager), 0, sizeof(InputDevManager));

    DListHeadInit(&manager->devList);

    pthread_mutex_init(&manager->mutex, NULL);

    manager->attachedDevNum = 0;

    manager->evtCallbackNum = 0;

    g_devManager = manager;

    return INPUT_SUCCESS;

}



static void FreeInputHdi(IInputInterface **hdi)

{

    if (hdi == NULL || *hdi == NULL) {

        return;

    }

    if ((*hdi)->iInputManager != NULL) {

        free((*hdi)->iInputManager);

        (*hdi)->iInputManager = NULL;

    }



    if ((*hdi)->iInputController != NULL) {

        free((*hdi)->iInputController);

        (*hdi)->iInputController = NULL;

    }



    if ((*hdi)->iInputReporter != NULL) {

        free((*hdi)->iInputReporter);

        (*hdi)->iInputReporter = NULL;

    }

    free((*hdi));

    *hdi = NULL;

}



static IInputInterface *InstanceInputHdi(void)

{

    int32_t ret;

    IInputInterface *hdi = (IInputInterface *)malloc(sizeof(IInputInterface));

    if (hdi == NULL) {

        HDF_LOGE("%s: malloc fail", __func__);

        return NULL;

    }

    (void)memset_s(hdi, sizeof(IInputInterface), 0, sizeof(IInputInterface));



    ret = InstanceManagerHdi(&hdi->iInputManager);

    if (ret != INPUT_SUCCESS) {

        FreeInputHdi(&hdi);

        return NULL;

    }



    ret = InstanceControllerHdi(&hdi->iInputController);

    if (ret != INPUT_SUCCESS) {

        FreeInputHdi(&hdi);

        return NULL;

    }



    ret = InstanceReporterHdi(&hdi->iInputReporter);

    if (ret != INPUT_SUCCESS) {

        FreeInputHdi(&hdi);

        return NULL;

    }

    return hdi;

}



int32_t GetInputInterface(IInputInterface **inputInterface)

{

    int32_t ret;

    IInputInterface *inputHdi = NULL;



    if (inputInterface == NULL) {

        HDF_LOGE("%s: parameter is null", __func__);

        return INPUT_INVALID_PARAM;

    }



    inputHdi = InstanceInputHdi();

    if (inputHdi == NULL) {

        HDF_LOGE("%s: failed to instance hdi", __func__);

        return INPUT_NULL_PTR;

    }



    ret = InitDevManager();

    if (ret != INPUT_SUCCESS) {

        HDF_LOGE("%s: failed to initialize manager", __func__);

        FreeInputHdi(&inputHdi);

        return INPUT_FAILURE;

    }



    *inputInterface = inputHdi;

    HDF_LOGI("%s: exit succ", __func__);

    return INPUT_SUCCESS;

}



static void FreeDevManager(InputDevManager *manager)

{

    (void)HdfDeviceUnregisterEventListener(manager->hostDev.service, manager->hostDev.listener);

    if (manager->hostDev.listener != NULL) {

        free(manager->hostDev.listener);

        manager->hostDev.listener = NULL;

        manager->hostDev.hostCb = NULL;

    }

    (void)HdfIoServiceRecycle(manager->hostDev.service);

    pthread_mutex_unlock(&manager->mutex);

    pthread_mutex_destroy(&manager->mutex);

    free(manager);

    g_devManager = NULL;

}



void ReleaseInputInterface(IInputInterface **inputInterface)

{

    DeviceInfoNode *pos = NULL;

    DeviceInfoNode *next = NULL;

    InputDevManager *manager = NULL;



    if (inputInterface == NULL) {

        return;

    }

    FreeInputHdi(inputInterface);



    if (g_devManager == NULL) {

        return;

    }

    manager = g_devManager;

    pthread_mutex_lock(&manager->mutex);

    DLIST_FOR_EACH_ENTRY_SAFE(pos, next, &manager->devList, DeviceInfoNode, node) {

        (void)HdfDeviceUnregisterEventListener(pos->service, pos->listener);

        if (pos->listener != NULL) {

            free(pos->listener);

            pos->listener = NULL;

            pos->eventCb = NULL;

        }

        (void)HdfIoServiceRecycle(pos->service);

        DListRemove(&pos->node);

        free(pos);

    }

    FreeDevManager(manager);

}