/*
 * Copyright 2025 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#include "multicore_coms.h"
#include "usb_host_config.h"
#include "usb_host.h"
#include "fsl_device_registers.h"
#include "usb_host_video.h"
#include "host_video.h"
#include "fsl_common.h"
#include "board.h"
#include "app.h"
#include "image_process.h"
#include "profile_pins.h"
#include "model_preprocess.h"

#if (defined(FSL_FEATURE_SOC_SYSMPU_COUNT) && (FSL_FEATURE_SOC_SYSMPU_COUNT > 0U))
#include "fsl_sysmpu.h"
#endif /* FSL_FEATURE_SOC_SYSMPU_COUNT */

#if ((!USB_HOST_CONFIG_KHCI) && (!USB_HOST_CONFIG_EHCI) && (!USB_HOST_CONFIG_OHCI) && (!USB_HOST_CONFIG_IP3516HS))
#error Please enable USB_HOST_CONFIG_KHCI, USB_HOST_CONFIG_EHCI, USB_HOST_CONFIG_OHCI, or USB_HOST_CONFIG_IP3516HS in file usb_host_config.
#endif

#include "fsl_debug_console.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "vglite_support.h"
#include "vg_lite.h"
#include "fsl_gpio.h"

#if defined(CPU_MIMXRT798SGFOA_cm33_core0) || defined(CPU_MIMXRT798SGFOA_cm33_core1)
#include "fsl_lcdif.h"
#endif
/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define APP_BUFFER_COUNT 2
#define DEFAULT_SIZE     256.0f;
/*******************************************************************************
 * Variables
 ******************************************************************************/
static int zoomOut    = 0;
static int scaleCount = 0;
static vg_lite_matrix_t matrix;
volatile bool s_frameDone = false;

#if (CUSTOM_VGLITE_MEMORY_CONFIG != 1)
#error "Application must be compiled with CUSTOM_VGLITE_MEMORY_CONFIG=1"
#else
#define VGLITE_COMMAND_BUFFER_SZ (128 * 1024)
/* On RT595S */
#if defined(CPU_MIMXRT595SFFOC_cm33) || defined(CPU_MIMXRT798SGFOA_cm33_core0) || defined(CPU_MIMXRT798SGFOA_cm33_core1)
#define VGLITE_HEAP_SZ 0x100000 /* 1 MB */
/* On RT1170 */
#elif defined(CPU_MIMXRT1176DVMAA_cm7) || defined(CPU_MIMXRT1166DVM6A_cm7)
#define VGLITE_HEAP_SZ 8912896 /* 8.5 MB */
#else
#error "Unsupported CPU !"
#endif
#if (720 * 1280 == (DEMO_PANEL_WIDTH) * (DEMO_PANEL_HEIGHT))
#define TW 720
/* On RT595S */
#if defined(CPU_MIMXRT595SFFOC_cm33)
/* Tessellation window = 720 x 128 */
#define TH 128
/* On RT798S */
#elif defined(CPU_MIMXRT798SGFOA_cm33_core0) || defined(CPU_MIMXRT798SGFOA_cm33_core1)
/* Tessellation window = 720 x 640 */
#define TH 640
/* On RT1170 */
#elif defined(CPU_MIMXRT1176DVMAA_cm7) || defined(CPU_MIMXRT1166DVM6A_cm7)
/* Tessellation window = 720 x 1280 */
#define TH 1280
#else
#error "Unsupported CPU !"
#endif
/* Panel RM67162. Supported only by platform RT595S. */
#elif (400 * 400 == (DEMO_PANEL_WIDTH) * (DEMO_PANEL_HEIGHT))
/* Tessellation window = 400 x 400 */
#define TW 400
#define TH 256
#else
/* Tessellation window = 256 x 256 */
#define TW 256
#define TH 256
#endif
/* Allocate the heap and set the command buffer(s) size */
AT_NONCACHEABLE_SECTION_ALIGN(uint8_t vglite_heap[VGLITE_HEAP_SZ], 64);

