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

#include <Elementary.h>


// *_X_POS, *_Y_POS, *_WIDTH, *_HEIGHT are virtual (relative) coords of grid widget
#define MAX_WIDTH 100
#define MAX_HEIGHT 100
#define IMAGE_X_POS 0
#define IMAGE_Y_POS 0
#define IMAGE_WIDTH MAX_WIDTH
#define IMAGE_HEIGHT 45
#define SLIDER_X_POS 0
#define SLIDER_Y_POS (IMAGE_Y_POS + IMAGE_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 200.0
#define DEFAULT_SCALE 100.0

#define DEFAULT_PARAMETER 0

#define LABEL_TEXT_TEMPLATE "%s%s<br/>PixelFormat: %s<br/>(%u x %u) => (%u x %u)<br/>"\
    "Time taken: %llums<br/>Result:E_SUCCESS"

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

struct _image_resize_view
{
    Elm_Object_Item *frame_item;
    Evas_Object *navi;
    Evas_Object *layout;
    Evas_Object *grid;
    Evas_Object *image_scroller;
    Evas_Object *image;
    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;
    Evas_Coord_Size image_original_size;
    Evas_Coord_Size image_scaled_size;
    long long ticks;
};

typedef struct _image_config
{
    const char *name;
    unsigned int width;
    unsigned int height;
    image_util_colorspace_e colorspace;
} image_config;

static const int toolbar_item_ids[TOOLBAR_ITEM_INDEX_COUNT_MAX] = {TOOLBAR_ITEM_INDEX_PREV, TOOLBAR_ITEM_INDEX_NEXT};
static const char *toolbar_item_names[TOOLBAR_ITEM_INDEX_COUNT_MAX] = { "Prev", "Next" };

static image_config image_configs[] =
{
    {"dog_1024_960.gif", DEFAULT_PARAMETER, DEFAULT_PARAMETER, DEFAULT_PARAMETER},
    {"giraffe_800_1179.jpg", DEFAULT_PARAMETER, DEFAULT_PARAMETER, DEFAULT_PARAMETER},
    {"YUV/foreman_cif.yuv", 352, 288, IMAGE_UTIL_COLORSPACE_I420}
};
static const double image_slider_pos[] = { 60.0, 10.0, 50.0};
#define IMAGES_COUNT (sizeof(image_configs) / sizeof(*image_configs))

static const char *title_view = "Image resize";

static void _push(image_resize_view *this);
static void _toolbar_button_clicked_cb(void *data, Evas_Object *obj, void *event_info);
static bool _create_layout_elements(image_resize_view *this);

static Evas_Object *_create_toolbar(image_resize_view *this);

static void _change_image(image_resize_view *view, bool to_next);
static bool _load_image(Evas_Object *image, const int image_id);
static void _update_view_controls(image_resize_view *view);
static void _update_title(image_resize_view *view);
static void _update_label(image_resize_view *view);
static void _update_scale(image_resize_view *view);
static void _update_toolbar_buttons(image_resize_view *view);
static void _slider_delay_changed_cb(void *data, Evas_Object *obj, void *event_info);
static void _image_scroller_resize_cb(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void _on_image_changed(image_resize_view *view);
static const char *_colorspace_to_str(image_util_colorspace_e colorspace);

image_resize_view *image_resize_view_add(app_data *app, Evas_Object *navi)
{
    image_resize_view *this = calloc(1, sizeof(image_resize_view));

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

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

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

        _push(this);
    }

    return this;
}

static void _push(image_resize_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_resize_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_resize_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 *slider = ic_create_slider(view->grid, SLIDER_MIN, SLIDER_MAX);
    RETVM_IF(NULL == slider, false, "create of layout elements failed");
    view->slider = slider;
    elm_slider_value_set(slider, image_slider_pos[0]);
    evas_object_smart_callback_add(slider, "delay,changed", _slider_delay_changed_cb, view);
    elm_grid_pack(grid, slider, SLIDER_X_POS, SLIDER_Y_POS, SLIDER_WIDTH, SLIDER_HEIGHT);

    Evas_Object *image_scroller = ic_create_scroller(grid);
    RETVM_IF(NULL == image_scroller, false, "create of layout elements failed");
    view->image_scroller = image_scroller;
    evas_object_event_callback_add(image_scroller, EVAS_CALLBACK_RESIZE, _image_scroller_resize_cb, view);
    elm_grid_pack(grid, image_scroller, IMAGE_X_POS, IMAGE_Y_POS, IMAGE_WIDTH, IMAGE_HEIGHT);

    Evas_Object *image = ic_create_image(image_scroller, image_configs[0].name);
    RETVM_IF(NULL == image, false, "create of layout elements failed");
    view->image = image;
    elm_object_content_set(image_scroller, image);

    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_resize_view *view = (image_resize_view*)data;

    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_PREV:
            _change_image(view, false);
            break;
        case TOOLBAR_ITEM_INDEX_NEXT:
            _change_image(view, true);
            break;
        default:
            ERR("Not supported button");
            break;
    }
}

