/****************************************************************************
*
*    Copyright 2012 - 2020 Vivante Corporation, Santa Clara, California.
*    All Rights Reserved.
*
*    Permission is hereby granted, free of charge, to any person obtaining
*    a copy of this software and associated documentation files (the
*    'Software'), to deal in the Software without restriction, including
*    without limitation the rights to use, copy, modify, merge, publish,
*    distribute, sub license, and/or sell copies of the Software, and to
*    permit persons to whom the Software is furnished to do so, subject
*    to the following conditions:
*
*    The above copyright notice and this permission notice (including the
*    next paragraph) shall be included in all copies or substantial
*    portions of the Software.
*
*    THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
*    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
*    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
*    IN NO EVENT SHALL VIVANTE AND/OR ITS SUPPLIERS BE LIABLE FOR ANY
*    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
*    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
*    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*****************************************************************************/

#include "elm_precom.h"

#if (VG_RENDER_TEXT==1)
#include "elm_text.h"
#endif /* VG_RENDER_TEXT */

static void multiply(vg_lite_matrix_t * matrix, vg_lite_matrix_t * mult)
{
    vg_lite_matrix_t temp;
    int row, column;

    /* Process all rows. */
    for (row = 0; row < 3; row++) {
        /* Process all columns. */
        for (column = 0; column < 3; column++) {
            /* Compute matrix entry. */
            temp.m[row][column] =  (matrix->m[row][0] * mult->m[0][column])
            + (matrix->m[row][1] * mult->m[1][column])
            + (matrix->m[row][2] * mult->m[2][column]);
        }
    }

    memcpy(matrix, &temp, sizeof(temp));

}

static vg_lite_filter_t quality_to_filter(ELM_QUALITY quality)
{
    switch (quality) {
        case ELM_QULIATY_HI:
            return VG_LITE_FILTER_BI_LINEAR;
            break;

        case ELM_QUALITY_MED:
            return VG_LITE_FILTER_LINEAR;
            break;

        case ELM_QUALITY_LOW:
        default:
            return VG_LITE_FILTER_POINT;
            break;
    }
}

static vg_lite_pattern_mode_t pat_to_pad(ELM_PATTERN_MODE mode)
{
    switch (mode) {
        case ELM_PATTERN_MODE_PAD:
            return VG_LITE_PATTERN_PAD;
            break;

        default:
            return VG_LITE_PATTERN_COLOR;
            break;
    }
}
static vg_lite_error_t draw_evo_pattern(el_Obj_Buffer *buff, el_Obj_Group *ego,int * index)
{
    el_Obj_EVO *evo;
    vg_lite_error_t error = VG_LITE_INVALID_ARGUMENT;
    vg_lite_color_t color;
    vg_lite_filter_t filter;
    vg_lite_blend_t blend;
    vg_lite_fill_t rule; 
    vg_lite_buffer_t *buffer;
    vg_lite_matrix_t mat;
    vg_lite_pattern_mode_t pat_mode;
    int width = 0;
    int height = 0;

    int start = *index;
    int i = start;
    evo = &ego->group.objects[i];
    width  = (int)(evo->data.path.bounding_box[2] - evo->data.path.bounding_box[0]);
    height = (int)(evo->data.path.bounding_box[3] - evo->data.path.bounding_box[1]);
    buffer = (vg_lite_buffer_t *)malloc(sizeof(vg_lite_buffer_t));
    memset(buffer,0,sizeof(vg_lite_buffer_t));
    buffer->width = width;
    buffer->height = height;
    buffer->format = VG_LITE_RGBA8888;
    error = vg_lite_allocate(buffer);
    vg_lite_clear(buffer,NULL,0xffffffff);
    i++;
    evo = &ego->group.objects[i];
    while(evo->is_pattern)
    {
        blend = (vg_lite_blend_t)evo->attribute.blend;
        rule = (vg_lite_fill_t)evo->attribute.fill_rule;
        color = (vg_lite_color_t)evo->attribute.paint.color;
        memcpy(&mat, &(evo->attribute.transform.matrix), sizeof(mat));
        error = vg_lite_draw(buffer, &evo->data.path,
                                rule,
                                &mat,
                                blend,
                                color);
        if(error)
            return error;
        i++;
        evo = &ego->group.objects[i];
    }
    *index = i - 1;
    evo = &ego->group.objects[start];
    blend = (vg_lite_blend_t)evo->attribute.blend;
    rule = (vg_lite_fill_t)evo->attribute.fill_rule;
    color = (vg_lite_color_t)evo->attribute.paint.color;
    memcpy(&mat, &(evo->attribute.transform.matrix), sizeof(mat));
    filter = VG_LITE_FILTER_POINT;
    pat_mode = VG_LITE_PATTERN_COLOR;
    error = vg_lite_draw_pattern(&buff->buffer, &evo->data.path,
                            rule,
                            &mat,
                            buffer,
                            &mat,
                            blend,
                            pat_mode,
                            color,
                            filter);
    vg_lite_finish();
    vg_lite_free(buffer);
    free(buffer);
    return error;
}

