/**
 * @file lv_draw_private.h
 *
 */

/**
 * Modified by NXP in 2024
 */

#ifndef LV_DRAW_PRIVATE_H
#define LV_DRAW_PRIVATE_H

#ifdef __cplusplus
extern "C" {
#endif

/*********************
 *      INCLUDES
 *********************/

#include "lv_draw.h"
#include "../osal/lv_os_private.h"
#include "../misc/cache/lv_cache.h"

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

struct _lv_draw_task_t {
    lv_draw_task_t * next;

    lv_draw_task_type_t type;

    /**
     * The area where to draw
     */
    lv_area_t area;

    /**
     * The real draw area. E.g. for shadow, outline, or transformed images it's different from `area`
     */
    lv_area_t _real_area;

    /** The original area which is updated*/
    lv_area_t clip_area_original;

    /**
     * The clip area of the layer is saved here when the draw task is created.
     * As the clip area of the layer can be changed as new draw tasks are added its current value needs to be saved.
     * Therefore during drawing the layer's clip area shouldn't be used as it might be already changed for other draw tasks.
     */
    lv_area_t clip_area;
    lv_layer_t * target_layer;

#if LV_DRAW_TRANSFORM_USE_MATRIX
    /** Transform matrix to be applied when rendering the layer */
    lv_matrix_t matrix;
#endif

    /* Reference to the draw unit for debug or draw context purposes */
    lv_draw_unit_t * draw_unit;

    volatile int state;              /** int instead of lv_draw_task_state_t to be sure its atomic */

    void * draw_dsc;

    /** Opacity of the layer */
    lv_opa_t opa;

    /**
     * The ID of the draw_unit which should take this task
     */
    uint8_t preferred_draw_unit_id;

    /**
     * Set to which extent `preferred_draw_unit_id` is good at this task.
     * 80: means 20% better (faster) than software rendering
     * 100: the default value
     * 110: means 10% worse (slower) than software rendering
     */
    uint8_t preference_score;

};

struct _lv_draw_mask_t {
    void * user_data;
};

struct _lv_draw_unit_t {
    lv_draw_unit_t * next;

    /**
     * Name and ID of the draw unit, for debugging purposes only.
     */
    const char * name;
    int32_t idx;

    /**
     * Called to try to assign a draw task to itself.
     * `lv_draw_get_next_available_task` can be used to get an independent draw task.
     * A draw task should be assign only if the draw unit can draw it too
     * @param draw_unit     pointer to the draw unit
     * @param layer         pointer to a layer on which the draw task should be drawn
     * @return              >=0:    The number of taken draw task:
     *                                  0 means the task has not yet been completed.
     *                                  1 means a new task has been accepted.
     *                      -1:     The draw unit wanted to work on a task but couldn't do that
     *                              due to some errors (e.g. out of memory).
     *                              It signals that LVGL should call the dispatcher later again
     *                              to let draw unit try to start the rendering again.
     */
    int32_t (*dispatch_cb)(lv_draw_unit_t * draw_unit, lv_layer_t * layer);

    /**
     *
     * @param draw_unit
     * @param task
     * @return
     */
    int32_t (*evaluate_cb)(lv_draw_unit_t * draw_unit, lv_draw_task_t * task);

    /**
     * Called to signal the unit to complete all tasks in order to return their ready status.
     * This callback can be implemented in case of asynchronous task processing.
     * Below is an example to show the difference between synchronous and asynchronous:
     *
     * Synchronous:
     * LVGL thread              DRAW thread                 HW
     *
     * task1             -->    submit               -->    Receive task1
     *                          wait_for_finish()
     *                   <--    task1->state = READY <--    Complete task1
     * task2             -->    submit               -->    Receive task2
     *                          wait_for_finish()
     *                          task2->state = READY <--    Complete task2
     * task3             -->    submit               -->    Receive task3
     *                          wait_for_finish()
     *                   <--    task3->state = READY <--    Complete task3
     * task4             -->    submit               -->    Receive task4
     *                          wait_for_finish()
     *                   <--    task4->state = READY <--    Complete task4
     * NO MORE TASKS
     *
     *
     * Asynchronous:
     * LVGL thread              DRAW thread                 HW
     *                                                      is IDLE
     * task1             -->    queue task1
     *                          submit               -->    Receive task1
     * task2             -->    queue task2                 is BUSY (with task1)
     * task3             -->    queue task3                 still BUSY (with task1)
     * task4             -->    queue task4                 becomes IDLE
     *                   <--    task1->state = READY <--    Complete task1
     *                          submit               -->    Receive task2, task3, task4
     * NO MORE TASKS
     * wait_for_finish_cb()     wait_for_finish()
     *                                               <--    Complete task2, task3, task4
     *                   <--    task2->state = READY <--
     *                   <--    task3->state = READY <--
     *                   <--    task4->state = READY <--
     *
     * @param draw_unit
     * @return
     */
    int32_t (*wait_for_finish_cb)(lv_draw_unit_t * draw_unit);

    /**
     * Called to delete draw unit.
     * @param draw_unit
     * @return
     */
    int32_t (*delete_cb)(lv_draw_unit_t * draw_unit);

    /**
     * Called when an event is sent to the draw unit.
     * @param event pointer to the event descriptor
     */
    void (*event_cb)(lv_event_t * event);
};

typedef struct {
    lv_draw_unit_t * unit_head;
    uint32_t unit_cnt;
    uint32_t used_memory_for_layers; /* measured as bytes */
#if LV_USE_OS
    lv_thread_sync_t sync;
#else
    volatile int dispatch_req;
#endif
    lv_mutex_t circle_cache_mutex;
    bool task_running;
} lv_draw_global_info_t;

/**********************
 * GLOBAL PROTOTYPES
 **********************/

/**********************
 *      MACROS
 **********************/

#ifdef __cplusplus
} /*extern "C"*/
#endif

#endif /*LV_DRAW_PRIVATE_H*/
