/*
 * 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 "config.h"
#include "../../common/inc/ui-utils.h"
#include "../../common/inc/main-app.h"
#include "../inc/image-converter-view.h"
#include "../inc/image-sample-utils.h"
#include "../inc/image-common.h"
#include <Elementary.h>
#include <image_util.h>

// *_X_POS, *_Y_POS, *_WIDTH, *_HEIGHT are virtual (relative) coords of grid widget
#define MAX_WIDTH 100
#define MAX_HEIGHT 100
#define IMAGES_X_POS 0
#define IMAGES_Y_POS 0
#define IMAGES_WIDTH MAX_WIDTH
#define IMAGES_HEIGHT 30
#define IMAGE_SOURCE_X_POS IMAGES_X_POS
#define IMAGE_SOURCE_Y_POS IMAGES_Y_POS
#define IMAGE_SOURCE_WIDTH (IMAGES_WIDTH/2)
#define IMAGE_SOURCE_HEIGHT IMAGES_HEIGHT
#define IMAGE_CONVERTER_X_POS (IMAGES_WIDTH/2)
#define IMAGE_CONVERTER_Y_POS IMAGES_Y_POS
#define IMAGE_CONVERTER_WIDTH (IMAGES_WIDTH/2)
#define IMAGE_CONVERTER_HEIGHT IMAGES_HEIGHT
#define SLIDER_X_POS 0
#define SLIDER_Y_POS (IMAGES_Y_POS + IMAGES_HEIGHT)
#define SLIDER_WIDTH MAX_WIDTH
#define SLIDER_HEIGHT 15
#define LABEL_X_POS 0
#define LABEL_Y_POS (SLIDER_Y_POS + SLIDER_HEIGHT)
#define LABEL_WIDTH MAX_WIDTH
#define LABEL_HEIGHT (MAX_HEIGHT - LABEL_Y_POS)

#define SLIDER_MIN 1.0
#define SLIDER_MAX 100.0

#define IMAGE_FILE_FORMAT_EXTENSION 5
#define IMAGE_MIN_CONVERTED_SIZE 16
#define IMAGE_OUTPUT_DIR "../data/"
#define IMAGE_INITIAL_ID 0

#define LABEL_TEXT_TEMPLATE "%s%s<br/>Format: %s => %s<br/>Dim = %u x %u => %u x %u<br/>File size: %u => %u<br/>Time to convert: %llu ms<br/>Result:E_SUCCESS"


enum
{
    TOOLBAR_ITEM_INDEX_PIXFMT = 0,
    TOOLBAR_ITEM_INDEX_PREV,
    TOOLBAR_ITEM_INDEX_NEXT,
    TOOLBAR_ITEM_INDEX_COUNT_MAX
} TOOLBAR_ITEM_INDEXS;

struct _image_converter_view
{
    Elm_Object_Item *frame_item;
    Evas_Object *navi;
    Evas_Object *layout;
    Evas_Object *grid;
    Evas_Object *image_source;
    Evas_Object *image_converted;
    char image_converted_uri[PATH_MAX];
    Evas_Object *slider;
    Evas_Object *label;
    Evas_Object *toolbar;
    app_data *app;
    Elm_Object_Item* toolbar_items[TOOLBAR_ITEM_INDEX_COUNT_MAX];
    unsigned int image_id;
    double scale_perc;
    bool convert_format_ind;
    bool is_processed;
    long long ticks;
};

typedef struct _image_config
{
    char name[NAME_MAX];
    unsigned char dimension;
    char format[IMAGE_FILE_FORMAT_EXTENSION];
} image_config;

static const int toolbar_item_ids[TOOLBAR_ITEM_INDEX_COUNT_MAX] = {TOOLBAR_ITEM_INDEX_PIXFMT, TOOLBAR_ITEM_INDEX_PREV, TOOLBAR_ITEM_INDEX_NEXT};
static const char *toolbar_item_names[TOOLBAR_ITEM_INDEX_COUNT_MAX] = { "PNG", "Prev", "Next" };
static const char *title_view = "Image converter";
static image_config image_configs[] = {
        {"zebra_1024_680.bmp", 70, "jpg"},
        {"dog_1024_960.gif", 50, "png"},
        {"pebble_689_517.jpg", 100, "png"},
        {"home_640_432.png", 30, "jpg"}
};

#define IMAGES_COUNT (sizeof(image_configs) / sizeof(image_configs[0]))

static const int image_formats[] = {IMAGE_FILE_FORMAT_JPG, IMAGE_FILE_FORMAT_PNG};

static void _push(image_converter_view *this);

static void _toolbar_button_clicked_cb(void *data, Evas_Object *obj, void *event_info);
static void _slider_delay_changed_cb(void *data, Evas_Object *obj, void *event_info);

static bool _create_layout_elements(image_converter_view *view);

static Evas_Object *_create_toolbar(image_converter_view *this);

static void _change_image(image_converter_view *view, bool to_next);
static void _update_view_controls(image_converter_view *view);
static void _update_label(image_converter_view *view);
static void _change_format(image_converter_view *view);
static bool _process(image_converter_view *view, double scale);
static void _load_config(image_converter_view *view, int id);
static void _update_title(image_converter_view *view);
static void _update_toolbar_buttons(image_converter_view *view);

image_converter_view *image_converter_view_add(app_data *app, Evas_Object *navi)
{
    image_converter_view *view = calloc(1, sizeof(image_converter_view));

    if(view)
    {
        view->navi = navi;
        view->app = app;
        view->layout = ui_utils_layout_add(navi, ic_main_layout_del_cb, view);

        _load_config(view, IMAGE_INITIAL_ID);
        view->image_converted_uri[0] = '\0';
        view->is_processed = false;
        view->ticks = 0;

        if (NULL == view->layout)
        {
            free(view);
            return NULL;
        }

        if (!_create_layout_elements(view))
        {
            evas_object_del(view->layout);
            return NULL;
        }

        _process(view, view->scale_perc / SLIDER_MAX);
        _push(view);
    }

    return view;
}


static void _push(image_converter_view *this)
{
    Elm_Object_Item *item = elm_naviframe_item_push(this->navi, NULL, NULL, NULL, this->layout, NULL);
    elm_object_item_part_content_set(elm_naviframe_top_item_get(this->navi), "toolbar", this->toolbar);
    this->frame_item = item;
    _update_view_controls(this);
}

static Evas_Object *_create_toolbar(image_converter_view *this)
{
    RETVM_IF(NULL == this, NULL, "create of toolbar failed");

    Evas_Object *obj = elm_toolbar_add(this->navi);
    RETVM_IF(NULL == obj, NULL, "create of toolbar failed");

    elm_object_style_set(obj, "default");
    elm_toolbar_shrink_mode_set(obj, ELM_TOOLBAR_SHRINK_EXPAND);
    elm_toolbar_transverse_expanded_set(obj, EINA_TRUE);
    elm_toolbar_select_mode_set(obj, ELM_OBJECT_SELECT_MODE_NONE);
    evas_object_size_hint_weight_set(obj, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);

    unsigned int i;
    for (i = 0; i < TOOLBAR_ITEM_INDEX_COUNT_MAX; ++i)
    {
        this->toolbar_items[i] = elm_toolbar_item_append(obj, NULL, toolbar_item_names[i], _toolbar_button_clicked_cb, this);
        evas_object_data_set(elm_toolbar_item_object_get(this->toolbar_items[i]), TOOLBAR_BUTTON_DATA_KEY, (void*)(&toolbar_item_ids[i]));
    }

    return obj;
}

static bool _create_layout_elements(image_converter_view *view)
{
    RETVM_IF(NULL == view, false, "create of layout elements failed");

    Evas_Object *grid = ic_create_grid(view->layout, MAX_WIDTH, MAX_HEIGHT);
    RETVM_IF(NULL == grid, false, "create of layout elements failed");
    view->grid = grid;

    Evas_Object *image_source = ic_create_image(grid, image_configs[0].name);
    RETVM_IF(NULL == image_source, false, "create of layout elements failed");
    view->image_source = image_source;
    elm_grid_pack(grid, image_source, IMAGE_SOURCE_X_POS, IMAGE_SOURCE_Y_POS, IMAGE_SOURCE_WIDTH, IMAGE_SOURCE_HEIGHT);

    Evas_Object *image_converted = ic_create_image(grid, image_configs[0].name);
    RETVM_IF(NULL == image_converted, false, "create of layout elements failed");
    view->image_converted = image_converted;
    elm_grid_pack(grid, image_converted, IMAGE_CONVERTER_X_POS, IMAGE_CONVERTER_Y_POS, IMAGE_CONVERTER_WIDTH, IMAGE_CONVERTER_HEIGHT);

    Evas_Object *slider = ic_create_slider(view->grid, SLIDER_MIN, SLIDER_MAX);
    RETVM_IF(NULL == slider, false, "create of layout elements failed");
    elm_slider_value_set(slider, view->scale_perc);
    evas_object_smart_callback_add(slider, "delay,changed", _slider_delay_changed_cb, view);
    view->slider = slider;
    elm_grid_pack(grid, slider, SLIDER_X_POS, SLIDER_Y_POS, SLIDER_WIDTH, SLIDER_HEIGHT);

    Evas_Object *label = ic_create_label(grid);
    RETVM_IF(NULL == label, false, "create of layout elements failed");
    view->label = label;
    elm_grid_pack(grid, label, LABEL_X_POS, LABEL_Y_POS, LABEL_WIDTH, LABEL_HEIGHT);

    Evas_Object *toolbar = _create_toolbar(view);
    RETVM_IF(NULL == toolbar, false, "create of layout elements failed");
    view->toolbar = toolbar;

    return true;
}

static void _toolbar_button_clicked_cb(void *data, Evas_Object *obj, void *event_info)
{
    RETM_IF(NULL == data, "data is NULL");
    RETM_IF(NULL == event_info, "event info is NULL");
    image_converter_view *view = (image_converter_view*)data;
    RETM_IF(view->is_processed, "image is processing");

    void *toolbar_item_data = evas_object_data_get(elm_toolbar_item_object_get(event_info), TOOLBAR_BUTTON_DATA_KEY);
    RETM_IF(NULL == toolbar_item_data, "Toolbar item data is NULL");
    int i = *((int*)toolbar_item_data);
    switch (i)
    {
        case TOOLBAR_ITEM_INDEX_PIXFMT:
            _change_format(view);
            break;
        case TOOLBAR_ITEM_INDEX_PREV:
            _change_image(view, false);
            break;
        case TOOLBAR_ITEM_INDEX_NEXT:
            _change_image(view, true);
            break;
        default:
            ERR("Not supported button");
            break;
    }
}



static void _slider_delay_changed_cb(void *data, Evas_Object *obj, void *event_info)
{
    RETM_IF(NULL == data, "data is NULL");
    image_converter_view *view = data;
    RETM_IF(view->is_processed, "Image is already processing");
    double scale = elm_slider_value_get(obj);

    view->is_processed = true;
    if (_process(view, scale / SLIDER_MAX))
    {
        _update_view_controls(view);
    }
    view->is_processed = false;
}

static void _change_image(image_converter_view *view, bool to_next)
{
    RETM_IF(NULL == view, "view is NULL");
    unsigned int image_id = view->image_id;
    RETM_IF(to_next && image_id + 1 >= IMAGES_COUNT, "Out of image container");
    RETM_IF(!to_next && image_id == 0, "Out of image container");

    if (to_next)
    {
        ++image_id;
    }
    else
    {
        --image_id;
    }

    elm_image_no_scale_set(view->image_converted, EINA_FALSE);
    elm_image_resizable_set(view->image_converted, EINA_TRUE, EINA_TRUE);
    elm_image_fill_outside_set(view->image_converted, EINA_FALSE);
    if (!ic_load_image(view->image_source, image_configs[image_id].name)
            || !ic_load_image(view->image_converted, image_configs[image_id].name))
    {
        ERR("ic_load_image failed");
    }

    _load_config(view, image_id);
    _process(view, view->scale_perc / SLIDER_MAX);

    _update_view_controls(view);
}

static void _update_view_controls(image_converter_view *view)
{
    RETM_IF(NULL == view, "view is NULL");
    _update_title(view);
    _update_toolbar_buttons(view);

    int next_format_ind = !view->convert_format_ind;
    elm_object_item_text_set(view->toolbar_items[TOOLBAR_ITEM_INDEX_PIXFMT],
               image_file_format_to_str(image_formats[next_format_ind]));
    elm_slider_value_set(view->slider, view->scale_perc);

    if (view->image_converted_uri)
    {
        if (EINA_FALSE == elm_image_file_set(view->image_converted, view->image_converted_uri, NULL))
        {
            ERR("ERROR with file_set");
        }
        else
        {
            Evas_Object *img_conv_eo = elm_image_object_get(view->image_converted);

            int container_w = 0, container_h = 0;
            evas_object_geometry_get(view->image_converted, NULL, NULL,
                    &container_w, &container_h);


            int w = 0, h = 0;
            evas_object_image_size_get(img_conv_eo, &w, &h);

            if (container_w > w && container_h > h)
            {
                elm_image_no_scale_set(view->image_converted, EINA_TRUE);
                elm_image_resizable_set(view->image_converted, EINA_FALSE,
                        EINA_FALSE);
            }
            else
            {
                elm_image_no_scale_set(view->image_converted, EINA_FALSE);
                elm_image_resizable_set(view->image_converted, EINA_TRUE,
                        EINA_TRUE);
            }
        }
    }
    _update_label(view);
}

static void _change_format(image_converter_view *view)
{
    RETM_IF(NULL == view, "view is NULL");
    RETM_IF(view->is_processed, "image is processing");

    view->convert_format_ind = !view->convert_format_ind;

    _process(view, view->scale_perc / SLIDER_MAX);

    _update_view_controls(view);
}


static bool _process(image_converter_view *view, double scale)
{
    RETVM_IF(NULL == view, false, "view is NULL");
    RETVM_IF(NULL == view->image_source, false, "view is NULL");
    bool result = true;
    int w = 0, h = 0;
    Evas_Object* source_eo = elm_image_object_get(view->image_source);
    RETVM_IF(NULL == source_eo, false, "evas_object image is NULL");

    long long ticks_begin = get_ticks();
    evas_object_image_size_get(source_eo, &w, &h);
    RETVM_IF(0 == w * h, false, "wrong size of source image: %d x %d", w, h);
    int dest_width = scale * w;
    int dest_height = scale * h;

    view->scale_perc = scale * SLIDER_MAX;
    int error = IMAGE_UTIL_ERROR_NONE;
    unsigned int dest_size = 0;
    unsigned char* dest_buff = NULL;
    void* src_buff = evas_object_image_data_get(source_eo, EINA_FALSE);
    RETVM_IF(NULL == src_buff, false, "image_util_calculate_buffer_size error");

    error = image_util_calculate_buffer_size(dest_width , dest_height, IMAGE_UTIL_COLORSPACE_BGRA8888, &dest_size);
    RETVM_IF(IMAGE_UTIL_ERROR_NONE != error, false, "image_util_calculate_buffer_size error");

    dest_buff = malloc(dest_size);
    RETVM_IF(NULL == dest_buff, false, "malloc destination buffer error %d", dest_size);

    error = image_sample_util_resize(w, h, src_buff, dest_width , dest_height, dest_buff);
    if (IMAGE_UTIL_ERROR_NONE == error)
    {
        Evas_Object* im = evas_object_image_filled_add(evas_object_evas_get(view->layout));
        if (im)
        {
            evas_object_image_colorspace_set(im, EVAS_COLORSPACE_ARGB8888);
            evas_object_image_alpha_set(im, EINA_FALSE);
            evas_object_image_size_set(im, dest_width, dest_height);
            evas_object_image_data_copy_set(im, dest_buff);

            char output_buf[PATH_MAX] = {'\0'};
            snprintf(output_buf, PATH_MAX, "%s%s%s.%s", get_resource_path(""), IMAGE_OUTPUT_DIR,
                    image_configs[view->image_id].name,
                    image_file_format_to_str(image_formats[view->convert_format_ind]));
            if (EINA_FALSE
                    == evas_object_image_save(im, output_buf, NULL, NULL))
            {
                ERR("evas_object_image_save error");
                result = false;
            }
            else
            {
                strcpy(view->image_converted_uri, output_buf);
            }
            evas_object_del(im);
            view->ticks = get_ticks() - ticks_begin;
        }
    }
    else
    {
        ERR("Image buffer manipulation: image_sample_util_resize error");
        result = false;
    }
    free(dest_buff);
    dest_buff = NULL;

    return result;
}

static void _load_config(image_converter_view *view, int id)
{
    RETM_IF(NULL == view, "view is NULL");
    view->image_id = id;
    view->scale_perc = (double) image_configs[id].dimension;
    if (0 == strcmp(image_configs[id].format, "jpg"))
    {
        view->convert_format_ind = 0;
    }
    else
    {
        view->convert_format_ind = 1;
    }
}

static void _update_label(image_converter_view *view)
{
    RETM_IF(NULL == view, "this as NULL is passed to method");

    const char *filename = NULL;
    elm_image_file_get(view->image_source, &filename, NULL);

    Evas_Object* obj_img = elm_image_object_get(view->image_source);
    int src_w = 0, src_h = 0;
    evas_object_image_size_get(obj_img, &src_w, &src_h);
    Evas_Object* dest_bj_img = elm_image_object_get(view->image_converted);
    int dest_w = 0, dest_h = 0;
    evas_object_image_size_get(dest_bj_img, &dest_w, &dest_h);
    unsigned dest_filesize = 0;
    unsigned src_filesize = 0;

    struct stat file_stat;
    int res = 0;
    res = stat(filename, &file_stat);
    if (res != -1)
    {
        src_filesize = file_stat.st_size;
    }
    res = stat(view->image_converted_uri, &file_stat);
    if (res != -1)
    {
        dest_filesize = file_stat.st_size;
    }

    const char *image_name = image_configs[view->image_id].name;
    char buff[LABEL_MAX_LEN] = {'\0'};
    snprintf(buff, LABEL_MAX_LEN, LABEL_TEXT_TEMPLATE,
            LABEL_REL_PATH, image_name, filename_extenstion_get(filename),
            image_file_format_to_str(image_formats[view->convert_format_ind]),
            src_w, src_h, dest_w, dest_h,
            src_filesize, dest_filesize, view->ticks);
    elm_object_text_set(view->label, buff);
}

static void _update_title(image_converter_view *view)
{
    RETM_IF(NULL == view, "view is NULL");
    char buf[TITLE_MAX_LEN] = {'\0'};
    snprintf(buf, sizeof(buf), "%s (%d/%d)", title_view, view->image_id + 1, IMAGES_COUNT);
    elm_object_item_text_set(view->frame_item, buf);
}

static void _update_toolbar_buttons(image_converter_view *view)
{
    RETM_IF(NULL == view, "view is NULL");
    Eina_Bool prev_button_dis = view->image_id == 0;
    Eina_Bool next_button_dis = view->image_id + 1 >= IMAGES_COUNT;
    elm_object_item_disabled_set(view->toolbar_items[TOOLBAR_ITEM_INDEX_PREV], prev_button_dis);
    elm_object_item_disabled_set(view->toolbar_items[TOOLBAR_ITEM_INDEX_NEXT], next_button_dis);
}