static vg_lite_error_t draw_evo(el_Obj_Buffer *buff, el_Obj_EVO *evo, vg_lite_matrix_t *mat)
{
    el_Obj_EBO *pattern;
    vg_lite_error_t error = VG_LITE_INVALID_ARGUMENT;
    vg_lite_color_t color;
    vg_lite_filter_t filter;
    vg_lite_matrix_t mat_pattern;
    ELM_PAINT_TYPE paint_type = evo->attribute.paint.type;
    vg_lite_blend_t blend = (vg_lite_blend_t)evo->attribute.blend;
    vg_lite_fill_t rule = (vg_lite_fill_t)evo->attribute.fill_rule;
    vg_lite_pattern_mode_t pat_mode = pat_to_pad(evo->attribute.paint.pattern.mode);

    switch (paint_type) {
    case ELM_PAINT_GRADIENT:
        memcpy(&evo->attribute.paint.grad->data.grad.matrix,
               &evo->attribute.paint.grad->data.transform.matrix,
               sizeof(evo->attribute.paint.grad->data.transform.matrix));
#if !DDRLESS
        error = vg_lite_draw_gradient(&buff->buffer, &evo->data.path,
                                      rule,
                                      mat,
                                      &evo->attribute.paint.grad->data.grad,
                                      blend);
#else
        error = vg_lite_draw_gradient(NULL, &evo->data.path,
                                      rule,
                                      mat,
                                      &evo->attribute.paint.grad->data.grad,
                                      blend);
#endif
        break;
    case ELM_PAINT_RADIAL_GRADIENT:
        memcpy(&evo->attribute.paint.radgrad->data.rad_grad.matrix,
               &evo->attribute.paint.radgrad->data.transform.matrix,
               sizeof(evo->attribute.paint.radgrad->data.transform.matrix));

        error = vg_lite_draw_radial_gradient(&buff->buffer, &evo->data.path,
                                      rule,
                                      mat,
                                      &evo->attribute.paint.radgrad->data.rad_grad,
                                      0,
                                      blend,
                                      VG_LITE_FILTER_LINEAR);
        break;
    case ELM_PAINT_COLOR:
        color = (vg_lite_color_t)evo->attribute.paint.color;

#if !DDRLESS
        error = vg_lite_draw(&buff->buffer, &evo->data.path,
                             rule,
                             mat,
                             blend,
                             color);
#else
        error = vg_lite_draw(NULL, &evo->data.path,
                             rule,
                             mat,
                             blend,
                             color);
#endif
        break;
    case ELM_PAINT_PATTERN:
        pattern = (el_Obj_EBO *)(evo->attribute.paint.pattern.pattern);
        blend = (vg_lite_blend_t)(pattern->attribute.blend);
        filter = quality_to_filter(evo->attribute.quality);
        color = (vg_lite_color_t)pattern->attribute.paint.color;
        memcpy(&mat_pattern, &pattern->attribute.transform.matrix,
               sizeof(pattern->attribute.transform.matrix));

#if !DDRLESS
        error = vg_lite_draw_pattern(&buff->buffer, &evo->data.path, rule, mat,
                                     &pattern->data.buffer, &mat_pattern, blend,
                                     pat_mode, color, filter);
#else
        error = vg_lite_draw_pattern(NULL, &evo->data.path, rule, mat,
                                     &pattern->data.buffer, &mat_pattern, blend,
                                     pat_mode, color, filter);
#endif
        break;
    case ELM_PAINT_TEXT:
#if (VG_RENDER_TEXT==1)
        error = draw_text(buff, evo, mat);
#endif /* VG_RENDER_TEXT */
        break;
    }

    return error;
}

