/*
 * Copyright 2015 Freescale
 * Copyright 2016,2019-2020 NXP
 * This software is owned or controlled by NXP and may only be used strictly
 * in accordance with the applicable license terms.  By expressly accepting
 * such terms or by downloading, installing, activating and/or otherwise using
 * the software, you are agreeing that you have read, and that you agree to
 * comply with and are bound by, such license terms.  If you do not agree to
 * be bound by the applicable license terms, then you may not retain, install,
 * activate or otherwise use the software.
 */

#include "usb_device_config.h"
#if ((defined(USB_DEVICE_CONFIG_HID)) && (USB_DEVICE_CONFIG_HID > 0U))

#include "usb.h"
#include "usb_device.h"
#include "usb_device_class.h"
#include "usb_device_hid.h"
#include "usb_device_ch9.h"
#include "usb_device_descriptor.h"
#include "composite.h"
#include "hid_consumer_control.h"
//#include "usb_ctrl.h"
#include "usb_internal.h"
#include "fsl_gpio.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/

/* Play/pause report mask*/
#define KEY_PLAYPAUSE_MASK         (0x01)
/* Volume increment report mask*/
#define KEY_VOLUME_INCREMENT_MASK  (0x02)
/* Volume decrement report mask*/
#define KEY_VOLUME_DECREMENT_MASK  (0x04)
/* Next track report mask*/
#define KEY_SCAN_NEXT_TRACK_MASK   (0x08)
/* Prev track report mask*/
#define KEY_SCAN_PREV_TRACK_MASK   (0x10)
/* Stop music report mask*/
#define KEY_STOP_MUSIC_MASK        (0x20)

/*
 * Sending USB HID event consists of 2 steps:
 * - Sending HID event (VOL_UP, ...)
 * - After this is done, sending CLEAR event to clear the event on the PC
 *
 * So we use this enum to keep track of where we are in the sending process
 */
typedef enum _usb_hid_state {
    kUSB_HidStateIdle = 0,           /**< Idle, not sending anything */
    kUSB_HidStateSending,            /**< sending an event other than clear */
    kUSB_HidStateSendingClear,       /**< sending a clear event */
    kUSB_HidStateCount,
} usb_hid_state_t;

/*******************************************************************************
 * Variables
 ******************************************************************************/

USB_DMA_NONINIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE) static uint8_t s_HidBuffer[USB_HID_CONSUMER_CONTROL_REPORT_LENGTH];
usb_device_composite_struct_t *g_UsbDeviceComposite;
static usb_device_hid_consumer_control_ctxt_t s_UsbDeviceHidConsumerControlCtxt;
static volatile usb_hid_state_t gs_HidSendState = kUSB_HidStateIdle;

/*******************************************************************************
 * Code
 ******************************************************************************/

static usb_status_t SendEvent(usbHIDReport_t event)
{
    switch (event) {
        case PHUSB_HID_VOLUME_INCREMENT:
            s_UsbDeviceHidConsumerControlCtxt.buffer[0] = KEY_VOLUME_INCREMENT_MASK;
            break;
        case PHUSB_HID_VOLUME_DECREMENT:
            s_UsbDeviceHidConsumerControlCtxt.buffer[0] = KEY_VOLUME_DECREMENT_MASK;
            break;
        case PHUSB_HID_NEXT_TRACK:
            s_UsbDeviceHidConsumerControlCtxt.buffer[0] = KEY_SCAN_NEXT_TRACK_MASK;
            break;
        case PHUSB_HID_PREVIOUS_TRACK:
            s_UsbDeviceHidConsumerControlCtxt.buffer[0] = KEY_SCAN_PREV_TRACK_MASK;
            break;
        case PHUSB_HID_PLAYPAUSE:
            s_UsbDeviceHidConsumerControlCtxt.buffer[0] = KEY_PLAYPAUSE_MASK;
            break;
        case PHUSB_HID_CLEAR:
            s_UsbDeviceHidConsumerControlCtxt.buffer[0] = 0;
            break;
        default:
            PERR("unknown hid report event '%d'\n", event);
            break;
    }

    if (PHUSB_HID_CLEAR == event) {
        gs_HidSendState = kUSB_HidStateSendingClear;
    } else {
        gs_HidSendState = kUSB_HidStateSending;
    }

    return USB_DeviceHidSend(g_UsbDeviceComposite->hidConsumerControlCtxt.hidHandle, USB_HID_CONSUMER_CONTROL_ENDPOINT,
                             s_UsbDeviceHidConsumerControlCtxt.buffer, USB_HID_CONSUMER_CONTROL_REPORT_LENGTH);
}

