/*
 * 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 "hdf_io_service_if.h"
#include <stdio.h>
#include <unistd.h>

#include <sys/time.h>

#include "hdf_log.h"
#include "osal_mem.h"
#include "osal_mutex.h"
#include "securec.h"
#include "signal.h"

#define TEST_WRITE         true
#define TEST_READ          false
#define SERVER_NAME_SDKAPI "usb_sdkapispeed_service"
#define SERVER_NAME_RAWAPI "usb_rawapispeed_service"
#define SERVER_NAME_NOSDK  "usb_nosdkspeed_service"
#define STRTOL_BASE  10

enum UsbSerialCmd {
    USB_SERIAL_OPEN = 0,
    USB_SERIAL_CLOSE,
    USB_SERIAL_SPEED,
};
struct UsbSpeedTest {
    int32_t busNum;
    int32_t devAddr;
    int32_t ifaceNum;
    int32_t writeOrRead;
    bool printData;
    int32_t paramNum;
};
enum speedServer {
    SDKAPI_SERVER = 0,
    RAWAPI_SERVER,
    NOSDK_SERVER,
};

static struct HdfIoService *g_service = NULL;
static struct HdfSBuf *g_data = NULL;
static struct HdfSBuf *g_reply = NULL;
static struct OsalMutex g_lock;
static enum speedServer g_spdServer = SDKAPI_SERVER;

static sigset_t g_mask;
pid_t g_stopHandlerTid;

static void SpeedTest(struct UsbSpeedTest test)
{
    OsalMutexLock(&g_lock);
    HdfSbufFlush(g_data);
    bool bufok = HdfSbufWriteBuffer(g_data, (const void *)&test, sizeof(test));
    if (!bufok) {
        printf("HdfSbufWriteBuffer err");
        goto RET;
    }
    int32_t status = g_service->dispatcher->Dispatch(&g_service->object, USB_SERIAL_SPEED, g_data, g_reply);
    if (status < 0) {
        printf("%s: Dispatch USB_SERIAL_SPEED failed status = %d\n", __func__, status);
    }
RET:
    OsalMutexUnlock(&g_lock);
}

static void SpeedInit(void)
{
    int32_t status;

    switch (g_spdServer) {
        case SDKAPI_SERVER:
            g_service = HdfIoServiceBind(SERVER_NAME_SDKAPI);
            break;
        case RAWAPI_SERVER:
            g_service = HdfIoServiceBind(SERVER_NAME_RAWAPI);
            break;
        case NOSDK_SERVER:
            g_service = HdfIoServiceBind(SERVER_NAME_NOSDK);
            break;
        default:
            break;
    }
    if (g_service == NULL || g_service->dispatcher == NULL || g_service->dispatcher->Dispatch == NULL) {
        printf("%s: GetService g_spdServer=%d err \n", __func__, g_spdServer);
        return;
    }

    // usb info max size is 2000
    uint32_t usbInfoMaxSize = 2000;
    g_data = HdfSbufObtain(usbInfoMaxSize);
    g_reply = HdfSbufObtain(usbInfoMaxSize);
    if (g_data == NULL || g_reply == NULL) {
        printf("%s: HdfSbufTypedObtain err", __func__);
        return;
    }

    status = g_service->dispatcher->Dispatch(&g_service->object, USB_SERIAL_OPEN, g_data, g_reply);
    if (status) {
        printf("%s: Dispatch USB_SERIAL_OPEN err status = %d\n", __func__, status);
        return;
    }

    if (OsalMutexInit(&g_lock) != HDF_SUCCESS) {
        printf("%s: init lock fail!", __func__);
        return;
    }
}

static void SpeedExit(void)
{
    if (g_service == NULL) {
        printf("%s: g_service is null", __func__);
        return;
    }
    int32_t status = g_service->dispatcher->Dispatch(&g_service->object, USB_SERIAL_CLOSE, g_data, g_reply);
    if (status) {
        printf("%s: Dispatch USB_SERIAL_CLOSE err status = %d\n", __func__, status);
    }

    HdfIoServiceRecycle(g_service);
    g_service = NULL;
    HdfSbufRecycle(g_data);
    HdfSbufRecycle(g_reply);
}

static void ShowHelp(const char *name)
{
    printf(">> usage:\n");
    printf(">> %s <-SDK>/<-RAW>/<-NOSDK> [<busNum> <devAddr>]  <ifaceNum> <w>/<r>/<endpoint> [printdata]> \n", name);
    printf("\n");
}

static void *StopHandler(void)
{
    int32_t signo;
    g_stopHandlerTid = getpid();

    while (true) {
        int32_t err = sigwait(&g_mask, &signo);
        if (err != 0) {
            printf("Sigwait failed: %d\n", err);
        }

        if ((signo == SIGINT) || (signo == SIGQUIT)) {
            printf("normal exit\n");
            SpeedExit();
            return 0;
        } else {
            printf("Unexpected signal %d\n", signo);
        }
    }
}

static enum speedServer checkServer(const char *input)
{
    char middle[10] = {0};
    if (input == NULL) {
        HDF_LOGE("%{public}s:%{public}d input is NULL", __func__, __LINE__);
        return SDKAPI_SERVER;
    }

    int32_t ret = strncpy_s(middle, sizeof(middle), input, (uint32_t)strlen(input));
    if (ret != EOK) {
        HDF_LOGE("%{public}s:%{public}d strncpy_s failed", __func__, __LINE__);
        return SDKAPI_SERVER;
    }

    if (strcmp(middle, "-SDK") == 0) {
        return SDKAPI_SERVER;
    }
    if (strcmp(middle, "-RAW") == 0) {
        return RAWAPI_SERVER;
    }
    if (strcmp(middle, "-NOSDK") == 0) {
        return NOSDK_SERVER;
    }
    return SDKAPI_SERVER;
}

static int32_t GetWriteOrReadFlag(const char *buffer)
{
    int32_t writeOrRead;

    if (!strncmp(buffer, "r", 1)) {
        writeOrRead = TEST_READ;
    } else if (!strncmp(buffer, "w", 1)) {
        writeOrRead = TEST_WRITE;
    } else {
        writeOrRead = (int32_t)strtol(buffer, NULL, STRTOL_BASE);
    }

    return writeOrRead;
}

static int32_t CheckParam(int32_t argc, const char *argv[], struct UsbSpeedTest *speedTest)
{
    int32_t ret = HDF_SUCCESS;
    bool printData = false;

    if ((argv == NULL) || (speedTest == NULL) || (argc <= 0)) {
        return HDF_ERR_INVALID_PARAM;
    }
    switch (argc) {
        case 7:                                  // 7 is number of arguments supplied to the main function
        case 6:                                  // 6 is number of arguments supplied to the main function
            g_spdServer = checkServer(argv[1]);  // 1 is argv second element
            speedTest->busNum = (int32_t)strtol(argv[2], NULL, STRTOL_BASE);   // 2 is argv third element
            speedTest->devAddr = (int32_t)strtol(argv[3], NULL, STRTOL_BASE);  // 3 is argv fourth element
            speedTest->ifaceNum = (int32_t)strtol(argv[4], NULL, STRTOL_BASE); // 4 is argv fifth element
            speedTest->writeOrRead = GetWriteOrReadFlag(argv[5]); // 5 is argv sixth element
            // 7 is number of arguments supplied to the main function
            if ((argc == 7) && (speedTest->writeOrRead == TEST_READ)) {
                printData = (strncmp(argv[6], "printdata", 1)) ? false : true; // 6 is argv seventh element
            }
            break;
        case 4:                                 // 4 number of arguments supplied to the main function
            g_spdServer = checkServer(argv[1]); // 1 is argv second element
            speedTest->busNum = 1;
            speedTest->devAddr = 2;                               // 2 is device address
            speedTest->ifaceNum = (int32_t)strtol(argv[2], NULL, STRTOL_BASE);         // 2 is argv third element
            speedTest->writeOrRead = GetWriteOrReadFlag(argv[3]); // 3 is argv fourth element
            break;
        default:
            printf("Error: parameter error!\n");
            ShowHelp(argv[0]); // 0 is argv first element
            ret = HDF_FAILURE;
            break;
    }
    if (ret == HDF_SUCCESS) {
        speedTest->printData = printData;
        speedTest->paramNum = argc - 1;
    }

    return ret;
}

int32_t main(int32_t argc, char *argv[])
{
    int32_t ret;
    struct UsbSpeedTest test;

    ret = CheckParam(argc, argv, &test);
    if (ret != HDF_SUCCESS) {
        goto END;
    }

    pthread_t threads;
    sigemptyset(&g_mask);
    sigaddset(&g_mask, SIGINT);
    sigaddset(&g_mask, SIGQUIT);
    if (pthread_sigmask(SIG_BLOCK, &g_mask, NULL) != 0) {
        printf("SIG_BLOCK error\n");
        ret = HDF_FAILURE;
        goto END;
    }
    if (pthread_create(&threads, NULL, StopHandler, NULL) != 0) {
        printf("Could not create core thread\n");
        ret = HDF_FAILURE;
        goto END;
    }

    SpeedInit();
    SpeedTest(test);
    kill(g_stopHandlerTid, SIGINT);
    pthread_join(threads, NULL);
END:
    return ret;
}