/**
 * @file lv_draw_rbasic.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "lv_draw_rbasic.h"
#if USE_LV_REAL_DRAW != 0

#include "../lv_hal/lv_hal_disp.h"
#include "../lv_misc/lv_font.h"
#include "lv_draw.h"

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

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

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

/**********************
 *  STATIC VARIABLES
 **********************/
static lv_color_t letter_bg_color;

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

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

/**
 * Put a pixel to the display
 * @param x x coordinate of the pixel
 * @param y y coordinate of the pixel
 * @param mask_p the pixel will be drawn on this area
 * @param color color of the pixel
 * @param opa opacity (ignored, only for compatibility with lv_vpx)
 */
void lv_rpx(lv_coord_t x, lv_coord_t y, const lv_area_t * mask_p, lv_color_t color, lv_opa_t opa)
{
    (void)opa;      /*Opa is used only for compatibility with lv_vpx*/

    lv_area_t area;
    area.x1 = x;
    area.y1 = y;
    area.x2 = x;
    area.y2 = y;

    lv_rfill(&area, mask_p, color, LV_OPA_COVER);
}

/**
 * Fill an area on the display
 * @param cords_p coordinates of the area to fill
 * @param mask_p fill only o this mask
 * @param color fill color
 * @param opa opacity (ignored, only for compatibility with lv_vfill)
 */
void lv_rfill(const lv_area_t * cords_p, const lv_area_t * mask_p,
              lv_color_t color, lv_opa_t opa)
{

    (void)opa;      /*Opa is used only for compatibility with lv_vfill*/

    lv_area_t masked_area;
    bool union_ok = true;

    if(mask_p != NULL) {
        union_ok = lv_area_intersect(&masked_area, cords_p, mask_p);
    } else {
        lv_area_t scr_area;
        lv_area_set(&scr_area, 0, 0, LV_HOR_RES - 1, LV_VER_RES - 1);
        union_ok = lv_area_intersect(&masked_area, cords_p, &scr_area);
    }

    if(union_ok != false) {
        lv_disp_fill(masked_area.x1, masked_area.y1, masked_area.x2, masked_area.y2, color);
    }
}

/**
 * Draw a letter to the display
 * @param pos_p left-top coordinate of the latter
 * @param mask_p the letter will be drawn only on this area
 * @param font_p pointer to font
 * @param letter a letter to draw
 * @param color color of letter
 * @param opa opacity of letter (ignored, only for compatibility with lv_vletter)
 */
void lv_rletter(const lv_point_t * pos_p, const lv_area_t * mask_p,
                const lv_font_t * font_p, uint32_t letter,
                lv_color_t color, lv_opa_t opa)
{
    (void)opa;      /*Opa is used only for compatibility with lv_vletter*/

    static uint8_t bpp1_opa_table[2] =  {0, 255};                   /*Opacity mapping with bpp = 1 (Just for compatibility)*/
    static uint8_t bpp2_opa_table[4] =  {0, 85, 170, 255};          /*Opacity mapping with bpp = 2*/
    static uint8_t bpp4_opa_table[16] = {0,   17,  34,  51,         /*Opacity mapping with bpp = 4*/
                                         68,  85,  102, 119,
                                         136, 153, 170, 187,
                                         204, 221, 238, 255
                                        };

    if(font_p == NULL) return;

    uint8_t letter_w = lv_font_get_width(font_p, letter);
    uint8_t letter_h = lv_font_get_height(font_p);
    uint8_t bpp = lv_font_get_bpp(font_p, letter);  /*Bit per pixel (1,2, 4 or 8)*/
    uint8_t * bpp_opa_table;
    uint8_t mask_init;
    uint8_t mask;

    switch(bpp) {
        case 1:
            bpp_opa_table = bpp1_opa_table;
            mask_init = 0x80;
            break;
        case 2:
            bpp_opa_table = bpp2_opa_table;
            mask_init = 0xC0;
            break;
        case 4:
            bpp_opa_table = bpp4_opa_table;
            mask_init = 0xF0;
            break;
        case 8:
            bpp_opa_table = NULL;
            mask_init = 0xFF;
            break;             /*No opa table, pixel value will be used directly*/
        default:
            return;        /*Invalid bpp. Can't render the letter*/
    }

    const uint8_t * map_p = lv_font_get_bitmap(font_p, letter);

    if(map_p == NULL) return;

    /*If the letter is completely out of mask don't draw it */
    if(pos_p->x + letter_w < mask_p->x1 || pos_p->x > mask_p->x2 ||
            pos_p->y + letter_h < mask_p->y1 || pos_p->y > mask_p->y2) return;

    lv_coord_t col, row;
    uint8_t col_bit;
    uint8_t col_byte_cnt;
    uint8_t width_byte_scr = letter_w >> 3;      /*Width in bytes (on the screen finally) (e.g. w = 11 -> 2 bytes wide)*/
    if(letter_w & 0x7) width_byte_scr++;
    uint8_t width_byte_bpp = (letter_w * bpp) >> 3;    /*Letter width in byte. Real width in the font*/
    if((letter_w * bpp) & 0x7) width_byte_bpp++;

    /* Calculate the col/row start/end on the map*/
    lv_coord_t col_start = pos_p->x >= mask_p->x1 ? 0 : mask_p->x1 - pos_p->x;
    lv_coord_t col_end = pos_p->x + letter_w <= mask_p->x2 ? letter_w : mask_p->x2 - pos_p->x + 1;
    lv_coord_t row_start = pos_p->y >= mask_p->y1 ? 0 : mask_p->y1 - pos_p->y;
    lv_coord_t row_end  = pos_p->y + letter_h <= mask_p->y2 ? letter_h : mask_p->y2 - pos_p->y + 1;

    /*Move on the map too*/
    map_p += (row_start * width_byte_bpp) + ((col_start * bpp) >> 3);

    uint8_t letter_px;
    for(row = row_start; row < row_end; row ++) {
        col_byte_cnt = 0;
        col_bit = (col_start * bpp) % 8;
        mask = mask_init >> col_bit;
        for(col = col_start; col < col_end; col ++) {
            letter_px = (*map_p & mask) >> (8 - col_bit - bpp);
            if(letter_px != 0) {
                lv_rpx(pos_p->x + col, pos_p->y + row, mask_p, lv_color_mix(color, letter_bg_color, bpp == 8 ? letter_px : bpp_opa_table[letter_px]), LV_OPA_COVER);
            }

            if(col_bit < 8 - bpp) {
                col_bit += bpp;
                mask = mask >> bpp;
            } else {
                col_bit = 0;
                col_byte_cnt ++;
                mask = mask_init;
                map_p ++;
            }
        }

        map_p += (width_byte_bpp) - col_byte_cnt;
    }
}

