/***********************************************************************************************************************
 * File Name    : hhid_thread_entry.c
 * Description  : Contains data structures and functions used in hhid_thread_entry.c.
 **********************************************************************************************************************/
/***********************************************************************************************************************
 * DISCLAIMER
 * This software is supplied by Renesas Electronics Corporation and is only intended for use with Renesas products. No
 * other uses are authorized. This software is owned by Renesas Electronics Corporation and is protected under all
 * applicable laws, including copyright laws.
 * THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
 * THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED. TO THE MAXIMUM
 * EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES
 * SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY REASON RELATED TO THIS
 * SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 * Renesas reserves the right, without notice, to make changes to this software and to discontinue the availability of
 * this software. By using this software, you agree to the additional terms and conditions found by accessing the
 * following link:
 * http://www.renesas.com/disclaimer
 *
 * Copyright (C) 2020 Renesas Electronics Corporation. All rights reserved.
 ***********************************************************************************************************************/
#include "common_utils.h"
#include "usbx_hhid_ep.h"

/*******************************************************************************************************************//**
 * @addtogroup USBX_hhid_ep
 * @{
 **********************************************************************************************************************/

/*Global variables */
static uint8_t  g_ux_pool_memory[MEMPOOL_SIZE];
static uint8_t  keyboard_key[BUFF_SIZE] = {RESET_VALUE};
ULONG           g_mouse_buttons = RESET_VALUE;
ULONG           g_keyboard_state = RESET_VALUE;
SLONG           g_mouse_x_position = RESET_VALUE;
SLONG           g_mouse_y_position = RESET_VALUE;
static ULONG    actual_flags = RESET_VALUE;
mouse_pos_t     mouse_axis = {RESET_VALUE};
UX_HOST_CLASS_HID * hid_class_instance;

/*Private function*/
static void deinit_usb_device(void);
static void deinit_host_stack(void);

/*******************************************************************************************************************//**
 * @brief     In this function, checks the USB device status and notifies to perform operation
 * @param[IN] event       updates usb events on attaching and detaching device.
 * @param[IN] *host_class  pointer to host class device structure
 * @param[IN] instance
 * @retval    none
 **********************************************************************************************************************/
static UINT apl_host_hid_change_function (ULONG event, UX_HOST_CLASS * host_class, VOID * instance)
{
    FSP_PARAMETER_NOT_USED(host_class);

    if (UX_DEVICE_INSERTION == event)
    {
        /* Get a pointer to a USB Host HID Class instance. */
        hid_class_instance = (UX_HOST_CLASS_HID *)instance;
        /* set the event flag as usb host device is plugged in */
        tx_event_flags_set(&g_usb_plug_events, EVENT_USB_PLUG_IN, TX_OR);

    }
    else if (UX_DEVICE_REMOVAL == event)
    {
        /* Update USB Host HID Class instance to NULL*/
        hid_class_instance = NULL;
        /* set the event flag as usb host device is plugged out */
        tx_event_flags_set(&g_usb_plug_events, EVENT_USB_PLUG_OUT, TX_OR);
    }
    return UX_SUCCESS;
}