static vg_lite_error_t draw_ebo(el_Obj_Buffer *buff, el_Obj_EBO *ebo, vg_lite_matrix_t *mat)
{
    vg_lite_error_t error;
    vg_lite_buffer_t *image_buffer = &ebo->data.buffer;
    vg_lite_blend_t   blend = (vg_lite_blend_t)ebo->attribute.blend;
    vg_lite_color_t   color = ebo->attribute.paint.color;
    vg_lite_filter_t filter = quality_to_filter(ebo->attribute.quality);

    if (image_buffer->format >= VG_LITE_INDEX_1 && image_buffer->format <= VG_LITE_INDEX_8)
    {
        vg_lite_set_CLUT(ebo->clut_count, ebo->clut);
    }

    image_buffer->image_mode = VG_LITE_NORMAL_IMAGE_MODE;
#if !DDRLESS
    error = vg_lite_blit(&buff->buffer, image_buffer, mat, blend, color, filter);
#else
    error = vg_lite_blit(NULL, image_buffer, mat, blend, color, filter);
#endif
    return error;
}
/*!
 @abstract Clear a render buffer with specified color and dimension.

 @discussion
 This function is called to clear full or partial of the buffer. If the rectangle is out of buffer space, the intersect portion will be cleared.

 @param buffer
 A render buffer handle.

 @param color
 Clear color value.

 @param x
 x origin of partical clear rectangle.

 @param y
 y origin of partial clear rectangle.

 @param width
 width of partical clear rectangle.

 @param height
 height of partical clear rectangle.

 @param full
 Flag to indicate a full buffer clear. If true, the dimension parameters will be ignored.

 @return bool
 Indicate the clear operation is set up correctly.
 */
BOOL ElmClear(ElmBuffer buffer, uint32_t color, uint32_t x, uint32_t y, uint32_t width, uint32_t height, BOOL full)
{
#if !DDRLESS
    vg_lite_error_t error = VG_LITE_SUCCESS;
    el_Obj_Buffer *buff = (el_Obj_Buffer *)get_object(buffer);
    vg_lite_rectangle_t rectangle;
    rectangle.x = x;
    rectangle.y = y;
    rectangle.width = width;
    rectangle.height = height;

    if (full == 1)
    {
        error = vg_lite_clear(&buff->buffer, NULL, color);
    }
    else
    {
        error = vg_lite_clear(&buff->buffer, &rectangle, color);
    }
    return ((error == VG_LITE_SUCCESS) ? TRUE : FALSE);
#else
    return TRUE;
#endif
}

/*!
 @abstract Finish all rendering on GPU issued before this call.

 @discussion
 This function tells the engine that it has finished the frame data and GPU can draw it now. It's blocked until GPU rendering done.

 @return
 If the opeartion is successfully done or not.
 */
BOOL ElmFinish()
{
    vg_lite_finish();
    return TRUE;
}

/*!
 @abstract Flush all rendering command to GPU issued before this call.

 @discussion
 This function tells the engine to start kicking off command to GPU side, it will return immediately after firing off GPU.

 @return
 If the opeartion is successfully done or not.
 */
BOOL ElmFlush()
{
    vg_lite_flush();
    return TRUE;
}



