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

#include <Elementary.h>
#include <player.h>

#define TIMER_UPD_PERIOD 0.1
#define TIME_LABEL_LEN 64

#define STR_STOP "Stop"
#define STR_PLAY "Play"
#define STR_PAUSE "Pause"


static const char *frame_titel_str = "Video player";

static const char *align_right_str = "right";
static const char *align_left_str = "left";
static const char *time_label_template = "<p font_size=20 align=%s >%02u:%02u</p>";


struct _video_player_view
{
    app_data *app;
    Elm_Object_Item *frame_item;
    Evas_Object *navi;
    Evas_Object *layout;
    Evas_Object *toolbar;
    Evas_Object *video_rect;
    Evas_Object *current_time_label;
    Evas_Object *all_time_label;
    Evas_Object *progressbar;

    player_h player;

    Elm_Object_Item *stop_item;
    Elm_Object_Item *play_pause_item;

    Ecore_Timer *timer;

    player_result_cb callback;
    void *user_data;
};

static void _register_app_cbs(video_player_view *view);
static void _on_pause_cb(void *user_data);
static player_h _create_player(video_player_view *this);
static bool _is_playing(video_player_view *this);
static void _destroy_player(video_player_view *this);
static void _destroy(video_player_view *this);
static void _push(video_player_view *this);
static void _on_transition_finished(void *data, Evas_Object *obj, void *event_info);
static Eina_Bool _on_pop_start(void *data, Elm_Object_Item *it);
static Evas_Object *_main_layout_add(video_player_view *this, Evas_Object *parent);
static void _start_player(video_player_view *this);
static void _stop_player(video_player_view *this);
static void _pause_player(video_player_view *this);
static void _prepare_player(video_player_view *this, const char *res_path);
static Evas_Object *_progressbar_add(video_player_view *this, Evas_Object *parent);
static Evas_Object *_video_rect_add(Evas_Object *parent);
static Evas_Object *_label_add(Evas_Object *parent);
static void _label_time_set(Evas_Object *label, bool right_align, unsigned int seconds);
static void _show_duration(video_player_view *this);
static Evas_Object *_toolbar_add(video_player_view *this, Evas_Object *parent);
static void _toolbar_buttons_upd(video_player_view *this, bool is_finished);
static void _on_toolbar_stop_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info);
static void _on_toolbar_play_pause_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info);
static void _on_player_completed_cb(void *user_data);
static void _on_main_layout_del_cb(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void _timer_start(video_player_view *this);
static void _timer_stop(video_player_view *this);
static void _progressbar_pos_upd(video_player_view *this);
static void _time_current_upd(video_player_view *this);
static Eina_Bool _on_timer_upd_cb(void *data);

Elm_Object_Item *video_player_view_add(app_data *app, Evas_Object *navi, const char *video_file, player_result_cb callback, void *user_data)
{
    RETVM_IF(!video_file, NULL, "video file is NULL")
    video_player_view *this = calloc(1, sizeof(video_player_view));

    if(this)
    {
        this->navi = navi;
        this->app = app;
        this->callback = callback;
        this->user_data = user_data;
        this->player = _create_player(this);
        if(this->player)
        {
            this->layout = _main_layout_add(this, navi);
            if(this->layout)
            {
                _register_app_cbs(this);
                _prepare_player(this, video_file);
                _show_duration(this);

                this->timer = ecore_timer_add(TIMER_UPD_PERIOD, _on_timer_upd_cb, this);
                _timer_stop(this);

                evas_object_event_callback_add(this->layout, EVAS_CALLBACK_DEL, _on_main_layout_del_cb, this);
                _push(this);
            }
            else
            {
                _destroy(this);
                return NULL;
            }
        }
        else
        {
            _destroy(this);
            return NULL;
        }
    }

    return this->frame_item;
}

static void _register_app_cbs(video_player_view *view)
{
    app_cbs cbs =
    {
       .pause = _on_pause_cb,
       .resume = NULL
    };

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

static void _on_pause_cb(void *user_data)
{
    video_player_view *view = user_data;

    if(view && _is_playing(view))
    {
        _pause_player(view);
    }
}

static player_h _create_player(video_player_view *this)
{
    player_h player = NULL;

    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, this);

    return player;
}

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

    return state == PLAYER_STATE_PLAYING;
}