/* HHID_Thread entry function */
void hhid_thread_entry(void)
{
    UINT       ux_return = UX_SUCCESS;
    fsp_err_t  err       = FSP_SUCCESS;
    UINT       error     = RESET_VALUE;
    UINT       status    = TX_SUCCESS;

    /* Initialize the RTT Thread.*/
    rtt_thread_init_check();

    /* print the banner and EP info. */
    app_rtt_print_data(RTT_OUTPUT_MESSAGE_BANNER, RESET_VALUE, NULL);

    /* ux_system_initialization */
    ux_return = ux_system_initialize((CHAR *)g_ux_pool_memory, MEMPOOL_SIZE, UX_NULL, RESET_VALUE);
    if (UX_SUCCESS != ux_return)
    {
        PRINT_ERR_STR(" UX_SYSTEM_INITIALIZE API FAILED.");
        ERROR_TRAP(ux_return);
    }

    PRINT_INFO_STR("ux_system initialized successfully!");

    /* ux host stack initialization */
    ux_return = ux_host_stack_initialize(apl_host_hid_change_function);
    if (UX_SUCCESS != ux_return)
    {
        PRINT_ERR_STR("UX_HOST_STACK_INITIALIZE API FAILED.");
        ERROR_TRAP(ux_return);
    }
    PRINT_INFO_STR("ux host stack initialized successfully!");

    /* Open usb driver */
    err = R_USB_Open(&g_basic0_ctrl, &g_basic0_cfg);
    if (FSP_SUCCESS != err)
    {
        PRINT_ERR_STR("R_USB_OPEN API FAILED..");
        deinit_host_stack();
        ERROR_TRAP(err);
    }
    PRINT_INFO_STR("Init completed");

    PRINT_INFO_STR("Connect mouse or keyboard to the USB Host connector");

    /*  Wait until device inserted.*/
    status = tx_event_flags_get (&g_usb_plug_events, EVENT_USB_PLUG_IN, TX_AND_CLEAR, &actual_flags, TX_WAIT_FOREVER);
    if((TX_SUCCESS == status) && (EVENT_USB_PLUG_IN == actual_flags))
    {
        PRINT_INFO_STR("USB device is inserted");
    }

    actual_flags = RESET_VALUE;
    while (true)
    {
        /*  Check if USB is plugged out.*/
        status = tx_event_flags_get (&g_usb_plug_events, EVENT_USB_PLUG_OUT, TX_AND_CLEAR, &actual_flags, TX_NO_WAIT);
        if ((TX_SUCCESS == status) && (EVENT_USB_PLUG_OUT == actual_flags))
        {
            PRINT_INFO_STR("Connect mouse or keyboard to the USB Host connector.");
            actual_flags = RESET_VALUE;
            /*  Wait until device inserted.*/
           status = tx_event_flags_get (&g_usb_plug_events, EVENT_USB_PLUG_IN, TX_AND_CLEAR, &actual_flags, TX_WAIT_FOREVER);
            if((TX_SUCCESS == status) && (EVENT_USB_PLUG_IN == actual_flags))
            {
                PRINT_INFO_STR("USB device is inserted");
            }
        }
        /* below section of code will execute when USB Host Device is inserted */
        else
        {
            if (NULL != hid_class_instance)
            {
                UX_HOST_CLASS_HID_CLIENT * hid_client = hid_class_instance->ux_host_class_hid_client;

                if (UX_SUCCESS == ux_utility_memory_compare (hid_client->ux_host_class_hid_client_name,
                                                              _ux_system_host_class_hid_client_keyboard_name,
                                                              _ux_utility_string_length_get(_ux_system_host_class_hid_client_keyboard_name)))
                {
                    /* Get the keyboard key pressed */
                    error = ux_host_class_hid_keyboard_key_get ((UX_HOST_CLASS_HID_KEYBOARD *)hid_client->ux_host_class_hid_client_local_instance,
                                                                (ULONG *)keyboard_key, &g_keyboard_state);
                    /* Get a keyboard state from the keyboard.  */
                    if (UX_SUCCESS == error || UX_NO_KEY_PRESS == error)
                    {
                        /* Check if CAPS lock key pressed */
                        if (g_keyboard_state != RESET_VALUE || keyboard_key !=RESET_VALUE)
                        {
                            if (RESET_VALUE != keyboard_key[BUFF_INDEX])
                            {
                                if((KEY_HIGH_VAL >= keyboard_key[BUFF_INDEX]) && (KEY_LOW_VAL <= keyboard_key[BUFF_INDEX]))
                                {
                                    app_rtt_print_data(RTT_OUTPUT_MESSAGE_APP_PRINT_KEY_PRESS, sizeof(keyboard_key), keyboard_key);
                                }
                                else
                                {
                                    PRINT_ERR_STR("Pressed key is out of specified key range");
                                }
                            }
                        }

                        tx_thread_sleep(TEN);
                        keyboard_key[BUFF_INDEX] = RESET_VALUE;
                        g_keyboard_state= RESET_VALUE;
                    }
                    else
                    {
                        PRINT_ERR_STR("ux_host_class_hid_keyboard_key_get API failed");
                        deinit_host_stack();
                        deinit_usb_device();
                        ERROR_TRAP(error);
                    }
                }
                else if(UX_SUCCESS == ux_utility_memory_compare(   hid_client->ux_host_class_hid_client_name,
                                                                    _ux_system_host_class_hid_client_mouse_name,
                                                                    _ux_utility_string_length_get(_ux_system_host_class_hid_client_mouse_name)))
                {
                    /* Get a mouse state from the mouse.  */
                    error = ux_host_class_hid_mouse_buttons_get ((UX_HOST_CLASS_HID_MOUSE *)hid_client->ux_host_class_hid_client_local_instance,
                                                                 (ULONG *)&g_mouse_buttons);

                    if (UX_SUCCESS == error)
                    {
                        /* Check if the left button pressed */
                        if (g_mouse_buttons & UX_HOST_CLASS_HID_MOUSE_BUTTON_1_PRESSED)
                        {
                            app_rtt_print_data(RTT_OUTPUT_MESSAGE_APP_PRINT_LEFT_BUTTON_PRESS, sizeof(UINT *), &g_mouse_buttons);
                        }
                        /* Check if the right button pressed */
                        else if (g_mouse_buttons & UX_HOST_CLASS_HID_MOUSE_BUTTON_2_PRESSED)
                        {
                            app_rtt_print_data(RTT_OUTPUT_MESSAGE_APP_PRINT_RIGHT_BUTTON_PRESS, sizeof(UINT *), &g_mouse_buttons);
                        }
                    }
                    else
                    {
                        PRINT_ERR_STR("ux_host_class_hid_mouse_buttons_get API failed");
                        deinit_host_stack();
                        deinit_usb_device();
                        ERROR_TRAP(error);
                    }

                    /* Check if the position moved */
                    error = ux_host_class_hid_mouse_position_get ((UX_HOST_CLASS_HID_MOUSE *)hid_client->ux_host_class_hid_client_local_instance,
                                                                  (SLONG *)&g_mouse_x_position, (SLONG *)&g_mouse_y_position);
                    if (UX_SUCCESS == error)
                    {
                        if ((mouse_axis.mouse_x_position_old != g_mouse_x_position) || (mouse_axis.mouse_y_position_old != g_mouse_y_position))
                        {
                            mouse_axis.mouse_x_position_old = g_mouse_x_position;
                            mouse_axis.mouse_y_position_old = g_mouse_y_position;
                            app_rtt_print_data(RTT_OUTPUT_MESSAGE_APP_PRINT_MOUSE_POSITION, sizeof(mouse_axis), &mouse_axis);
                        }
                    }
                    else
                    {
                        PRINT_ERR_STR("ux_host_class_hid_mouse_position_get API failed");
                        deinit_host_stack();
                        deinit_usb_device();
                        ERROR_TRAP(error);
                    }
                }
                else
                {
                    PRINT_INFO_STR("Connected device is unsupported, Connect a keyboard or mouse");
                }
            }
            else
            {
                PRINT_ERR_STR("USB instance error");
            }
        }
      tx_thread_sleep(TEN);
    }
}

/******************************************************************************************************************
 * @brief       This function closes the opened usb instance.
 * @param[IN]   None
 * @retval      None
 **********************************************************************************************************************/
static void deinit_usb_device(void)
{
    fsp_err_t err = FSP_SUCCESS;

    /*Close opened USB interface*/
    err = R_USB_Close(&g_basic0_ctrl);
    /*Handle error in-case of failure*/
    if (UX_SUCCESS != err)
    {
        PRINT_ERR_STR("R_USB_Close API Failed");
    }
}

/******************************************************************************************************************
 * @brief       This function closes the opened host stack.
 * @param[IN]   None
 * @retval      None
 **********************************************************************************************************************/
static void deinit_host_stack(void)
{
    fsp_err_t err = FSP_SUCCESS;

    /* Uninitializes all the host code for USB */
    err =  ux_host_stack_uninitialize();
    /* Handle error in-case of failure */
    if (UX_SUCCESS != err)
    {
        PRINT_ERR_STR("ux_host_stack_uninitialize failed");
    }
}

/******************************************************************************************************************
 * @} (end addtogroup usbx_hhid_ep)
 *********************************************************************************************************************/