void *vglite_heap_base        = &vglite_heap;
uint32_t vglite_heap_size     = VGLITE_HEAP_SZ;
#endif
/*******************************************************************************
 * Variables
 ******************************************************************************/
usb_host_handle g_HostHandle;
extern usb_host_video_camera_instance_t g_Video;

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
static void vglite_task(void *pvParameters);

/*!
 * @brief host callback function.
 *
 * device attach/detach callback function.
 *
 * @param deviceHandle          device handle.
 * @param configurationHandle   attached device's configuration descriptor information.
 * @param eventCode             callback event code, please reference to enumeration host_event_t.
 *
 * @retval kStatus_USB_Success              The host is initialized successfully.
 * @retval kStatus_USB_NotSupported         The application don't support the configuration.
 */
static usb_status_t USB_HostEvent(usb_device_handle deviceHandle,
                                  usb_host_configuration_handle configurationHandle,
                                  uint32_t eventCode);

/*!
 * @brief application initialization.
 */
static void USB_HostApplicationInit(void);

/*!
 * @brief host freertos task function.
 *
 * @param g_HostHandle   host handle
 */
static void USB_HostTask(void *param);

/*!
 * @brief host video freertos task function.
 *
 * @param param   the host video instance pointer.
 */
static void USB_HostApplicationTask(void *param);
extern void USB_HostClockInit(void);
extern void USB_HostIsrEnable(void);
extern void USB_HostTaskFn(void *param);
void BOARD_InitHardware(void);
/*******************************************************************************
 * Code
 ******************************************************************************/

/*!
 * @brief host callback function.
 *
 * device attach/detach callback function.
 *
 * @param deviceHandle           device handle.
 * @param configurationHandle attached device's configuration descriptor information.
 * @param eventCode           callback event code, please reference to enumeration host_event_t.
 *
 * @retval kStatus_USB_Success              The host is initialized successfully.
 * @retval kStatus_USB_NotSupported         The application don't support the configuration.
 */
static usb_status_t USB_HostEvent(usb_device_handle deviceHandle,
                                  usb_host_configuration_handle configurationHandle,
                                  uint32_t eventCode)
{
    usb_status_t status = kStatus_USB_Success;

    switch (eventCode & 0x0000FFFFU)
    {
        case kUSB_HostEventAttach:
            status = USB_HostVideoEvent(deviceHandle, configurationHandle, eventCode);
            break;

        case kUSB_HostEventNotSupported:
            PRINTF("device not supported.\r\n");
            break;

        case kUSB_HostEventEnumerationDone:
            status = USB_HostVideoEvent(deviceHandle, configurationHandle, eventCode);
            break;

        case kUSB_HostEventDetach:
            status = USB_HostVideoEvent(deviceHandle, configurationHandle, eventCode);
            break;

        case kUSB_HostEventEnumerationFail:
            PRINTF("enumeration failed\r\n");
            break;

        default:
            break;
    }
    return status;
}

static void USB_HostApplicationInit(void)
{
    usb_status_t status = kStatus_USB_Success;

    USB_HostClockInit();

#if ((defined FSL_FEATURE_SOC_SYSMPU_COUNT) && (FSL_FEATURE_SOC_SYSMPU_COUNT))
    SYSMPU_Enable(SYSMPU, 0);
#endif /* FSL_FEATURE_SOC_SYSMPU_COUNT */

    status = USB_HostInit(CONTROLLER_ID, &g_HostHandle, USB_HostEvent);
    if (status != kStatus_USB_Success)
    {
        PRINTF("host init error\r\n");
        return;
    }
    USB_HostIsrEnable();

    PRINTF("host init done\r\n");
}

static void USB_HostTask(void *param)
{
    while (1)
    {
        USB_HostTaskFn(param);
    }
}

static void USB_HostApplicationTask(void *param)
{
	multicore_coms_msg rcvd_data;

	INIT_CORE1_PROFILE_PIN();
	MODEL_PreprocessInit();
	MCC_Init();
	FrameSlotQueuesInit();

	// Handshake at the beginning between both cores
	MCC_Rcv(&rcvd_data, 1);

    while (1)
    {
        USB_HostVideoTask(param);
    }
}

