/*
 * 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.
 */
#ifndef HDC_HOST_USB_H
#define HDC_HOST_USB_H
#include "host_common.h"

namespace Hdc {
struct ClearUsbChannelWorkInfo {
    int result = 0;
    HSession hSession = nullptr;
    HDaemonInfo pDaemonInfo = nullptr;
};
using HClearUsbChannelWorkInfo = struct ClearUsbChannelWorkInfo *;

class HdcHostUSB : public HdcUSBBase {
public:
    enum class UsbCheckStatus {
        HOST_USB_IGNORE = 1,        // Device temporarily unavailable, retry later
        HOST_USB_READY,             // Reserved enum value (not used currently)
        HOST_USB_REGISTER,          // Device connected to current process
        HOST_USB_SUSPENDED          // Temporarily suspended by spawn-sub command
    };

    HdcHostUSB(const bool serverOrDaemonIn, void *ptrMainBase, void *ctxUSBin);
    virtual ~HdcHostUSB();
    int Initial();
    static void InitLogging(void *ctxUSB);
    int SendUSBRaw(HSession hSession, uint8_t *data, const int length) override;
    HSession ConnectDetectDaemon(const HSession hSession, const HDaemonInfo pdi);
    void Stop();
    static libusb_log_level GetLibusbLogLevel(void);
    static void SetLibusbLogLevelEnv(libusb_log_level logLevel);
    void SendSoftResetToDaemon(HSession hSession, uint32_t sessionIdOld);
    static void WatchUsbNodeChange(HdcHostUSB *thisClass);
    void ReviewUsbNodeLater(const string &nodeKey, UsbCheckStatus status = UsbCheckStatus::HOST_USB_IGNORE);
    void AllowUsbNodeRescan(string &usbMountPoint);

private:

    enum class DetectReturnType {
        NEW_OBJECT_FAIL,
        OPEN_DEVICE_FAIL,
        WRONG_SERIAL_FAIL,
        MALLOC_SESSION_FAIL,
        DETECT_SUCCESS
    };

    static int LIBUSB_CALL HotplugHostUSBCallback(libusb_context *ctx, libusb_device *device,
                                                  libusb_hotplug_event event, void *userData);
    static void UsbWorkThread(void *arg);  // 3rd thread
    static void WatchUsbNodeChange(uv_timer_t *handle);
    static void KickoutZombie(HSession hSession);
    static void LIBUSB_CALL USBBulkCallback(struct libusb_transfer *transfer);
    static void ClearUsbChannel(uv_work_t *req);
    static void ClearUsbChannelFinished(uv_work_t *req, int status);
    static void SendSoftResetToDaemonSync(HSession hSession, uint32_t sessionIdOld);
    int StartupUSBWork();
    void CheckUsbEndpoint(int& ret, HUSB hUSB, libusb_config_descriptor *descConfig);
    int CheckActiveConfig(libusb_device *device, HUSB hUSB, libusb_device_descriptor& desc);
    int OpenDeviceMyNeed(HUSB hUSB);
    bool HasValidDevice(libusb_device *device);
    int CheckDescriptor(HUSB hUSB, libusb_device_descriptor& desc);
    bool IsDebuggableDev(const struct libusb_interface_descriptor *ifDescriptor);
    bool ReadyForWorkThread(HSession hSession) override;
    bool FindDeviceByID(HUSB hUSB, const char *usbMountPoint, libusb_context *ctxUSB);
    DetectReturnType DetectMyNeed(libusb_device *device, string &sn);
    void RestoreHdcProtocol(HUSB hUsb, const uint8_t *buf, int bufSize);
    void UpdateUSBDaemonInfo(HUSB hUSB, HSession hSession, uint8_t connStatus);
    void BeginUsbRead(HSession hSession);
    void RemoveIgnoreDevice(string &mountInfo, bool force = false);
    void CancelUsbIo(HSession hSession) override;
    int UsbToHdcProtocol(uv_stream_t *stream, uint8_t *appendData, int dataSize) override;
    int SubmitUsbBio(HSession hSession, bool sendOrRecv, uint8_t *buf, int bufSize);

    libusb_context *ctxUSB;
    uv_timer_t devListWatcher;
    map<string, UsbCheckStatus> mapIgnoreDevice;
    int logRePrintTimer;
    int logRePrintCount;

private:
    uv_thread_t threadUsbWork;
};
}  // namespace Hdc
#endif