/*!
 @abstract Draw a graphics object onto current render target

 @discussion
 This is an enssentail function to do the real job, it takes all current setting of the elementary object and
 render into theb buffer target.

 @param buffer
 The render target that an elmentary object will be rendered into.

 @param obj
 The elmentary object will be draw into render target.

 @return bool
 The draw operation for this elmentary object is sucessful.
 */
BOOL ElmDraw(ElmBuffer buffer, ElmHandle object)
{
    BOOL status = TRUE;
    el_Object *elm;
    el_Obj_Buffer *buff;
    vg_lite_error_t error;
    vg_lite_matrix_t mat;
    elm = get_object(object);

    if (!elm)
    {
        return FALSE;
    }

#if !DDRLESS
    buff = (el_Obj_Buffer *)get_object(buffer);
    if (buff == NULL)
    {
        return FALSE;
    }
#else
    buff = NULL;
#endif

    if (elm->type == ELM_OBJECT_TYPE_EGO)
    {
        el_Obj_EVO *evo;
        el_Obj_Group *ego = (el_Obj_Group *)elm;
        vg_lite_matrix_t mat_group, res_mat;

        if (ego->group.count > 0)
        {
            int i;
            memcpy(&mat_group, &(ego->transform.matrix), sizeof(ego->transform.matrix));
            for (i = 0; i < ego->group.count; i++)
            {
                evo = &ego->group.objects[i];

                /* Font objects may generate empty objects */
                if (evo->object.handle == ELM_NULL_HANDLE)
                    continue;

                if(evo->is_image)
                {
                    ElmHandle ebo_handle;
                    el_Object *elm_ebo;
                    el_Obj_EBO *ebo;
                    ebo_handle = ElmCreateObjectFromFile(ELM_OBJECT_TYPE_EBO, evo->eboname);
                    elm_ebo = get_object(ebo_handle);
                    ebo = (el_Obj_EBO *)elm_ebo;
                    memcpy(&mat, &evo->defaultAttrib.transform.matrix, sizeof(mat));

                    error = draw_ebo(buff, ebo, &mat);
                    if (error)
                    {
                        status = FALSE;
                    }
                    continue;
                }
                memcpy(&mat, &(evo->attribute.transform.matrix), sizeof(mat));
                memcpy(&res_mat, &mat_group, sizeof(mat_group));
                multiply(&res_mat, &mat);

                if(evo->has_pattern)
                    error = draw_evo_pattern(buff,ego,&i);
                else
                    error = draw_evo(buff, evo, &res_mat);
                if (error)
                {
                    status = FALSE;
                    break;
                }
            }
        }
    }
    else if (elm->type == ELM_OBJECT_TYPE_EVO)
    {
        el_Obj_EVO *evo = (el_Obj_EVO *)elm;
        memcpy(&mat, &(evo->attribute.transform.matrix), sizeof(mat));

        error = draw_evo(buff, evo, &mat);
        if (error)
        {
            status = FALSE;
        }
    }
    else if (elm->type == ELM_OBJECT_TYPE_EBO)
    {
        el_Obj_EBO *ebo = (el_Obj_EBO *)elm;
        memcpy(&mat, &(ebo->attribute.transform.matrix), sizeof(mat));

        error = draw_ebo(buff, ebo, &mat);
        if (error)
        {
            status = FALSE;
        }

    }
    else if (elm->type == ELM_OBJECT_TYPE_BUF)
    {
        el_Obj_Buffer *src = (el_Obj_Buffer *)elm;
        vg_lite_identity(&mat);

#if !DDRLESS
        if (VG_LITE_SUCCESS !=
            vg_lite_blit(&buff->buffer, &src->buffer, &mat, VG_LITE_BLEND_NONE, 0,
                         VG_LITE_FILTER_BI_LINEAR)
            )
#else
        if (VG_LITE_SUCCESS !=
            vg_lite_blit(NULL, &src->buffer, &mat, VG_LITE_BLEND_NONE, 0,
                         VG_LITE_FILTER_BI_LINEAR)
            )
#endif
        {
            status = FALSE;
        }
    }
    return status;
}
