/*
 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
 *
 * Licensed under the Apache License, Version 2.0 (the License);
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "../inc/image-sample-utils.h"
#include "config.h"
#include "../inc/image-common.h"

#include <Elementary.h>
#include <sys/time.h>
#include <assert.h>
#include <stddef.h>
#include <string.h>
#include <stdbool.h>
#include <math.h>
#include <image_util.h>
#include <sys/types.h>

static const char *file_ext_jpg_string = "jpg";
static const char *file_ext_png_string = "png";
static const char *file_ext_gif_string = "gif";
static const char *file_ext_bmp_string = "bmp";
static const char *file_ext_tif_string = "tif";

typedef struct _bgra32
{
    u_int8_t b;
    u_int8_t g;
    u_int8_t r;
    u_int8_t a;
} bgra32;

typedef u_int16_t rgb16;
typedef unsigned char uchar;

#define BRGA_BPP 4
#define RGB24_BPP 3

#define RGB32_CHANEL_BIT_SIZE 8
#define RGB32_DEFAULT_ALPHA 255

#define RGB16_R_SIZE 5
#define RGB16_G_SIZE 6
#define RGB16_B_SIZE 5

#define RGB16_B_SHIFT 0
#define RGB16_G_SHIFT (RGB16_B_SHIFT + RGB16_B_SIZE)
#define RGB16_R_SHIFT (RGB16_G_SHIFT + RGB16_G_SHIFT)

static void _convert_bgra8888_to_rgb565( uchar *dest, const uchar *src,
                                         const int width, const int height,
                                         const int d_size, const int s_size);
static void _convert_bgra8888_to_yuv420( uchar *dest, const uchar *src,
                                         const int width, const int height,
                                         const int d_size, const int s_size);
static void _convert_rgb565_to_bgra8888( uchar *dest, const uchar *src,
                                         const int width, const int height,
                                         const int d_size, const int s_size);
static void _convert_rgb565_to_yuv420( uchar *dest, const uchar *src,
                                         const int width, const int height,
                                       const int d_size, const int s_size);
static void _convert_yuv420_to_rgb565( uchar *dest, const uchar *src,
                                         const int width, const int height,
                                         const int d_size, const int s_size);
static void _convert_yuv420_to_bgra8888( uchar *dest, const uchar *src,
                                         const int width, const int height,
                                         const int d_size, const int s_size);
static void _convert_yuv422_to_bgra8888( uchar *dest, const uchar *src,
                                         const int width, const int height,
                                         const int d_size, const int s_size);
static void _convert_rgb888_to_bgra8888( uchar *dest, const uchar *src,
                                         const int width, const int height,
                                         const int d_size, const int s_size);

static inline rgb16 bgra_to_rgb(const bgra32 src);
static inline bgra32 rgb_to_bgra(const rgb16 src);
static inline uchar clamp(int x);
static inline uchar bgra_to_yuv_y(const bgra32 src);
static inline uchar bgra_to_yuv_u(const bgra32 src);
static inline uchar bgra_to_yuv_v(const bgra32 src);
static inline uchar yuv_to_r(const uchar yuv_y, const uchar yuv_u, const uchar yuv_v);
static inline uchar yuv_to_g(const uchar yuv_y, const uchar yuv_u, const uchar yuv_v);
static inline uchar yuv_to_b(const uchar yuv_y, const uchar yuv_u, const uchar yuv_v);

static bool _read_file(const char *fullpath, unsigned char **buff, long long *file_length);

const char *image_file_format_to_str(image_file_format_e format)
{
    switch(format)
    {
        case IMAGE_FILE_FORMAT_JPG:
            return file_ext_jpg_string;
            break;

        case IMAGE_FILE_FORMAT_PNG:
            return file_ext_png_string;
            break;

        case IMAGE_FILE_FORMAT_GIF:
            return file_ext_gif_string;
            break;

        case IMAGE_FILE_FORMAT_BMP:
            return file_ext_bmp_string;
            break;

        case IMAGE_FILE_FORMAT_TIFF:
            return file_ext_tif_string;
            break;

        default:
            break;
    }

    assert(false);
    return "";
}

long long get_ticks()
{
    struct timeval time;

    gettimeofday(&time, NULL);
    long long mseconds = ((time.tv_sec) * 1000 + time.tv_usec / 1000.0);

    return mseconds;
}

const char *filename_extenstion_get(const char *file_path)
{
    const char *pos = strrchr(file_path, '.');
    if (pos == NULL || pos == file_path)
    {
        return NULL;
    }
    else
    {
        return pos + 1;
    }
}

image_file_format_e image_file_format_to_id(const char *format)
{
    if (0 == strcasecmp(format, file_ext_jpg_string) )
    {
        return IMAGE_FILE_FORMAT_JPG;
    }
    else if (0 == strcasecmp(format, file_ext_png_string) )
    {
        return IMAGE_FILE_FORMAT_PNG;
    }
    else if (0 == strcasecmp(format, file_ext_gif_string) )
    {
        return IMAGE_FILE_FORMAT_GIF;
    }
    else if (0 == strcasecmp(format, file_ext_bmp_string) )
    {
        return IMAGE_FILE_FORMAT_BMP;
    }
    else if (0 == strcasecmp(format, file_ext_tif_string) )
    {
        return IMAGE_FILE_FORMAT_TIFF;
    }
    return IMAGE_FILE_FORMAT_UNKNOWN;
}

int image_sample_util_resize(const int src_w, const int src_h, const uchar *src, const int dest_w, const int dest_h,  uchar *dest)
{
    if (src_w <= 0 || src_h <= 0 || !src || dest_w <= 0 || dest_h <= 0 || !dest)
    {
        return IMAGE_UTIL_ERROR_INVALID_PARAMETER;
    }

    int h = 0, w = 0;
    float t, u, coef;
    t = u = coef = 0.0;
    float c1, c2, c3, c4;
    c1 = c2 = c3 = c4 = 0.0;
    u_int32_t pixel1, pixel2, pixel3, pixel4;
    pixel1 = pixel2 = pixel3 = pixel4 = 0;
    u_int32_t *pixel_res = NULL;
    u_int32_t red, green, blue, alpha;
    red = green = blue = alpha = 0;

    int i = 0, j = 0;
    for (j = 0; j < dest_h; j++)
    {
        coef = (float) (j) / (float) (dest_h - 1) * (src_h - 1);
        h = (int) floor(coef);
        if (h < 0)
        {
            h = 0;
        }
        else
        {
            if (h >= src_h - 1)
            {
                h = src_h - 2;
            }
        }
        u = coef - h;

        for (i = 0; i < dest_w; i++)
        {
            coef = (float) (i) / (float) (dest_w - 1) * (src_w - 1);
            w = (int) floor(coef);
            if (w < 0)
            {
                w = 0;
            }
            else
            {
                if (w >= src_w - 1)
                {
                    w = src_w - 2;
                }
            }
            t = coef - w;

            c1 = (1 - t) * (1 - u);
            c2 = t * (1 - u);
            c3 = t * u;
            c4 = (1 - t) * u;

            pixel1 = *((u_int32_t*) (src + BRGA_BPP * (h * src_w + w)));
            pixel2 = *((u_int32_t*) (src + BRGA_BPP * (h * src_w + w + 1)));
            pixel3 = *((u_int32_t*) (src + BRGA_BPP * ((h + 1) * src_w + w + 1)));
            pixel4 = *((u_int32_t*) (src + BRGA_BPP * ((h + 1) * src_w + w)));

            blue = (uchar) pixel1 * c1 + (uchar) pixel2 * c2
                    + (uchar) pixel3 * c3 + (uchar) pixel4 * c4;
            green = (uchar) (pixel1 >> 8) * c1
                    + (uchar) (pixel2 >> 8) * c2
                    + (uchar) (pixel3 >> 8) * c3
                    + (uchar) (pixel4 >> 8) * c4;
            red = (uchar) (pixel1 >> 16) * c1
                    + (uchar) (pixel2 >> 16) * c2
                    + (uchar) (pixel3 >> 16) * c3
                    + (uchar) (pixel4 >> 16) * c4;
            alpha = (uchar) (pixel1 >> 24) * c1
                    + (uchar) (pixel2 >> 24) * c2
                    + (uchar) (pixel3 >> 24) * c3
                    + (uchar) (pixel4 >> 24) * c4;

            pixel_res = (u_int32_t*)(dest + BRGA_BPP * (i + j * dest_w));
            *pixel_res = ((u_int32_t) alpha << 24) | ((u_int32_t) red << 16)
                    | ((u_int32_t) green << 8) | (blue);
        }
    }
    return IMAGE_UTIL_ERROR_NONE;
}

int image_sample_util_convert_colorspace( uchar *dest , image_util_colorspace_e dest_colorspace,
                                   const uchar *src , int width, int height,
                                   image_util_colorspace_e src_colorspace)
{
    RETVM_IF(NULL == src, IMAGE_UTIL_ERROR_INVALID_PARAMETER, "source buffer in NULL");
    RETVM_IF(NULL == dest, IMAGE_UTIL_ERROR_INVALID_PARAMETER, "destination buffer in NULL");
    RETVM_IF(width <= 0 || height <= 0, IMAGE_UTIL_ERROR_INVALID_PARAMETER, "width or heightis incorrect");
    int error = IMAGE_UTIL_ERROR_NONE;


    if (IMAGE_UTIL_COLORSPACE_BGRA8888 != src_colorspace
            && IMAGE_UTIL_COLORSPACE_RGB565 != src_colorspace
            && IMAGE_UTIL_COLORSPACE_I420 != src_colorspace
            && IMAGE_UTIL_COLORSPACE_UYVY != src_colorspace
            && IMAGE_UTIL_COLORSPACE_RGB888 != src_colorspace)
    {
        ERR("src_colorspace not supported yet");
        return IMAGE_UTIL_ERROR_NOT_SUPPORTED_FORMAT;
    }

    if (IMAGE_UTIL_COLORSPACE_BGRA8888 != dest_colorspace
            && IMAGE_UTIL_COLORSPACE_RGB565 != dest_colorspace
            && IMAGE_UTIL_COLORSPACE_I420 != dest_colorspace)
    {
        ERR("dest_colorspace not supported yet");
        return IMAGE_UTIL_ERROR_NOT_SUPPORTED_FORMAT;
    }

    unsigned int dest_size = 0;
    error = image_util_calculate_buffer_size(width, height, dest_colorspace, &dest_size);
    RETVM_IF(IMAGE_UTIL_ERROR_NONE != error, error, "image_util_calculate_buffer_size error");
    unsigned int src_size = 0;
    error = image_util_calculate_buffer_size(width, height, src_colorspace, &src_size);
    RETVM_IF(IMAGE_UTIL_ERROR_NONE != error, error, "image_util_calculate_buffer_size error");

    if (dest_colorspace == src_colorspace)
    {
        memcpy(dest, src, src_size);
        return error;
    }


    if (IMAGE_UTIL_COLORSPACE_BGRA8888 == src_colorspace)
    {
        if (IMAGE_UTIL_COLORSPACE_I420 == dest_colorspace)
        {
            _convert_bgra8888_to_yuv420(dest, src, width, height, dest_size, src_size);
        }
        else
        {
            _convert_bgra8888_to_rgb565(dest, src, width, height, dest_size, src_size);
        }
    }
    else if (IMAGE_UTIL_COLORSPACE_RGB565 == src_colorspace)
    {
        if (IMAGE_UTIL_COLORSPACE_I420 == dest_colorspace)
        {
            _convert_rgb565_to_yuv420(dest, src, width, height, dest_size, src_size);
        }
        else
        {
            _convert_rgb565_to_bgra8888(dest, src, width, height, dest_size, src_size);
        }
    }
    else if (IMAGE_UTIL_COLORSPACE_I420 == src_colorspace)
    {
        if (IMAGE_UTIL_COLORSPACE_RGB565 == dest_colorspace)
        {
            _convert_yuv420_to_rgb565(dest, src, width, height, dest_size, src_size);
        }
        else
        {
            _convert_yuv420_to_bgra8888(dest, src, width, height, dest_size, src_size);
        }
    }
    else if (IMAGE_UTIL_COLORSPACE_UYVY == src_colorspace)
    {
        if (IMAGE_UTIL_COLORSPACE_BGRA8888 == dest_colorspace)
        {
            _convert_yuv422_to_bgra8888(dest, src, width, height, dest_size, src_size);
        }
        else
        {
            ERR("dest_colorspace not supported yet");
            return IMAGE_UTIL_ERROR_NOT_SUPPORTED_FORMAT;
        }
    }
    else
    {
        if (IMAGE_UTIL_COLORSPACE_BGRA8888 == dest_colorspace)
        {
            _convert_rgb888_to_bgra8888(dest, src, width, height, dest_size, src_size);
        }
        else
        {
            ERR("dest_colorspace not supported yet");
            return IMAGE_UTIL_ERROR_NOT_SUPPORTED_FORMAT;
        }
    }

    return error;
}


static void _convert_bgra8888_to_rgb565( uchar *dest, const uchar *src,
                                         const int width, const int height,
                                         const int d_size, const int s_size)
{
    unsigned int src_stride = (s_size / height);
    unsigned int dest_stride = (d_size / height);

    unsigned int y;
    for (y = 0; y < height; ++y)
    {
        const bgra32* src_pixel = (const bgra32*) &(src[y * src_stride]);
        rgb16* dest_pixel = (rgb16*) &(dest[y * dest_stride]);
        unsigned int x;
        for (x = 0; x < width; ++x)
        {
            dest_pixel[x] = bgra_to_rgb( src_pixel[x]);
        }
    }
}

/**
 * @brief RGBA <=> YUV420 format converion
 * @see https://en.wikipedia.org/wiki/YUV#Y.27UV420p_.28and_Y.27V12_or_YV12.29_to_RGB888_conversion
 */
