/*
 * 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-flip-rotate-view.h"
#include "../inc/image-common.h"
#include "../inc/image-sample-utils.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 RELATIVE_BORDER 2

#define FLIP_ROTATE_ELEMENTS_X_POS RELATIVE_BORDER
#define FLIP_ROTATE_ELEMENTS_Y_POS RELATIVE_BORDER
#define FLIP_ROTATE_ELEMENTS_WIDTH (MAX_WIDTH - 2 * RELATIVE_BORDER)
#define FLIP_ROTATE_ELEMENTS_HEIGHT 38
#define FLIP_ROTATE_ELEMENTS_BORDER 1

#define IMAGES_X_POS 0
#define IMAGES_Y_POS 0
#define IMAGES_WIDTH (MAX_WIDTH / 2 - FLIP_ROTATE_ELEMENTS_BORDER)
#define IMAGES_HEIGHT (75 - FLIP_ROTATE_ELEMENTS_BORDER)
#define IMAGE_FLIP_X_POS IMAGES_X_POS
#define IMAGE_FLIP_Y_POS IMAGES_Y_POS
#define IMAGE_ROTATE_X_POS (IMAGES_X_POS + IMAGES_WIDTH + 2 * FLIP_ROTATE_ELEMENTS_BORDER)
#define IMAGE_ROTATE_Y_POS IMAGES_Y_POS

#define BUTTONS_X_POS 0
#define BUTTONS_Y_POS (IMAGES_Y_POS + IMAGES_HEIGHT + 2 * FLIP_ROTATE_ELEMENTS_BORDER)
#define BUTTONS_WIDTH IMAGES_WIDTH
#define BUTTONS_HEIGHT (MAX_HEIGHT - BUTTONS_Y_POS)
#define BUTTON_FLIP_X_POS BUTTONS_X_POS
#define BUTTON_FLIP_Y_POS BUTTONS_Y_POS
#define BUTTON_ROTATE_X_POS (BUTTON_FLIP_X_POS + BUTTONS_WIDTH + 2 * FLIP_ROTATE_ELEMENTS_BORDER)
#define BUTTON_ROTATE_Y_POS BUTTONS_Y_POS

#define LABEL_Y_OFFSET 5
#define LABEL_X_POS 0
#define LABEL_Y_POS (FLIP_ROTATE_ELEMENTS_Y_POS + FLIP_ROTATE_ELEMENTS_HEIGHT + LABEL_Y_OFFSET)
#define LABEL_WIDTH MAX_WIDTH
#define LABEL_HEIGHT (MAX_HEIGHT - LABEL_Y_POS)

#define LABEL_TEXT_TEMPLATE "%s%s<br/>PixelFormat:%s<br/>%s<br/>%s<br/>Time taken: %llums<br/>Result:E_SUCCESS"

#define DEFAULT_PARAMETER 0
enum
{
    TOOLBAR_ITEM_INDEX_PREV = 0,
    TOOLBAR_ITEM_INDEX_NEXT,
    TOOLBAR_ITEM_INDEX_COUNT_MAX
} TOOLBAR_ITEM_INDEXS;

enum
{
    FLIP_STATE_NONE = 0,
    FLIP_STATE_HORIZONTAL,
    FLIP_STATE_VERTICAL,
    FLIP_STATES_MAX
} FLIP_STATES;

enum
{
    ROTATE_STATE_0 = 0,
    ROTATE_STATE_90,
    ROTATE_STATE_180,
    ROTATE_STATE_270,
    ROTATE_STATES_MAX
} ROTATE_STATES;


struct _image_flip_rotate_view
{
    Elm_Object_Item *frame_item;
    Evas_Object *navi;
    Evas_Object *layout;
    Evas_Object *grid;
    Evas_Object *image_flip;
    Evas_Object *image_rotate;
    Evas_Object *button_flip;
    Evas_Object *button_rotate;
    Evas_Object *label;
    Evas_Object *toolbar;
    app_data *app;
    Elm_Object_Item *toolbar_items[TOOLBAR_ITEM_INDEX_COUNT_MAX];
    unsigned int image_id;
    unsigned int flip_state;
    unsigned int rotate_state;
    long long ticks;
};

typedef struct _image_info
{
    char name[NAME_MAX];
    unsigned int width;
    unsigned int height;
    image_util_colorspace_e colorspace;
} image_info;

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 const char *title_view = "Flip & rotate";

static const char *flip_state_names [FLIP_STATES_MAX] = {"Flip: None", "Flip: Horizontal", "Flip: Vertical"};
static const char *rotate_state_names [ROTATE_STATES_MAX] = {"Rotate:0", "Rotate:90", "Rotate:180", "Rotate:270"};

static image_info image_infos[] =
{
    { "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 },
    { "YUV/dog_1024x960_uyvy.raw", 1024, 960, IMAGE_UTIL_COLORSPACE_UYVY }
};
#define IMAGES_COUNT (sizeof(image_infos) / sizeof(image_infos[0]))
static const int ind = 0;

static void _push(image_flip_rotate_view *this);

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

static bool _create_layout_elements(image_flip_rotate_view *this);

static Evas_Object *_create_button(Evas_Object *parent, Evas_Smart_Cb cb, void *data);
static Evas_Object *_create_toolbar(image_flip_rotate_view *this);

static void _change_image(image_flip_rotate_view *view, bool to_next);
static void _update_view_controls(image_flip_rotate_view *view);
static void _update_title(image_flip_rotate_view *view);
static void _update_label(image_flip_rotate_view *view);
static void _update_toolbar_buttons(image_flip_rotate_view *view);
static void _update_flip_state(image_flip_rotate_view *view);
static void _update_rotate_state(image_flip_rotate_view *view);
static void _on_image_changed(image_flip_rotate_view *view);
static int _convert_to_elm_flip_state(const int state);
static int _convert_to_elm_rotate_state(const int state);
static const char *_colorspace_to_str(image_util_colorspace_e colorspace);
static bool _load_image(Evas_Object *image, const int image_id);

image_flip_rotate_view *image_flip_rotate_view_add(app_data *app, Evas_Object *navi)
{
    image_flip_rotate_view *this = calloc(1, sizeof(image_flip_rotate_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_flip_rotate_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_flip_rotate_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_flip_rotate_view *this)
{
    RETVM_IF(NULL == this, false, "create of layout elements failed");

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

    Evas_Object *grid_flip_rotate_elements = ic_create_grid(grid, MAX_WIDTH, MAX_HEIGHT);
    RETVM_IF(NULL == grid_flip_rotate_elements, false, "create of layout elements failed");
    elm_grid_pack(grid, grid_flip_rotate_elements,
                  FLIP_ROTATE_ELEMENTS_X_POS, FLIP_ROTATE_ELEMENTS_Y_POS,
                  FLIP_ROTATE_ELEMENTS_WIDTH, FLIP_ROTATE_ELEMENTS_HEIGHT);

    Evas_Object *image_flip = ic_create_image(grid, image_infos[ind].name);
    RETVM_IF(NULL == image_flip, false, "create of layout elements failed");
    this->image_flip = image_flip;
    elm_grid_pack(grid_flip_rotate_elements, image_flip,
                  IMAGE_FLIP_X_POS, IMAGE_FLIP_Y_POS,
                  IMAGES_WIDTH, IMAGES_HEIGHT);
    Evas_Object *image_rotate = ic_create_image(grid, image_infos[ind].name);
    RETVM_IF(NULL == image_rotate, false, "create of layout elements failed");
    this->image_rotate = image_rotate;
    elm_grid_pack(grid_flip_rotate_elements, image_rotate,
                  IMAGE_ROTATE_X_POS, IMAGE_ROTATE_Y_POS,
                  IMAGES_WIDTH, IMAGES_HEIGHT);

    Evas_Object *button_flip = _create_button(this->grid, _on_button_flip_clicked_cb, this);
    RETVM_IF(NULL == button_flip, false, "create of layout elements failed");
    this->button_flip = button_flip;
    elm_grid_pack(grid_flip_rotate_elements, button_flip,
                  BUTTON_FLIP_X_POS, BUTTON_FLIP_Y_POS,
                  BUTTONS_WIDTH, BUTTONS_HEIGHT);
    Evas_Object *button_rotate = _create_button(this->grid, _on_button_rotate_clicked_cb, this);
    RETVM_IF(NULL == button_rotate, false, "create of layout elements failed");
    this->button_rotate = button_rotate;
    elm_grid_pack(grid_flip_rotate_elements, button_rotate,
                  BUTTON_ROTATE_X_POS, BUTTON_ROTATE_Y_POS,
                  BUTTONS_WIDTH, BUTTONS_HEIGHT);

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

    return true;
}

static Evas_Object *_create_button(Evas_Object *parent, Evas_Smart_Cb cb, void *data)
{
    Evas_Object *btn = elm_button_add(parent);
    if (btn)
    {
        evas_object_show(btn);
        evas_object_smart_callback_add(btn, "clicked", cb, data);
    }
    return btn;
}

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_flip_rotate_view *view = (image_flip_rotate_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 _on_button_flip_clicked_cb(void *data, Evas_Object *obj, void *event_info)
{
    RETM_IF(NULL == data, "data is NULL");
    long long time_begin = get_ticks();
    image_flip_rotate_view *view = data;
    if (view->flip_state + 1 < FLIP_STATES_MAX)
    {
        ++(view->flip_state);
    }
    else
    {
        view->flip_state = FLIP_STATE_NONE;
    }
    elm_image_orient_set(view->image_flip, _convert_to_elm_flip_state(view->flip_state));
    _update_flip_state(view);
    view->ticks = get_ticks() - time_begin;
    _update_label(view);
}

static void _on_button_rotate_clicked_cb(void *data, Evas_Object *obj, void *event_info)
{
    RETM_IF(NULL == data, "data is NULL");
    long long time_begin = get_ticks();
    image_flip_rotate_view *view = data;
    if (view->rotate_state + 1 < ROTATE_STATES_MAX)
    {
        ++(view->rotate_state);
    }
    else
    {
        view->rotate_state = ROTATE_STATE_0;
    }
    elm_image_orient_set(view->image_rotate, _convert_to_elm_rotate_state(view->rotate_state));
    _update_rotate_state(view);
    view->ticks = get_ticks() - time_begin;
    _update_label(view);
}

static void _change_image(image_flip_rotate_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_flip, image_id)
            || !_load_image(view->image_rotate, image_id))
    {
        ERR("_load_image failed");
    }
    view->image_id = image_id;

    _on_image_changed(view);
    _update_view_controls(view);
}

static void _update_view_controls(image_flip_rotate_view *view)
{
    RETM_IF(NULL == view, "view is NULL");
    _update_title(view);
    _update_toolbar_buttons(view);
    _update_flip_state(view);
    _update_rotate_state(view);
    _update_label(view);
}

static void _update_title(image_flip_rotate_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_label(image_flip_rotate_view *view)
{
    RETM_IF(NULL == view, "view is NULL");
    const char *image_name = image_infos[view->image_id].name;
    const char *pixel_format = _colorspace_to_str(image_infos[view->image_id].colorspace);
    char buf[LABEL_MAX_LEN] = {'\0'};
    snprintf(buf, LABEL_MAX_LEN, LABEL_TEXT_TEMPLATE, LABEL_REL_PATH, image_name, pixel_format,
             flip_state_names[view->flip_state], rotate_state_names[view->rotate_state], view->ticks);
    elm_object_text_set(view->label, buf);
}

static void _update_toolbar_buttons(image_flip_rotate_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_flip_state(image_flip_rotate_view *view)
{
    RETM_IF(NULL == view, "view is NULL");
    elm_object_text_set(view->button_flip, flip_state_names[view->flip_state]);
}

static void _update_rotate_state(image_flip_rotate_view *view)
{
    RETM_IF(NULL == view, "view is NULL");
    elm_object_text_set(view->button_rotate, rotate_state_names[view->rotate_state]);
}

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

static void _on_image_changed(image_flip_rotate_view *view)
{
    RETM_IF(NULL == view, "view is NULL");
    view->ticks = 0;
    view->flip_state = FLIP_STATE_NONE;
    view->rotate_state = ROTATE_STATE_0;
}

static int _convert_to_elm_flip_state(const int state)
{
    int result = ELM_IMAGE_ORIENT_0;
    if (FLIP_STATE_HORIZONTAL == state)
    {
        result = ELM_IMAGE_FLIP_HORIZONTAL;
    }
    else if (FLIP_STATE_VERTICAL == state)
    {
        result = ELM_IMAGE_ROTATE_180;
    }
    else if (FLIP_STATE_NONE == state)
    {
        result = ELM_IMAGE_FLIP_VERTICAL;
    }
    else
    {
        ERR("Incorrect argument %d", state);
    }
    return result;
}

static int _convert_to_elm_rotate_state(const int state)
{
    return ELM_IMAGE_ROTATE_90;
}

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;
}