usb_status_t USB_DeviceHidConsumerControlSendEvent(usbHIDReport_t event)
{
    usb_hid_state_t currState = kUSB_HidStateIdle;

    if (!g_UsbDeviceComposite) {
        return kStatus_USB_Error;
    }

    /* send the new report only when the device is attached */
    if (!g_UsbDeviceComposite->attach) {
        return kStatus_USB_Error;
    }
    OSA_SR_ALLOC();     
    OSA_ENTER_CRITICAL();   
    currState = gs_HidSendState;
    OSA_EXIT_CRITICAL();     

    if (currState != kUSB_HidStateIdle) {
        return kStatus_USB_Busy;
    }

    return SendEvent(event);
}

usb_status_t USB_DeviceHidConsumerControlCallback(class_handle_t handle, uint32_t event, void *param)
{
    (void)handle;
    (void)param;

    usb_status_t error = kStatus_USB_Error;

    switch (event)
    {
        case kUSB_DeviceHidEventSendResponse:
            if (g_UsbDeviceComposite && g_UsbDeviceComposite->attach)
            {
                if (kUSB_HidStateSending == gs_HidSendState) {
                    /* Send of key done, clear the key here */
                    error = SendEvent(PHUSB_HID_CLEAR);
                } else if (kUSB_HidStateSendingClear == gs_HidSendState ) {
                    /* Done sending event, we are idle after this callback */
                    gs_HidSendState = kUSB_HidStateIdle;
                    error = kStatus_USB_Success;
                } else {
                    /* Got this interrupt while idle? */
                    gs_HidSendState = kUSB_HidStateIdle;
                    error = kStatus_USB_Success;
                }
            }
            break;
        case kUSB_DeviceHidEventGetReport:
        case kUSB_DeviceHidEventSetReport:
        case kUSB_DeviceHidEventRequestReportBuffer:
            error = kStatus_USB_InvalidRequest;
            break;
        case kUSB_DeviceHidEventGetIdle:
        case kUSB_DeviceHidEventGetProtocol:
        case kUSB_DeviceHidEventSetIdle:
        case kUSB_DeviceHidEventSetProtocol:
            break;
        default:
            break;
    }

    return error;
}

usb_status_t USB_DeviceHidConsumerControlSetConfigure(class_handle_t handle, uint8_t configure)
{
    (void)handle;

    if (USB_COMPOSITE_CONFIGURE_INDEX == configure)
    {
        /* TODO:  */
        return kStatus_USB_Success;
    }

    return kStatus_USB_Error;
}

usb_status_t USB_DeviceHidConsumerControlSetInterface(class_handle_t handle, uint8_t interface, uint8_t alternateSetting)
{
    (void)handle;
    (void)alternateSetting;

    if (USB_HID_CONSUMER_CONTROL_INTERFACE_INDEX == interface)
    {
        /* TODO:  */
        return kStatus_USB_Success;
    }

    return kStatus_USB_Error;
}

usb_status_t USB_DeviceHidConsumerControlInit(usb_device_composite_struct_t *deviceComposite)
{
    g_UsbDeviceComposite = deviceComposite;
    s_UsbDeviceHidConsumerControlCtxt.buffer = s_HidBuffer;

    gs_HidSendState = kUSB_HidStateIdle;
    return kStatus_USB_Success;
}

#endif