static void _convert_bgra8888_to_yuv420( uchar *dest, const uchar *src,
                                         const int width, const int height,
                                         const int d_size, const int s_size)
{
    unsigned int src_stride = (s_size / height);
    unsigned int pix_count = width * height;
    unsigned int upos = pix_count;
    unsigned int vpos = upos + upos / 4;

    unsigned int y;
    for (y = 0; y < height; ++y )
    {
        const bgra32* src_pixel = (const bgra32*) &(src[y * src_stride]);
        unsigned int dest_line_pos = width * y;

        if( !(y % 2) )
        {
            unsigned int x;
            for (x = 0; x < width; x += 2 )
            {
                bgra32 pixel = src_pixel[x];

                dest[dest_line_pos + x] = bgra_to_yuv_y(pixel);
                dest[upos++] = bgra_to_yuv_u(pixel);
                dest[vpos++] = bgra_to_yuv_v(pixel);

                pixel = src_pixel[x + 1];
                dest[dest_line_pos + x + 1] = bgra_to_yuv_y(pixel);
            }
        }
        else
        {
            unsigned int x;
            for (x = 0; x < width; ++x)
            {
                dest[dest_line_pos + x] = bgra_to_yuv_y( src_pixel[x] );
            }
        }
    }
}

static void _convert_rgb565_to_bgra8888( uchar *dest, const uchar *src,
                                         const int width, const int height,
                                         const int d_size, const int s_size)
{
    unsigned int src_stride = (s_size / height);
    unsigned int dest_stride = (d_size / height);

    unsigned int y;
    for (y = 0; y < height; ++y)
    {
        const rgb16* src_pixel = (const rgb16*) &(src[y * src_stride]);
        bgra32* dest_pixel = (bgra32*) &(dest[y * dest_stride]);
        unsigned int x;
        for (x = 0; x < width; ++x)
        {
            dest_pixel[x] = rgb_to_bgra( src_pixel[x]);
        }
    }
}