static void _change_image(image_resize_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;
    }

    if (!_load_image(view->image, image_id))
    {
        ERR("_load_image failed");
    }
    view->image_id = image_id;
    _on_image_changed(view);
    _update_view_controls(view);
}

static bool _load_image(Evas_Object *image, const int index)
{
    return image_sample_util_load_image(image, image_configs[index].name, image_configs[index].colorspace,
                                 image_configs[index].width, image_configs[index].height);
}

static void _update_view_controls(image_resize_view *view)
{
    RETM_IF(NULL == view, "view is NULL");
    _update_title(view);
    _update_toolbar_buttons(view);
    _update_scale(view);
    _update_label(view);
}

static void _update_title(image_resize_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_resize_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);
}

static void _update_label(image_resize_view *view)
{
    RETM_IF(NULL == view, "view is NULL");
    const char *image_name = image_configs[view->image_id].name;
    const char *pixel_format = _colorspace_to_str(image_configs[view->image_id].colorspace);
    long long time_taken = view->ticks;

    char buf[LABEL_MAX_LEN] = {'\0'};
    snprintf(buf, LABEL_MAX_LEN, LABEL_TEXT_TEMPLATE, LABEL_REL_PATH, image_name, pixel_format,
            view->image_original_size.w, view->image_original_size.h,
            view->image_scaled_size.w, view->image_scaled_size.h, time_taken);
    elm_object_text_set(view->label, buf);
}

static void _update_scale(image_resize_view *view)
{
    RETM_IF(NULL == view, "view is NULL");
    long long time_begin = get_ticks();
    Evas_Object *evas_image = elm_image_object_get(view->image);
    double scale = elm_slider_value_get(view->slider) / DEFAULT_SCALE;
    Evas_Coord_Rectangle new_geometry = {};
    bool result = ic_calc_image_new_geometry(evas_image, view->image_scroller, scale, &new_geometry);
    RETM_IF(!result, "error: on image new geometry calculation");

    evas_object_image_fill_set(evas_image, 0, 0, new_geometry.w, new_geometry.h);
    evas_object_resize(evas_image, new_geometry.w, new_geometry.h);
    evas_object_move(evas_image, new_geometry.x, new_geometry.y);
    view->image_scaled_size.w = new_geometry.w;
    view->image_scaled_size.h = new_geometry.h;
    view->ticks = get_ticks() - time_begin;
}

static void _slider_delay_changed_cb(void *data, Evas_Object *obj, void *event_info)
{
    RETM_IF(NULL == data, "data is NULL");
    image_resize_view *view = data;
    _update_scale(view);
    _update_label(view);
}

static void _image_scroller_resize_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
    RETM_IF(NULL == data, "data is NULL");
    image_resize_view *view = data;
    _on_image_changed(view);
    _update_scale(view);
    _update_label(view);
}

static void _on_image_changed(image_resize_view *view)
{
    RETM_IF(NULL == view, "view is NULL");
    elm_slider_value_set(view->slider, image_slider_pos[view->image_id]);
    Evas_Object * evas_image = elm_image_object_get(view->image);
    int w = 0, h = 0;
    evas_object_image_size_get(evas_image, &w, &h);
    view->image_original_size.w = w;
    view->image_original_size.h = h;
}

static const char *_colorspace_to_str(image_util_colorspace_e colorspace)
{
    const char *result = NULL;
    switch (colorspace)
    {
        case IMAGE_UTIL_COLORSPACE_UYVY:
            result = "YUV422";
            break;
        case IMAGE_UTIL_COLORSPACE_I420:
            result = "YUV420";
            break;
        default:
            result = "RGB32";
            break;
    }
    return result;
}
