/**
 * @file lv_draw_rect.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "../core/lv_obj.h"
#include "lv_draw_rect.h"
#include "../misc/lv_assert.h"
#include "../core/lv_obj_event.h"
#include "../stdlib/lv_string.h"

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

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

/**********************
 *  STATIC PROTOTYPES
 **********************/

/**********************
 *  STATIC VARIABLES
 **********************/

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

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

LV_ATTRIBUTE_FAST_MEM void lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc)
{
    lv_memzero(dsc, sizeof(lv_draw_rect_dsc_t));
    dsc->bg_color = lv_color_white();
    dsc->bg_grad.stops[0].color = lv_color_white();
    dsc->bg_grad.stops[1].color = lv_color_black();
    dsc->bg_grad.stops[1].frac = 0xFF;
    dsc->bg_grad.stops_count = 2;
    dsc->border_color = lv_color_black();
    dsc->shadow_color = lv_color_black();
    dsc->bg_image_symbol_font = LV_FONT_DEFAULT;
    dsc->bg_opa = LV_OPA_COVER;
    dsc->bg_image_opa = LV_OPA_COVER;
    dsc->outline_opa = LV_OPA_COVER;
    dsc->border_opa = LV_OPA_COVER;
    dsc->shadow_opa = LV_OPA_COVER;
    dsc->border_side = LV_BORDER_SIDE_FULL;
}

void lv_draw_fill_dsc_init(lv_draw_fill_dsc_t * dsc)
{
    lv_memzero(dsc, sizeof(*dsc));
    dsc->opa = LV_OPA_COVER;
}

void lv_draw_border_dsc_init(lv_draw_border_dsc_t * dsc)
{
    lv_memzero(dsc, sizeof(*dsc));
    dsc->opa = LV_OPA_COVER;
    dsc->side = LV_BORDER_SIDE_FULL;
}

void lv_draw_box_shadow_dsc_init(lv_draw_box_shadow_dsc_t * dsc)
{
    lv_memzero(dsc, sizeof(*dsc));
    dsc->opa = LV_OPA_COVER;
}

void lv_draw_bg_image_dsc_init(lv_draw_bg_image_dsc_t * dsc)
{
    lv_memzero(dsc, sizeof(*dsc));
    dsc->opa = LV_OPA_COVER;
}