/**
 * @brief RGBA <=> YUV420 format converion
 * @see https://en.wikipedia.org/wiki/YUV#Y.27UV420p_.28and_Y.27V12_or_YV12.29_to_RGB888_conversion
 */
static void _convert_rgb565_to_yuv420( uchar *dest, const uchar *src,
                                         const int width, const int height,
                                         const int d_size, const int s_size)
{
    unsigned int src_stride = (s_size / height);
    unsigned int pix_count = width * height;
    unsigned int upos = pix_count;
    unsigned int vpos = upos + upos / 4;

    unsigned int y;
    for (y = 0; y < height; ++y )
    {
        const rgb16* src_pixel = (const rgb16*) &(src[y * src_stride]);
        unsigned int dest_line_pos = width * y;

        if ( !(y % 2) )
        {
            unsigned int x;
            for (x = 0; x < width; x += 2 )
            {
                bgra32 pixel = rgb_to_bgra(src_pixel[x]);

                dest[dest_line_pos + x] = bgra_to_yuv_y(pixel);
                dest[upos++] = bgra_to_yuv_u(pixel);
                dest[vpos++] = bgra_to_yuv_v(pixel);

                pixel = rgb_to_bgra(src_pixel[x + 1]);
                dest[dest_line_pos + x + 1] = bgra_to_yuv_y(pixel);
            }
        }
        else
        {
            unsigned int x;
            for (x = 0; x < width; ++x)
            {
                bgra32 pixel = rgb_to_bgra(src_pixel[x]);
                dest[dest_line_pos + x] = bgra_to_yuv_y(pixel);
            }
        }
    }
}