static void _destroy_player(video_player_view *this)
{
    if(this->player)
    {
        player_stop(this->player);
        player_unprepare(this->player);
        player_destroy(this->player);
        this->player = NULL;
    }
}

static Eina_Bool _on_pop_start(void *data, Elm_Object_Item *it)
{
    video_player_view *this = data;
    _destroy_player(this);

    evas_object_del(this->video_rect);
    this->video_rect = NULL;
    ecore_timer_del(this->timer);
    this->timer = NULL;

    if(this && this->callback)
    {
        this->callback(STATE_FINISHED, this->user_data);
    }
    return EINA_TRUE;
}


static void _destroy(video_player_view *this)
{
    app_reset_cbs(this->app);
    _destroy_player(this);

    if(this->video_rect)
    {
        evas_object_del(this->video_rect);
        this->video_rect = NULL;
    }
    if(this->timer)
    {
        ecore_timer_del(this->timer);
        this->timer = NULL;
    }

    free(this);
}

static void _push(video_player_view *this)
{
    evas_object_smart_callback_add(this->navi, "transition,finished", _on_transition_finished, this);
    this->frame_item = elm_naviframe_item_push(this->navi, frame_titel_str, NULL, NULL, this->layout, NULL);

    elm_naviframe_item_pop_cb_set(this->frame_item, _on_pop_start, this);
    this->toolbar = _toolbar_add(this, this->navi);
    elm_object_item_part_content_set(this->frame_item, "toolbar", this->toolbar);
}

static void _on_transition_finished(void *data, Evas_Object *obj, void *event_info)
{
    video_player_view *this = data;
    if(this)
    {
        _start_player(this);
        evas_object_smart_callback_del(this->navi, "transition,finished", _on_transition_finished);
    }
}

static Evas_Object *_main_layout_add(video_player_view *this, Evas_Object *parent)
{
    Evas_Object *layout = elm_layout_add(parent);
    this->layout = layout;

    elm_layout_file_set(layout, VIDEOPLAYEREDJ, "video_player_layout");

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

    this->current_time_label = _label_add(layout);
    _label_time_set(this->current_time_label, false, 0);
    elm_object_part_content_set(layout, "swallow.timepoint.current", this->current_time_label);

    this->all_time_label = _label_add(layout);
    _label_time_set(this->all_time_label, true, 0);
    elm_object_part_content_set(layout, "swallow.timepoint.all", this->all_time_label);

    this->progressbar = _progressbar_add(this, layout);
    elm_object_part_content_set(layout, "swallow.progressbar", this->progressbar);

    this->video_rect = _video_rect_add(this->layout);
    elm_object_part_content_set(this->layout, "swallow.render", this->video_rect);

    return layout;
}

static void _start_player(video_player_view *this)
{
    player_state_e state = PLAYER_STATE_NONE;
    player_get_state(this->player, &state);

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

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

    _toolbar_buttons_upd(this, false);
}

static void _stop_player(video_player_view *this)
{
    player_state_e state = PLAYER_STATE_NONE;
    player_get_state(this->player, &state);

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

    elm_progressbar_value_set(this->progressbar, 0.0);
    _toolbar_buttons_upd(this, true);
}

static void _pause_player(video_player_view *this)
{
    player_state_e state = PLAYER_STATE_NONE;
    player_get_state(this->player, &state);

    if(state == PLAYER_STATE_PLAYING)
    {
        player_pause(this->player);
    }

    _toolbar_buttons_upd(this, false);
}

static void _prepare_player(video_player_view *this, const char *res_path)
{
    player_set_uri(this->player, res_path);
    player_set_display(this->player, PLAYER_DISPLAY_TYPE_EVAS, GET_DISPLAY(this->video_rect));
    player_prepare(this->player);
}