int main(void)
{
	uint32_t startupData;
	status_t status;

	BOARD_InitHardware();
    USB_HostApplicationInit();

    if (xTaskCreate(vglite_task, "vglite_task", configMINIMAL_STACK_SIZE + 2000, NULL, configMAX_PRIORITIES - 1, NULL) != pdPASS)
    {
        PRINTF("create vglite task error\r\n");
        while (1)
            ;
    }
    if (xTaskCreate(USB_HostTask, "usb host task", 2500L / sizeof(portSTACK_TYPE), g_HostHandle, 4, NULL) != pdPASS)
    {
        PRINTF("create host task error\r\n");
    }
    if (xTaskCreate(USB_HostApplicationTask, "app task", 2500L / sizeof(portSTACK_TYPE), &g_Video, 3, NULL) != pdPASS)
    {
        PRINTF("create video task error\r\n");
    }

    vTaskStartScheduler();
    while (1)
    {
        ;
    }
}

void gpu_blit(vg_lite_buffer_t *rt, vg_lite_buffer_t * source, bool clear, vg_lite_blend_t blend, uint32_t dest_off_x, uint32_t dest_off_y)
{
    vg_lite_buffer_t *blitBuffer = source;
    int8_t error = 0;

    if (rt == NULL)
    {
        PRINTF("vg_lite_get_renderTarget error\r\n");
        while (1)
            ;
    }

#if CLEAR_COLOR_STRIPE
    vg_lite_rectangle_t rectangle;
    rectangle.width = DEMO_BUFFER_WIDTH / 3;
    rectangle.height = DEMO_BUFFER_HEIGHT / 3;

    rectangle.x = 0;
    rectangle.y = 0;
    vglite_error = vg_lite_clear(target, &rectangle, 0x000000FF);
#endif
    if (clear)
    {
       vg_lite_clear(rt, NULL, 0x005F2F2F);
       vg_lite_finish();
    }
    vg_lite_identity(&matrix);

#if ROTATE_LANDSCAPE_TO_PORTRAIT
    //vg_lite_translate(DEMO_PANEL_WIDTH / 2 , DEMO_PANEL_HEIGHT / 2 , &matrix);
    vg_lite_translate(DEMO_PANEL_WIDTH , 0 , &matrix);
    vg_lite_rotate(90, &matrix);
    //vg_lite_scale(0.5, 0.5, &matrix);
#endif

    vg_lite_translate(dest_off_x , dest_off_y , &matrix);
    error = vg_lite_blit(rt, blitBuffer, &matrix, blend, 0, VG_LITE_FILTER_POINT);
    if (error)
    {
      PRINTF("Error****%d:%s\n", __LINE__, __func__);
      return;
    }

    vg_lite_finish();

    return;
}

static vg_lite_error_t init_vg_lite(void)
{
    vg_lite_error_t error = VG_LITE_SUCCESS;
    int fb_width, fb_height;

    // Set GPU command buffer size for this drawing task.
    error = vg_lite_set_command_buffer_size(VGLITE_COMMAND_BUFFER_SZ);
    if (error)
    {
        PRINTF("vg_lite_set_command_buffer_size() returned error %d\n", error);
        return error;
    }
    // Initialize the draw.
    error = vg_lite_init(TW, TH);
    if (error)
    {
        PRINTF("vg_lite engine init failed: vg_lite_init() returned error %d\r\n", error);
        return error;
    }

    return error;
}

static void vglite_task(void *pvParameters)
{
    status_t status;
    vg_lite_error_t error;
    uint32_t n = 0, fps_x_1000;

    status = BOARD_PrepareVGLiteController();
    if (status != kStatus_Success)
    {
        PRINTF("Prepare VGlite contolor error\r\n");
        while (1)
            ;
    }

    error = init_vg_lite();
    if (error)
    {
        PRINTF("init_vg_lite failed: init_vg_lite() returned error %d\r\n", error);
        while (1)
            ;
    }

    vTaskDelete(NULL);
}