/**
 * @brief RGBA <=> YUV420 format converion
 * @see https://en.wikipedia.org/wiki/YUV#Y.27UV420p_.28and_Y.27V12_or_YV12.29_to_RGB888_conversion
 */
static void _convert_yuv420_to_rgb565( uchar *dest, const uchar *src,
                                         const int width, const int height,
                                         const int d_size, const int s_size)
{
    unsigned int pix_count = width * height;
    unsigned int dest_stride = (d_size / height);

    unsigned int y;
    for (y = 0; y < height; y++)
    {
        rgb16* dest_pixel = (rgb16*) &(dest[y*dest_stride]);
        unsigned int x;
        for (x = 0; x < width; x++)
        {
            uchar yuv_y = src[ y*width + x];
            uchar yuv_u = src[ (int)(pix_count + (y/2)*(width/2)  + x/2)];
            uchar yuv_v = src[ (int)(pix_count*1.25 + (y/2)*(width/2) + x/2)];

            dest_pixel[x] =
                    ( ( yuv_to_r(yuv_y, yuv_u, yuv_v) >> (RGB32_CHANEL_BIT_SIZE - RGB16_R_SIZE)) << RGB16_R_SHIFT)
                    | ((yuv_to_g(yuv_y, yuv_u, yuv_v) >> (RGB32_CHANEL_BIT_SIZE - RGB16_G_SIZE)) << RGB16_G_SHIFT)
                    | ((yuv_to_b(yuv_y, yuv_u, yuv_v) >> (RGB32_CHANEL_BIT_SIZE - RGB16_B_SIZE)) << RGB16_B_SHIFT);
        }
    }
}