static Evas_Object *_progressbar_add(video_player_view *this, Evas_Object *parent)
{
    Evas_Object *progressbar = elm_progressbar_add(parent);
    evas_object_size_hint_align_set(progressbar, EVAS_HINT_FILL, 0.5);
    evas_object_size_hint_weight_set(progressbar, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
    elm_progressbar_value_set(progressbar, 0.0);
    evas_object_show(progressbar);
    return progressbar;
}

static Evas_Object *_video_rect_add(Evas_Object *parent)
{
    Evas *evas = evas_object_evas_get(parent);
    Evas_Object *rect = evas_object_image_filled_add(evas);
    evas_object_size_hint_weight_set(rect, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
    evas_object_show(rect);
    return rect;
}

static Evas_Object *_label_add(Evas_Object *parent)
{
    Evas_Object *obj = elm_label_add(parent);
    evas_object_show(obj);
    return obj;
}

static void _label_time_set(Evas_Object *label, bool right_align, unsigned int seconds)
{
    char buf[TIME_LABEL_LEN] = {'\0'};

    const char * align = right_align ? align_right_str : align_left_str;
    snprintf(buf, TIME_LABEL_LEN, time_label_template, align, seconds / 60, seconds % 60);
    elm_object_text_set(label, buf);
}

static void _show_duration(video_player_view *this)
{
    int duration = 0;
    player_get_duration(this->player, &duration);
    _label_time_set(this->all_time_label, true, duration / 1000);
}

static Evas_Object *_toolbar_add(video_player_view *this, 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);

    this->stop_item = elm_toolbar_item_append(toolbar, NULL, STR_STOP, _on_toolbar_stop_btn_pressed_cb, this);
    this->play_pause_item = elm_toolbar_item_append(toolbar, NULL, STR_PLAY, _on_toolbar_play_pause_btn_pressed_cb, this);

    elm_object_item_disabled_set(this->stop_item, EINA_TRUE);

    return toolbar;
}

static void _toolbar_buttons_upd(video_player_view *this, bool is_finished)
{
    const char *text = _is_playing(this) ? STR_PAUSE : STR_PLAY;
    elm_object_item_text_set(this->play_pause_item, text);

    elm_object_item_disabled_set(this->stop_item, (Eina_Bool)is_finished);
}

static void _on_toolbar_stop_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info)
{
    video_player_view *this = data;

    if(this)
    {
        _stop_player(this);
        _label_time_set(this->current_time_label, false, 0);
    }
}

static void _on_toolbar_play_pause_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info)
{
    video_player_view *this = data;

    if(this)
    {
        if(_is_playing(this))
        {
            _pause_player(this);
        }
        else
        {
            _start_player(this);
        }
    }
}

static void _on_player_completed_cb(void *user_data)
{
    video_player_view *this = user_data;

    if(this)
    {
        _stop_player(this);
        _label_time_set(this->current_time_label, false, 0);
    }
}

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

    if(this)
    {
        _destroy(this);
        if(this->callback)
        {
            this->callback(STATE_DESTROYED, this->user_data);
        }
    }
}

static void _timer_start(video_player_view *this)
{
    ecore_timer_reset(this->timer);
    ecore_timer_thaw(this->timer);
}

static void _timer_stop(video_player_view *this)
{
    ecore_timer_freeze(this->timer);
}

static void _progressbar_pos_upd(video_player_view *this)
{
    if(this->player)
    {
        int duration = 0;
        player_get_duration(this->player, &duration);

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

        elm_progressbar_value_set(this->progressbar, pos / (double)duration);
    }
}

static void _time_current_upd(video_player_view *this)
{
    if(this->player)
    {
        int pos = 0;
        player_get_play_position(this->player, &pos);
        _label_time_set(this->current_time_label, false, pos / 1000);
    }
}

static Eina_Bool _on_timer_upd_cb(void *data)
{
    video_player_view *this = data;

    if(this)
    {
        _progressbar_pos_upd(this);
        _time_current_upd(this);
    }

    return EINA_TRUE;
}
