/**
 * @file lv_rle.c
 */

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

#include "../../stdlib/lv_string.h"
#include "lv_rle.h"

#if LV_USE_RLE

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

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

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

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

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

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

uint32_t lv_rle_decompress(const uint8_t * input,
                           uint32_t input_buff_len, uint8_t * output,
                           uint32_t output_buff_len, uint8_t blk_size)
{
    uint32_t ctrl_byte;
    uint32_t rd_len = 0;
    uint32_t wr_len = 0;

    while(rd_len < input_buff_len) {
        ctrl_byte = input[0];
        rd_len++;
        input++;
        if(rd_len > input_buff_len)
            return 0;

        if(ctrl_byte & 0x80) {
            /* copy directly from input to output */
            uint32_t bytes = blk_size * (ctrl_byte & 0x7f);
            rd_len += bytes;
            if(rd_len > input_buff_len)
                return 0;

            wr_len += bytes;
            if(wr_len > output_buff_len) {
                if(wr_len > output_buff_len + blk_size)
                    return 0; /* Error */
                lv_memcpy(output, input, output_buff_len - (wr_len - bytes));
                return output_buff_len;
            }

            lv_memcpy(output, input, bytes);
            output += bytes;
            input += bytes;
        }
        else {
            rd_len += blk_size;
            if(rd_len > input_buff_len)
                return 0;

            wr_len += blk_size * ctrl_byte;
            if(wr_len > output_buff_len) {
                if(wr_len > output_buff_len + blk_size)
                    return 0; /* Error happened */

                /* Skip the last pixel, which could overflow output buffer.*/
                for(uint32_t i = 0; i < ctrl_byte - 1; i++) {
                    lv_memcpy(output, input, blk_size);
                    output += blk_size;
                }
                return output_buff_len;
            }

            if(blk_size == 1) {
                /* optimize the most common case. */
                lv_memset(output, input[0], ctrl_byte);
                output += ctrl_byte;
            }
            else {
                for(uint32_t i = 0; i < ctrl_byte; i++) {
                    lv_memcpy(output, input, blk_size);
                    output += blk_size;
                }
            }
            input += blk_size;
        }
    }

    return wr_len;
}

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

#endif /*LV_USE_RLE*/