/**
 * @brief RGBA <=> YUV420 format converion
 * @see https://en.wikipedia.org/wiki/YUV#Y.27UV420p_.28and_Y.27V12_or_YV12.29_to_RGB888_conversion
 */
static void _convert_yuv420_to_bgra8888( uchar *dest, const uchar *src,
                                         const int width, const int height,
                                         const int d_size, const int s_size)
{
    unsigned int pix_count = width * height;
    unsigned int dest_stride = (d_size / height);

    unsigned int y;
    for (y = 0; y < height; y++)
    {
        bgra32* dest_pixel = (bgra32*) &(dest[dest_stride * y]);
        unsigned int x;
        for (x = 0; x < width; x++)
        {
            uchar yuv_y = src[ y*width + x];
            uchar yuv_u = src[ (int)(pix_count + (y/2)*(width/2)  + x/2)];
            uchar yuv_v = src[ (int)(pix_count*1.25 + (y/2)*(width/2) + x/2)];

            dest_pixel[x].r = yuv_to_r(yuv_y, yuv_u, yuv_v);
            dest_pixel[x].g = yuv_to_g(yuv_y, yuv_u, yuv_v);
            dest_pixel[x].b = yuv_to_b(yuv_y, yuv_u, yuv_v);
            dest_pixel[x].a = RGB32_DEFAULT_ALPHA;
        }
    }
}

/**
 * @brief RGBA <=> YUV422 format converion
 * @see https://en.wikipedia.org/wiki/YUV#Y.27UV422_to_RGB888_conversion
 */
