/*
 * 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/media-player-view.h"
#include "../../common/inc/main-app.h"

#include <libgen.h>
#include <assert.h>
#include <string.h>
#include <player.h>
#include <Elementary.h>
#include <Ecore.h>

#define TIMER_UPD_PERIOD 0.1
#define MAX_LABEL_LEN 512
#define MEDIA_RES_COUNT 7
#define TITLE_MAX_LEN 128
#define SWALLOW_VISUALIZATION "swallow.visualization"

typedef enum
{
    M_UNKNOWN,
    M_AUDIO,
    M_VIDEO
} media_type;

typedef struct
{
    media_type type;
    const char *res_path;
} media_data;

struct _media_player_view
{
    app_data *app;
    Elm_Object_Item *frame_item;
    Evas_Object *navi;
    Evas_Object *layout;
    Evas_Object *toolbar;
    Evas_Object *audio_icon;
    Evas_Object *video_rect;
    Evas_Object *progressbar;
    Evas_Object *path_label;
    Evas_Object *status_label;
    Evas_Object *pos_label;

    Ecore_Timer *timer;
    player_h player;
    unsigned res_index;
    media_type media_type;

    Elm_Object_Item *prev_item;
    Elm_Object_Item *next_item;
    Elm_Object_Item *play_stop_item;

    app_device_orientation_e rotation;
    int prev_view_rotation;
};

static media_data _media_data_list[MEDIA_RES_COUNT] =
{
    {M_AUDIO, SAMPLEMP3},
    {M_AUDIO, SAMPLEAAC},
    {M_AUDIO, SAMPLEWAV},
    {M_AUDIO, SAMPLEAMR},
    {M_VIDEO, SAMPLEH263},
    {M_VIDEO, SAMPLEH264},
    {M_VIDEO, SAMPLEMPEG4}
};

static void _destroy(media_player_view *view);
static void _push(media_player_view *view);
static Evas_Object *_main_layout_add(media_player_view *view, Evas_Object *parent);
static void _toolbar_play_stop_btn_upd(media_player_view *view);
static Evas_Object *_info_box_add(media_player_view *view, Evas_Object *parent);
static Evas_Object *_toolbar_add(media_player_view *view, Evas_Object *parent);
static Evas_Object *_image_add(Evas_Object *parent);
static Evas_Object *_label_add(Evas_Object *parent);
static void _label_text_set(Evas_Object *label, const char *text);
static void _label_status_set(media_player_view *view, const char *text);
static void _label_position_set(media_player_view *view, int current, int max);
static void _label_res_path_set(media_player_view *view, const char *path);
static Evas_Object *_video_rect_add(Evas_Object *parent);
static Evas_Object *_progressbar_add(media_player_view *view, Evas_Object *parent);
static void _progressbar_upd_pos(media_player_view *view);
static void _info_upd_pos(media_player_view *view);
static void _navi_bar_slide_number_upd(media_player_view *view);
static void _prepare_view_for_player(media_player_view *view, media_type type);
static void _prepare_player(media_player_view *view, media_type type, const char *res_path);
static void _start_player(media_player_view *view);
static void _stop_player(media_player_view *view);
static bool _is_playing(media_player_view *view);
static player_h _create_player(media_player_view *view);
static void _destroy_player(media_player_view *view);
static void _open_media_res(media_player_view *view, unsigned index);
static void _timer_start(media_player_view *view);
static void _timer_stop(media_player_view *view);

static void _on_player_completed_cb(void *user_data);
static Eina_Bool _on_timer_upd_cb(void *data);
static void _on_slider_drag_stop_cb(void *data, Evas_Object *obj, void *event_info);
static void _on_slider_changed_cb(void *data, Evas_Object *obj, void *event_info);
static void _on_toolbar_prev_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info);
static void _on_toolbar_next_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info);
static void _on_toolbar_play_stop_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info);
static void _on_main_layout_del_cb(void *data, Evas *e, Evas_Object *obj, void *event_info);

static void _register_app_cbs(media_player_view *view);
static void _unregister_app_cbs(app_data *app);
static void _on_rotate_cb(app_event_info_h event_info, void *user_data);
static void _set_rotation(media_player_view *view);

media_player_view *media_player_view_add(app_data *app, Evas_Object *navi)
{
    media_player_view *view = calloc(1, sizeof(media_player_view));

    if(view)
    {
        view->navi = navi;
        view->app = app;
        view->rotation = app_get_device_orientation();
        view->prev_view_rotation = win_get_rotation(app->win);
        view->player = _create_player(view);
        view->layout = _main_layout_add(view, navi);

        if(view->player && view->layout)
        {
            _register_app_cbs(view);
            view->timer = ecore_timer_add(TIMER_UPD_PERIOD, _on_timer_upd_cb, view);
            _timer_stop(view);

            evas_object_event_callback_add(view->layout, EVAS_CALLBACK_DEL, _on_main_layout_del_cb, view);
            _push(view);
            _open_media_res(view, 0);
        }
        else
        {
            evas_object_del(view->layout);
            _destroy(view);
            return NULL;
        }
    }

    return view;
}

static void _timer_start(media_player_view *view)
{
    ecore_timer_reset(view->timer);
    ecore_timer_thaw(view->timer);
}

static void _timer_stop(media_player_view *view)
{
    ecore_timer_freeze(view->timer);
}

static player_h _create_player(media_player_view *view)
{
    player_h player;

    player_create(&player);
    player_set_sound_type(player, SOUND_TYPE_MEDIA);
    player_set_volume(player, 1.0, 1.0);
    player_set_looping(player, false);
    player_set_completed_cb(player, _on_player_completed_cb, view);

    return player;
}

static bool _is_playing(media_player_view *view)
{
    player_state_e state;
    player_get_state(view->player, &state);

    return state == PLAYER_STATE_PLAYING;
}

static void _destroy_player(media_player_view *view)
{
    player_stop(view->player);
    player_unprepare(view->player);
    player_destroy(view->player);
    view->player = NULL;
}

static void _destroy(media_player_view *view)
{
    _unregister_app_cbs(view->app);
    win_set_rotation(view->app->win, view->prev_view_rotation);

    _destroy_player(view);

    ecore_timer_del(view->timer);
    view->timer = NULL;

    evas_object_del(view->audio_icon);
    view->audio_icon = NULL;

    evas_object_del(view->video_rect);
    view->video_rect = NULL;

    free(view);
}

static void _push(media_player_view *view)
{
    view->frame_item = elm_naviframe_item_push(view->navi, "Media player", NULL, NULL, view->layout, NULL);

    view->toolbar = _toolbar_add(view, view->navi);
    elm_object_item_part_content_set(view->frame_item, "toolbar", view->toolbar);
    _navi_bar_slide_number_upd(view);
}

static Evas_Object *_main_layout_add(media_player_view *view, Evas_Object *parent)
{
    Evas_Object *layout = elm_layout_add(parent);
    view->layout = layout;

    elm_layout_file_set(layout, MEDIAPLAYEREDJ, "media_player");
    _set_rotation(view);

    evas_object_size_hint_weight_set(layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
    evas_object_size_hint_align_set(layout, EVAS_HINT_FILL, EVAS_HINT_FILL);
    evas_object_show(layout);

    Evas_Object *info_box = _info_box_add(view, layout);
    elm_object_part_content_set(layout, "swallow.info", info_box);

    view->progressbar = _progressbar_add(view, layout);
    elm_object_part_content_set(layout, "swallow.progressbar", view->progressbar);
    elm_object_disabled_set(view->progressbar, EINA_TRUE);

    view->audio_icon = _image_add(view->layout);
    view->video_rect = _video_rect_add(view->layout);

    return layout;
}

static void _start_player(media_player_view *view)
{
    player_state_e state;
    player_get_state(view->player, &state);

    if(state != PLAYER_STATE_PLAYING)
    {
        player_start(view->player);

        if(_is_playing(view))
        {
            _toolbar_play_stop_btn_upd(view);
            elm_object_disabled_set(view->progressbar, EINA_FALSE);

            int ms = 0;
            player_get_duration(view->player, &ms);
            elm_slider_min_max_set(view->progressbar, 0, ms / 1000);
            elm_slider_value_set(view->progressbar, 0.0);
            _label_status_set(view, "Playing");
        }
    }

    if(_is_playing(view))
    {
        _timer_start(view);
    }
}

static void _stop_player(media_player_view *view)
{
    player_state_e state;
    player_get_state(view->player, &state);

    if(state == PLAYER_STATE_PLAYING || state == PLAYER_STATE_PAUSED)
    {
        player_stop(view->player);
    }

    if(!_is_playing(view))
    {
        elm_slider_value_set(view->progressbar, 0.0);
        elm_object_disabled_set(view->progressbar, EINA_TRUE);
        _toolbar_play_stop_btn_upd(view);
        _timer_stop(view);
        _label_status_set(view, "Stopped");
        _label_position_set(view, 0, 0);
    }
}

static void _prepare_player(media_player_view *view, media_type type, const char *res_path)
{
    if(type == M_AUDIO)
    {
        player_set_uri(view->player, res_path);
        player_prepare(view->player);
    }
    else if(type == M_VIDEO)
    {
        player_set_uri(view->player, res_path);
        player_set_display(view->player, PLAYER_DISPLAY_TYPE_EVAS, GET_DISPLAY(view->video_rect));
        player_set_display_mode(view->player, PLAYER_DISPLAY_MODE_FULL_SCREEN);
        player_prepare(view->player);
    }
}

static void _open_media_res(media_player_view *view, unsigned index)
{
    if(index < MEDIA_RES_COUNT)
    {
        media_type type = _media_data_list[index].type;
        const char *res_path = get_resource_path(_media_data_list[index].res_path);

        _stop_player(view);
        player_unprepare(view->player);

        _prepare_view_for_player(view, type);
        _prepare_player(view, type, res_path);

        view->res_index = index;
        view->media_type = type;

        _navi_bar_slide_number_upd(view);
        _label_res_path_set(view, res_path);
        _label_position_set(view, 0, 0);

        player_state_e state;
        player_get_state(view->player, &state);

        if(state == PLAYER_STATE_READY)
        {
            _label_status_set(view, "Opened");
        }
        else
        {
            _label_status_set(view, "Error");
        }
    }
}

static void _navi_bar_slide_number_upd(media_player_view *view)
{
    if(view->frame_item)
    {
        char buf[TITLE_MAX_LEN] = {'\0'};
        snprintf(buf, TITLE_MAX_LEN, "Media player(%d/%d)", view->res_index + 1, MEDIA_RES_COUNT);
        elm_object_item_text_set(view->frame_item, buf);
    }
}

static Evas_Object *_progressbar_add(media_player_view *view, Evas_Object *parent)
{
    Evas_Object *slider = elm_slider_add(parent);
    elm_slider_horizontal_set(slider, EINA_TRUE);
    evas_object_show(slider);
    elm_slider_indicator_format_set(slider, "%1.0f");
    evas_object_size_hint_weight_set(slider, EVAS_HINT_EXPAND, 0);
    evas_object_size_hint_align_set(slider, EVAS_HINT_FILL, 0);
    elm_slider_value_set(slider, 0);
    elm_slider_indicator_show_set(slider, EINA_TRUE);
    evas_object_smart_callback_add(slider, "slider,drag,stop", _on_slider_drag_stop_cb, view);
    evas_object_smart_callback_add(slider, "changed", _on_slider_changed_cb, view);
    return slider;
}

static void _progressbar_upd_pos(media_player_view *view)
{
    int ms = 0;
    player_get_play_position(view->player, &ms);
    elm_slider_value_set(view->progressbar, ms / 1000.0f + 0.5f);
}

static void _info_upd_pos(media_player_view *view)
{
    int duration = 0;
    player_get_duration(view->player, &duration);

    int pos = 0;
    player_get_play_position(view->player, &pos);

    _label_position_set(view, pos, duration);
}

static void _prepare_view_for_player(media_player_view *view, media_type type)
{
    Evas_Object *old_content = elm_object_part_content_get(view->layout, SWALLOW_VISUALIZATION);
    if(old_content)
    {
        elm_object_part_content_unset(view->layout, SWALLOW_VISUALIZATION);
        evas_object_hide(old_content);
    }

    Evas_Object *new_content = NULL;

    if(type == M_AUDIO)
    {
        new_content = view->audio_icon;
    }
    else if(type == M_VIDEO)
    {
        new_content = view->video_rect;
    }

    if(new_content)
    {
        elm_object_part_content_set(view->layout, SWALLOW_VISUALIZATION, new_content);
        evas_object_show(new_content);
    }
}

static Evas_Object *_image_add(Evas_Object *parent)
{
    Evas_Object *image = elm_image_add(parent);
    if (image)
    {
        elm_image_file_set(image, get_resource_path(AUDIOICON), NULL);
        elm_image_no_scale_set(image, EINA_FALSE);
        elm_image_resizable_set(image, EINA_TRUE, EINA_TRUE);
        elm_image_smooth_set(image, EINA_TRUE);
        elm_image_aspect_fixed_set(image, EINA_TRUE);
        elm_image_editable_set(image, EINA_FALSE);
        evas_object_show(image);
    }
    return image;
}

static Evas_Object *_label_add(Evas_Object *parent)
{
    Evas_Object *obj = elm_label_add(parent);
    evas_object_size_hint_weight_set(obj, 1.0, EVAS_HINT_EXPAND);
    evas_object_size_hint_align_set(obj, 0.0, 0.5);
    evas_object_show(obj);
    return obj;
}

static void _label_text_set(Evas_Object *label, const char *text)
{
    char buf[MAX_LABEL_LEN] = {'\0'};
    snprintf(buf, MAX_LABEL_LEN, "%s", text);
    elm_object_text_set(label, buf);
}

static void _label_status_set(media_player_view *view, const char *text)
{
    char buf[MAX_LABEL_LEN] = {'\0'};
    snprintf(buf, MAX_LABEL_LEN, "Status: %s", text);
    _label_text_set(view->status_label, buf);
}

static void _label_position_set(media_player_view *view, int current, int max)
{
    char buf[MAX_LABEL_LEN] = {'\0'};
    snprintf(buf, MAX_LABEL_LEN, "Position: %d/%d ms", current, max);
    _label_text_set(view->pos_label, buf);
}

static void _label_res_path_set(media_player_view *view, const char *path)
{
    _label_text_set(view->path_label, basename((char*)path));
}

static Evas_Object *_video_rect_add(Evas_Object *parent)
{
    Evas *evas = evas_object_evas_get(parent);
    Evas_Object *image = evas_object_image_filled_add(evas);
    evas_object_show(image);
    return image;
}

static Evas_Object *_info_box_add(media_player_view *view, Evas_Object *parent)
{
    Evas_Object *box = elm_box_add(parent);
    elm_box_horizontal_set(box, false);
    evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
    evas_object_size_hint_align_set(box, EVAS_HINT_FILL, EVAS_HINT_FILL);
    evas_object_show(box);

    view->path_label = _label_add(box);
    view->status_label = _label_add(box);
    view->pos_label = _label_add(box);

    elm_box_pack_end(box, view->path_label);
    elm_box_pack_end(box, view->status_label);
    elm_box_pack_end(box, view->pos_label);

    return box;
}

static Evas_Object *_toolbar_add(media_player_view *view, Evas_Object *parent)
{
    Evas_Object *toolbar = elm_toolbar_add(parent);

    evas_object_size_hint_weight_set(toolbar, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
    evas_object_size_hint_align_set(toolbar, EVAS_HINT_FILL, EVAS_HINT_FILL);
    evas_object_show(toolbar);

    elm_toolbar_select_mode_set(toolbar, ELM_OBJECT_SELECT_MODE_NONE);
    elm_toolbar_shrink_mode_set(toolbar, ELM_TOOLBAR_SHRINK_EXPAND);
    elm_toolbar_transverse_expanded_set(toolbar, EINA_TRUE);

    view->prev_item = elm_toolbar_item_append(toolbar, NULL, "Prev", _on_toolbar_prev_btn_pressed_cb, view);
    view->next_item = elm_toolbar_item_append(toolbar, NULL, "Next", _on_toolbar_next_btn_pressed_cb, view);
    view->play_stop_item = elm_toolbar_item_append(toolbar, NULL, "Play", _on_toolbar_play_stop_btn_pressed_cb, view);

    elm_object_item_disabled_set(view->prev_item, EINA_TRUE);

    return toolbar;
}

static void _toolbar_play_stop_btn_upd(media_player_view *view)
{
    const char *text = _is_playing(view) ? "Stop" : "Play";
    elm_object_item_text_set(view->play_stop_item, text);
}

static void _on_slider_drag_stop_cb(void *data, Evas_Object *obj, void *event_info)
{
    media_player_view *view = data;
    if(view)
    {
        int val = (int)elm_slider_value_get(obj);
        player_set_play_position(view->player, val * 1000, true, NULL, NULL);
    }
}

static void _on_slider_changed_cb(void *data, Evas_Object *obj, void *event_info)
{
    media_player_view *view = data;
    if(view)
    {
        int val = (int)elm_slider_value_get(obj);
        player_set_play_position(view->player, val * 1000, true, NULL, NULL);
    }
}

static void _on_toolbar_prev_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info)
{
    media_player_view *view = data;

    if(view)
    {
        unsigned index = view->res_index - 1;
        _open_media_res(view, index);

        if(view->res_index == index)
        {
            bool disable = view->res_index == 0;
            elm_object_item_disabled_set(view->prev_item, disable);
            elm_object_item_disabled_set(view->next_item, EINA_FALSE);
        }
    }
}

static void _on_toolbar_next_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info)
{
    media_player_view *view = data;

    if(view)
    {
        unsigned index = view->res_index + 1;
        _open_media_res(view, index);

        if(index == view->res_index)
        {
            bool disable = view->res_index >= MEDIA_RES_COUNT - 1;
            elm_object_item_disabled_set(view->next_item, disable);
            elm_object_item_disabled_set(view->prev_item, EINA_FALSE);
        }
    }
}

static void _on_toolbar_play_stop_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info)
{
    media_player_view *view = data;

    if(view)
    {
        if(_is_playing(view))
        {
            _stop_player(view);
        }
        else
        {
            _start_player(view);
        }
    }
}

static Eina_Bool _on_timer_upd_cb(void *data)
{
    media_player_view *view = data;

    if(view)
    {
        _progressbar_upd_pos(view);
        _info_upd_pos(view);
    }

    return EINA_TRUE;
}

static void _on_player_completed_cb(void *user_data)
{
    media_player_view *view = user_data;

    if(view)
    {
        _stop_player(view);
    }
}

static void _on_main_layout_del_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
    media_player_view *view = data;

    if(view)
    {
        _destroy(view);
    }
}

static void _register_app_cbs(media_player_view *view)
{
    app_cbs cbs;

    ui_app_add_event_handler(&cbs.event_handlers[APP_EVENT_DEVICE_ORIENTATION_CHANGED],
                             APP_EVENT_DEVICE_ORIENTATION_CHANGED, _on_rotate_cb, view);

    app_set_cbs(view->app, cbs, view);
}

static void _unregister_app_cbs(app_data *app)
{
    ui_app_remove_event_handler(app->cb.event_handlers[APP_EVENT_DEVICE_ORIENTATION_CHANGED]);
    app_reset_cbs(app);
}

static void _on_rotate_cb(app_event_info_h event_info, void *user_data)
{
    media_player_view *view = (media_player_view *)user_data;
    if(view)
    {
        app_event_get_device_orientation(event_info, &view->rotation);
        _set_rotation(view);
    }
}

static void _set_rotation(media_player_view *view)
{
    switch (view->rotation)
    {
        case APP_DEVICE_ORIENTATION_0:
        case APP_DEVICE_ORIENTATION_180:
            edje_object_signal_emit(elm_layout_edje_get(view->layout), "portrait", "");
            break;
        case APP_DEVICE_ORIENTATION_270:
        case APP_DEVICE_ORIENTATION_90:
            edje_object_signal_emit(elm_layout_edje_get(view->layout), "landscape", "");
            win_show_indicator(view->app->win, EINA_TRUE);
            break;
        default:
            break;
    }

    win_set_rotation(view->app->win, (int)view->rotation);
}