/**
 * When the letter is ant-aliased it needs to know the background color
 * @param bg_color the background color of the currently drawn letter
 */
void lv_rletter_set_background(lv_color_t color)
{
    letter_bg_color = color;
}

/**
 * Draw a color map to the display (image)
 * @param cords_p coordinates the color map
 * @param mask_p the map will drawn only on this area
 * @param map_p pointer to a lv_color_t array
 * @param opa opacity of the map (ignored, only for compatibility with 'lv_vmap')
 * @param chroma_keyed true: enable transparency of LV_IMG_LV_COLOR_TRANSP color pixels
 * @param alpha_byte true: extra alpha byte is inserted for every pixel (not supported, only l'v_vmap' can draw it)
 * @param recolor mix the pixels with this color
 * @param recolor_opa the intense of recoloring
 */
void lv_rmap(const lv_area_t * cords_p, const lv_area_t * mask_p,
             const uint8_t * map_p, lv_opa_t opa, bool chroma_key, bool alpha_byte,
             lv_color_t recolor, lv_opa_t recolor_opa)
{
    if(alpha_byte) return;      /*Pixel level opacity i not supported in real map drawing*/

    (void)opa;              /*opa is used only for compatibility with lv_vmap*/
    lv_area_t masked_a;
    bool union_ok;

    union_ok = lv_area_intersect(&masked_a, cords_p, mask_p);

    /*If there are common part of the mask and map then draw the map*/
    if(union_ok == false) return;

    /*Go to the first pixel*/
    lv_coord_t map_width = lv_area_get_width(cords_p);
    map_p += (masked_a.y1 - cords_p->y1) * map_width * sizeof(lv_color_t);
    map_p += (masked_a.x1 - cords_p->x1) * sizeof(lv_color_t);

    lv_coord_t row;
    if(recolor_opa == LV_OPA_TRANSP && chroma_key == false) {
        lv_coord_t mask_w = lv_area_get_width(&masked_a) - 1;
        for(row = masked_a.y1; row <= masked_a.y2; row++) {
            lv_disp_map(masked_a.x1, row, masked_a.x1 + mask_w, row, (lv_color_t *)map_p);
            map_p += map_width * sizeof(lv_color_t);               /*Next row on the map*/
        }
    } else {
        lv_color_t chroma_key_color = LV_COLOR_TRANSP;
        lv_coord_t col;
        for(row = masked_a.y1; row <= masked_a.y2; row++) {
            for(col = masked_a.x1; col <= masked_a.x2; col++) {
                lv_color_t * px_color = (lv_color_t *) &map_p[(uint32_t)(col - masked_a.x1) * sizeof(lv_color_t)];

                if(chroma_key && chroma_key_color.full == px_color->full) continue;

                if(recolor_opa != LV_OPA_TRANSP) {
                    lv_color_t recolored_px = lv_color_mix(recolor, *px_color, recolor_opa);

                    lv_rpx(col, row, mask_p, recolored_px, LV_OPA_COVER);
                } else {
                    lv_rpx(col, row, mask_p, *px_color, LV_OPA_COVER);
                }

            }
            map_p += map_width * sizeof(lv_color_t);               /*Next row on the map*/
        }
    }
}

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

#endif  /*USE_LV_REAL_DRAW*/