/*
 * Copyright (c) 2022 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.
 */

#ifndef INPUT_DEVICE_MANAGER_H
#define INPUT_DEVICE_MANAGER_H

#include <functional>
#include <linux/input.h>
#include <list>
#include <map>
#include <mutex>
#include <pthread.h>
#include <sys/epoll.h>
#include <thread>
#include <unistd.h>
#include <vector>
#include "hdf_log.h"
#include "input_manager.h"

namespace OHOS {
namespace Input {
using namespace std;
#define EPOLL_WAIT_TIMEOUT  (-1)
#define DEVICE_INFO_SIZE    (64)
#define EPOLL_MAX_EVENTS    (32)
#define MAX_SUPPORT_DEVS    (32)
#define BUFFER_SIZE         (1024)
#define ARRAY_LENGTH        (128)
#define NAME_LENGTH         (128)
#define EVENT_BUFFER_SIZE   (512)
#define MS_THOUSAND         (1000)

enum InputDeviceStatus {
    INPUT_DEVICE_STATUS_OPENED = 1,
    INPUT_DEVICE_STATUS_CLOSED,
    INPUT_DEVICE_STATUS_UNKNOWN
};

// manager the device node list
typedef struct {
    uint32_t index;
    uint32_t status;
    int32_t fd;
    char devPathNode[DEV_NAME_LEN];
    InputDeviceInfo detailInfo;
} InputDevListNode;

class InputDeviceManager {
public:
    InputDeviceManager() = default;
    virtual ~InputDeviceManager()
    {
        inputDevList_.clear();
        reportEventPkgCallback_.clear();
    };
    InputDeviceManager(const InputDeviceManager &other) = delete;
    InputDeviceManager(InputDeviceManager &&other) = delete;
    InputDeviceManager &operator=(const InputDeviceManager &other) = delete;
    InputDeviceManager &operator=(InputDeviceManager &&other) = delete;
    void Init(void);
    vector<string> GetFiles(string path);
    void DoRead(int32_t fd, struct input_event *event, size_t size);
    int32_t OpenInputDevice(string devPath);
    RetStatus CloseInputDevice(string devPath);
    int32_t GetInputDeviceInfo(int32_t fd, InputDeviceInfo *detailInfo);
    void GetInputDeviceInfoList();
    int32_t DoInputDeviceAction(void);
    int32_t InotifyEventHandler(int32_t epollFd, int32_t notifyFd);
    void RemoveEpoll(int32_t epollFd, int32_t fileFd);
    int32_t AddToEpoll(int32_t epollFd, int32_t fileFd);
    void WorkerThread();
    int32_t FindIndexFromFd(int32_t &fd, uint32_t *index);
    int32_t FindIndexFromDevName(string devName, uint32_t *index);
    void DoWithEventDeviceAdd(int32_t &epollFd, int32_t &fd, string devPath);
    void SendHotPlugEvent(uint32_t &type, uint32_t &index, uint32_t status);
    void DoWithEventDeviceDel(int32_t &epollFd, uint32_t &index);
    void ReportEventPkg(int32_t iFd, InputEventPackage **iEvtPkg, size_t iCount);
    // InputManager
    RetStatus ScanDevice(InputDevDesc *staArr, uint32_t arrLen);
    RetStatus OpenDevice(uint32_t deviceIndex);
    RetStatus CloseDevice(uint32_t deviceIndex);
    int32_t GetDevice(int32_t deviceIndex, InputDeviceInfo **devInfo);
    int32_t GetDeviceList(uint32_t *devNum, InputDeviceInfo **deviceList, uint32_t size);
    // InputController
    RetStatus SetPowerStatus(uint32_t devIndex, uint32_t status);
    RetStatus GetPowerStatus(uint32_t devIndex, uint32_t *status);
    RetStatus GetDeviceType(uint32_t devIndex, uint32_t *deviceType);
    RetStatus GetChipInfo(uint32_t devIndex, char *chipInfo, uint32_t length);
    RetStatus GetVendorName(uint32_t devIndex, char *vendorName, uint32_t length);
    RetStatus GetChipName(uint32_t devIndex, char *chipName, uint32_t length);
    RetStatus SetGestureMode(uint32_t devIndex, uint32_t gestureMode);
    RetStatus RunCapacitanceTest(uint32_t devIndex, uint32_t testType, char *result, uint32_t length);
    RetStatus RunExtraCommand(uint32_t devIndex, InputExtraCmd *cmd);
    // InputReporter
    RetStatus RegisterReportCallback(uint32_t devIndex, InputEventCb *callback);
    RetStatus UnregisterReportCallback(uint32_t devIndex);
    RetStatus RegisterHotPlugCallback(InputHostCb *callback);
    RetStatus UnregisterHotPlugCallback(void);
    void dumpInfoList(InputDevListNode in)
    {
        HDF_LOGD("%{public}s index: %{public}u state:%{public}u fd:%{public}d devPathNode:%{public}s", __func__,
                 in.index, in.status, in.fd, in.devPathNode);
        HDF_LOGD("devIndex: %{public}u devType:%{public}u chipInfo:%{public}s "
                 "vendorName: %{public}s chipName: %{public}s attrSet.devName: %s "
                 "attrSet.id.busType: %{public}u attrSet.id.vendor: %{public}u "
                 "attrSet.id.product: %{public}u attrSet.id.version: %{public}u ",
                 in.detailInfo.devIndex, in.detailInfo.devType,
                 in.detailInfo.chipInfo, in.detailInfo.vendorName,
                 in.detailInfo.chipName, in.detailInfo.attrSet.devName,
                 in.detailInfo.attrSet.id.busType, in.detailInfo.attrSet.id.vendor,
                 in.detailInfo.attrSet.id.product, in.detailInfo.attrSet.id.version);
        for (int32_t i = 0; i < ABS_CNT; i++) {
            HDF_LOGD("attrSet.axisInfo.axis: %{public}d attrSet.axisInfo.min: %{public}d "
                     "attrSet.axisInfo.max: %{public}d attrSet.axisInfo.fuzz: %{public}d "
                     "attrSet.axisInfo.flat: %{public}d attrSet.axisInfo.range: %{public}d ",
                     in.detailInfo.attrSet.axisInfo[i].axis, in.detailInfo.attrSet.axisInfo[i].flat,
                     in.detailInfo.attrSet.axisInfo[i].fuzz, in.detailInfo.attrSet.axisInfo[i].max,
                     in.detailInfo.attrSet.axisInfo[i].min, in.detailInfo.attrSet.axisInfo[i].range);
        }
    }
private:
    void DeleteDevListNode(int index);
    int32_t AddDeviceNodeToList(
        int32_t &epollFd, int32_t &fd, string devPath, std::shared_ptr<InputDeviceInfo> &detailInfo);
    int32_t GetCurDevIndex();
    void LoadInputDevices(std::vector<std::string> &flist);
    void ReloadInputDevices(std::vector<std::string> flist);
    int32_t CreateInputDevListNode(InputDevListNode &inputDevNode, std::string &fileName);

    mutable std::mutex lock_;
    std::mutex reportEventPkgCallBackLock_;
    std::map<uint32_t, InputDevListNode> inputDevList_;
    struct epoll_event epollEventList_[EPOLL_MAX_EVENTS] {};
    std::string devPath_ {"/dev/input"};
    uint32_t devIndex_ {1};
    std::map<int32_t, InputEventCb*> reportEventPkgCallback_;
    InputHostCb *reportHotPlugEventCallback_ = nullptr;
    std::thread thread_ {};
    int32_t mEpollId_ {0};
    int32_t mInotifyId_ {0};
};
}
}
#endif // INPUT_DEVICE_MANAGER_H