static void _convert_yuv422_to_bgra8888( uchar *dest, const uchar *src,
                                         const int width, const int height,
                                         const int d_size, const int s_size)
{
    unsigned int dest_stride = (d_size / height);
    uchar yuv_u, yuv_v, yuv_y1, yuv_y2;
    yuv_u = yuv_v = yuv_y1 = yuv_y2 = 0;
    unsigned int y;
    for (y = 0; y < height; y++)
    {
        bgra32* dest_pixel = (bgra32*) &(dest[dest_stride * y]);
        const uchar * src_line = &(src[2 * y * width]);
        unsigned int x;
        for (x = 0; x < width; x += 2)
        {
            yuv_y1 = src_line[2 * x + 1];
            yuv_y2 = src_line[2 * x + 3];
            yuv_u = src_line[2 * x];
            yuv_v = src_line[2 * x + 2];

            dest_pixel[x].r = yuv_to_r(yuv_y1, yuv_u, yuv_v);
            dest_pixel[x].g = yuv_to_g(yuv_y1, yuv_u, yuv_v);
            dest_pixel[x].b = yuv_to_b(yuv_y1, yuv_u, yuv_v);
            dest_pixel[x].a = RGB32_DEFAULT_ALPHA;

            dest_pixel[x + 1].r = yuv_to_r(yuv_y2, yuv_u, yuv_v);
            dest_pixel[x + 1].g = yuv_to_g(yuv_y2, yuv_u, yuv_v);
            dest_pixel[x + 1].b = yuv_to_b(yuv_y2, yuv_u, yuv_v);
            dest_pixel[x + 1].a = RGB32_DEFAULT_ALPHA;
        }
    }
}

static void _convert_rgb888_to_bgra8888( uchar *dest, const uchar *src,
                                         const int width, const int height,
                                         const int d_size, const int s_size)
{
    unsigned int src_stride = (s_size / height);
    unsigned int dest_stride = (d_size / height);

    unsigned int y;
    for (y = 0; y < height; ++y)
    {
        const uchar* src_pixel = &(src[y * src_stride]);
        bgra32* dest_pixel = (bgra32*) &(dest[y * dest_stride]);
        unsigned int x;
        for (x = 0; x < width; ++x)
        {
            dest_pixel[x].r = src_pixel[RGB24_BPP * x];
            dest_pixel[x].g = src_pixel[RGB24_BPP * x + 1];
            dest_pixel[x].b = src_pixel[RGB24_BPP * x + 2];
            dest_pixel[x].a = RGB32_DEFAULT_ALPHA;
        }
    }
}

static inline rgb16 bgra_to_rgb(const bgra32 src)
{
    rgb16 result = (rgb16)
            ( ((src.r >> (RGB32_CHANEL_BIT_SIZE - RGB16_R_SIZE)) << RGB16_R_SHIFT)
            | ((src.g >> (RGB32_CHANEL_BIT_SIZE - RGB16_G_SIZE)) << RGB16_G_SHIFT)
            | ((src.b >> (RGB32_CHANEL_BIT_SIZE - RGB16_B_SIZE)) << RGB16_B_SHIFT));
    return result;
}

static inline bgra32 rgb_to_bgra(const rgb16 src)
{
    bgra32 result = {};
    result.r = (uchar)( src >> RGB16_R_SHIFT) << (RGB32_CHANEL_BIT_SIZE - RGB16_R_SIZE);
    result.g = (uchar)( src >> RGB16_G_SHIFT) << (RGB32_CHANEL_BIT_SIZE - RGB16_G_SIZE);
    result.b = (uchar)( src >> RGB16_B_SHIFT) << (RGB32_CHANEL_BIT_SIZE - RGB16_B_SIZE);
    result.a = RGB32_DEFAULT_ALPHA;
    return result;
}

static uchar clamp(int x)
{
    if (x > 255)
    {
        x = 255;
    }
    else if (x < 0)
    {
        x = 0;
    }
    return x;
}

/**
 * @brief RGBA <=> YUV colorspace format converions
 * @see https://en.wikipedia.org/wiki/YUV#Y.27UV444_to_RGB888_conversion
 */
static inline uchar bgra_to_yuv_y(const bgra32 src)
{
    return ((66*src.r + 129*src.g + 25*src.b) >> 8) + 16;
}

static inline uchar bgra_to_yuv_u(const bgra32 src)
{
    return ((-38*src.r + -74*src.g + 112*src.b) >> 8) + 128;
}

static inline uchar bgra_to_yuv_v(const bgra32 src)
{
    return ((112*src.r + -94*src.g + -18*src.b) >> 8) + 128;
}