void lv_draw_rect(lv_layer_t * layer, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
{

    LV_PROFILER_BEGIN;
    bool has_shadow;
    bool has_fill;
    bool has_border;
    bool has_outline;
    bool has_bg_img;

    if(dsc->shadow_width == 0 ||
       dsc->shadow_opa <= LV_OPA_MIN ||
       (dsc->shadow_width == 1 && dsc->shadow_spread <= 0 &&
        dsc->shadow_offset_x == 0 && dsc->shadow_offset_y == 0)) {
        has_shadow = false;
    }
    else {
        has_shadow = true;
    }

    if(dsc->bg_opa <= LV_OPA_MIN) has_fill = false;
    else has_fill = true;

    if(dsc->bg_image_opa <= LV_OPA_MIN || dsc->bg_image_src == NULL) has_bg_img = false;
    else has_bg_img = true;

    if(dsc->border_opa <= LV_OPA_MIN || dsc->border_width == 0 || dsc->border_post == true) has_border = false;
    else has_border = true;

    if(dsc->outline_opa <= LV_OPA_MIN || dsc->outline_width == 0) has_outline = false;
    else has_outline = true;

    bool bg_cover = true;
    if(dsc->bg_opa < LV_OPA_COVER) bg_cover = false;
    else if(dsc->bg_grad.dir != LV_GRAD_DIR_NONE) {
        uint32_t s;
        for(s = 0; s < dsc->bg_grad.stops_count; s++) {
            if(dsc->bg_grad.stops[s].opa != LV_OPA_COVER) {
                bg_cover = false;
                break;
            }
        }
    }

    lv_draw_task_t * t;

    /*Shadow*/
    if(has_shadow) {
        /*Check whether the shadow is visible*/
        t = lv_draw_add_task(layer, coords);
        lv_draw_box_shadow_dsc_t * shadow_dsc = lv_malloc(sizeof(lv_draw_box_shadow_dsc_t));
        t->draw_dsc = shadow_dsc;
        shadow_dsc->base = dsc->base;
        shadow_dsc->radius = dsc->radius;
        shadow_dsc->color = dsc->shadow_color;
        shadow_dsc->width = dsc->shadow_width;
        shadow_dsc->spread = dsc->shadow_spread;
        shadow_dsc->opa = dsc->shadow_opa;
        shadow_dsc->ofs_x = dsc->shadow_offset_x;
        shadow_dsc->ofs_y = dsc->shadow_offset_y;
        shadow_dsc->bg_cover = bg_cover;
        t->type = LV_DRAW_TASK_TYPE_BOX_SHADOW;
        lv_draw_finalize_task_creation(layer, t);
    }

    /*Background*/
    if(has_fill) {
        lv_area_t bg_coords = *coords;
        /*If the border fully covers make the bg area 1px smaller to avoid artifacts on the corners*/
        if(dsc->border_width > 1 && dsc->border_opa >= LV_OPA_MAX && dsc->radius != 0) {
            bg_coords.x1 += (dsc->border_side & LV_BORDER_SIDE_LEFT) ? 1 : 0;
            bg_coords.y1 += (dsc->border_side & LV_BORDER_SIDE_TOP) ? 1 : 0;
            bg_coords.x2 -= (dsc->border_side & LV_BORDER_SIDE_RIGHT) ? 1 : 0;
            bg_coords.y2 -= (dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? 1 : 0;
        }

        t = lv_draw_add_task(layer, &bg_coords);
        lv_draw_fill_dsc_t * bg_dsc = lv_malloc(sizeof(lv_draw_fill_dsc_t));
        t->draw_dsc = bg_dsc;
        bg_dsc->base = dsc->base;
        bg_dsc->radius = dsc->radius;
        bg_dsc->color = dsc->bg_color;
        bg_dsc->grad = dsc->bg_grad;
        bg_dsc->opa = dsc->bg_opa;
        t->type = LV_DRAW_TASK_TYPE_FILL;
        lv_draw_finalize_task_creation(layer, t);
    }

    /*Background image*/
    if(has_bg_img) {

        t = lv_draw_add_task(layer, coords);


        lv_image_src_t src_type = lv_image_src_get_type(dsc->bg_image_src);
        lv_result_t res = LV_RESULT_OK;
        lv_image_header_t header;
        if(src_type == LV_IMAGE_SRC_VARIABLE || src_type == LV_IMAGE_SRC_FILE) {
            res  = lv_image_decoder_get_info(dsc->bg_image_src, &header);
        }
        else if(src_type == LV_IMAGE_SRC_UNKNOWN) {
            res = LV_RESULT_INVALID;
        }
        else {
            lv_memzero(&header, sizeof(header));
        }

        if(res == LV_RESULT_OK) {
            lv_draw_bg_image_dsc_t * bg_image_dsc = lv_malloc(sizeof(lv_draw_bg_image_dsc_t));
            t->draw_dsc = bg_image_dsc;
            bg_image_dsc->base = dsc->base;
            bg_image_dsc->radius = dsc->radius;
            bg_image_dsc->src = dsc->bg_image_src;
            bg_image_dsc->font = dsc->bg_image_symbol_font;
            bg_image_dsc->opa = dsc->bg_image_opa;
            bg_image_dsc->recolor = dsc->bg_image_recolor;
            bg_image_dsc->recolor_opa = dsc->bg_image_recolor_opa;
            bg_image_dsc->tiled = dsc->bg_image_tiled;
            bg_image_dsc->img_header = header;
            t->type = LV_DRAW_TASK_TYPE_BG_IMG;
            lv_draw_finalize_task_creation(layer, t);
        }
    }

    /*Border*/
    if(has_border) {
        t = lv_draw_add_task(layer, coords);
        lv_draw_border_dsc_t * border_dsc = lv_malloc(sizeof(lv_draw_border_dsc_t));
        t->draw_dsc = border_dsc;
        border_dsc->base = dsc->base;
        border_dsc->radius = dsc->radius;
        border_dsc->color = dsc->border_color;
        border_dsc->opa = dsc->border_opa;
        border_dsc->width = dsc->border_width;
        border_dsc->side = dsc->border_side;
        t->type = LV_DRAW_TASK_TYPE_BORDER;
        lv_draw_finalize_task_creation(layer, t);
    }

    /*Outline*/
    if(has_outline) {
        lv_area_t outline_coords = *coords;
        lv_area_increase(&outline_coords, dsc->outline_width + dsc->outline_pad, dsc->outline_width + dsc->outline_pad);
        t = lv_draw_add_task(layer, &outline_coords);
        lv_draw_border_dsc_t * outline_dsc = lv_malloc(sizeof(lv_draw_border_dsc_t));
        t->draw_dsc = outline_dsc;
        outline_dsc->base = dsc->base;
        outline_dsc->radius = dsc->radius == LV_RADIUS_CIRCLE ? LV_RADIUS_CIRCLE : dsc->radius + dsc->outline_width +
                              dsc->outline_pad;
        outline_dsc->color = dsc->outline_color;
        outline_dsc->opa = dsc->outline_opa;
        outline_dsc->width = dsc->outline_width;
        outline_dsc->side = LV_BORDER_SIDE_FULL;
        t->type = LV_DRAW_TASK_TYPE_BORDER;
        lv_draw_finalize_task_creation(layer, t);
    }

    LV_ASSERT_MEM_INTEGRITY();

    LV_PROFILER_END;
}


/**********************
 *   STATIC FUNCTIONS
 **********************/