static inline uchar yuv_to_r(const uchar yuv_y, const uchar yuv_u, const uchar yuv_v)
{
    return clamp(yuv_y + 1.402 * (yuv_v - 128));
}

static inline uchar yuv_to_g(const uchar yuv_y, const uchar yuv_u, const uchar yuv_v)
{
    return clamp(yuv_y - 0.344 * (yuv_u - 128) - 0.714 * (yuv_v - 128));
}

static inline uchar yuv_to_b(const uchar yuv_y, const uchar yuv_u, const uchar yuv_v)
{
    return clamp(yuv_y + 1.772 * (yuv_u - 128));
}

bool image_sample_util_load_image(Evas_Object *image, const char *file_name,
                                  image_util_colorspace_e colorspace, const int width, const int height)
{
    RETVM_IF(NULL == image, false, "image is NULL");
    bool result = true;
    bool is_yuv_format = ( IMAGE_UTIL_COLORSPACE_I420 == colorspace ||
         IMAGE_UTIL_COLORSPACE_UYVY == colorspace );

    if (!is_yuv_format)
    {
        result = ic_load_image(image, file_name);
        if (!result)
        {
            ERR("error with ic_load_image, image path %s", file_name);
        }
        return result;
    }

    long long file_length = 0;
    char fullpath[PATH_MAX] = {'\0'};
    sprintf(fullpath, "%s%s", get_resource_path("images/"), file_name);

    unsigned char *buff_src = NULL;
    RETVM_IF(!_read_file(fullpath, &buff_src, &file_length), false, "error on read file into buffer");

    int error = IMAGE_UTIL_ERROR_NONE;

    unsigned int size_src = 0;
    error = image_util_calculate_buffer_size(width, height, colorspace, &size_src);
    if (IMAGE_UTIL_ERROR_NONE != error)
    {
        free(buff_src);
        return false;
}
    if (size_src != file_length)
    {
        ERR("Required raw buffer file size and real file size mismatch %d, %d", size_src, file_length);
        free(buff_src);
        return false;
    }

    unsigned int  size_dest = 0;
    image_util_colorspace_e colorspace_dest = IMAGE_UTIL_COLORSPACE_BGRA8888;
    error = image_util_calculate_buffer_size(width, height, colorspace_dest, &size_dest);
    if (IMAGE_UTIL_ERROR_NONE != error)
    {
        free(buff_src);
        return false;
    }
    unsigned char *buff_dest = (unsigned char*) malloc(size_dest);
    if (!buff_dest)
    {
        ERR("Memory error!");
        free(buff_src);
        return false;
    }
    error = image_sample_util_convert_colorspace(buff_dest, colorspace_dest, buff_src, width, height, colorspace);
    free(buff_src);
    Evas_Object *evas_image = elm_image_object_get(image);
    if (IMAGE_UTIL_ERROR_NONE == error && evas_image)
    {
        evas_object_image_alpha_set(evas_image, EINA_FALSE);
        evas_object_image_size_set(evas_image, width, height);
        evas_object_image_colorspace_set(evas_image, EVAS_COLORSPACE_ARGB8888);
        evas_object_image_data_set(evas_image, buff_dest);
        elm_image_fill_outside_set(image, EINA_FALSE);
    }
    else
    {
        free(buff_dest);
        result = false;
        ERR("image_sample_util_convert_colorspace error");
    }

    return result;
}

static bool _read_file(const char *fullpath, unsigned char **buff, long long *file_length)
{
    FILE *file = NULL;
    file = fopen(fullpath, "rb");
    if (!file)
    {
        ERR("Unable to open file %s", fullpath);
        return false;
    }

    fseek(file, 0, SEEK_END);
    *file_length = ftell(file);
    fseek(file, 0, SEEK_SET);

    unsigned char *buff_src = (unsigned char*) malloc(*file_length);
    if (!buff_src)
    {
        ERR("Memory error!");
        fclose(file);
        return false;
    }

    size_t size_read = fread(buff_src, 1, *file_length, file);
    fclose(file);
    if (size_read != *file_length)
    {
        ERR("fread file error, mismatch file size and reading size");
        free(buff_src);
        return false;
    }

    *buff = buff_src;
    return true;
}

