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

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

#define MAX_LABEL_LEN 256
#define TIMER_UPD_PERIOD 0.1
#define STR_STOP "Stop"
#define STR_PLAY "Play"
#define STR_PAUSE "Pause"
#define STR_TITLE "Audio player"
#define LABEL_LEFT_ALIGN 0.1
#define LABEL_RIGHT_ALIGN 0.9
#define PROGRESS_BAR_WEIGHT 0.1

struct _audio_player_view
{
    app_data *app;
    Elm_Object_Item *frame_item;
    Evas_Object *navi;
    Evas_Object *box; // main layout
    Evas_Object *toolbar;
    Evas_Object *progressbar;
    Evas_Object *left_label;
    Evas_Object *right_label;

    Elm_Object_Item *play_pause_item;
    Elm_Object_Item *stop_item;

    player_h player;
    char *file_path;

    Ecore_Timer *timer;
};

static void _destroy(audio_player_view *view);

static player_h _player_create(audio_player_view *view);
static void _player_destroy(audio_player_view *view);
static void _player_start(audio_player_view *view);
static void _player_resume(audio_player_view *view);
static void _player_stop(audio_player_view *view);
static void _player_pause(audio_player_view *view);
static bool _player_is_playing(audio_player_view *view);

static void _push(audio_player_view *view);
static void _register_app_cbs(audio_player_view *view);
static Evas_Object *_main_layout_add(audio_player_view *view, Evas_Object *parent);
static Evas_Object *_toolbar_add(audio_player_view *view, Evas_Object *parent);
static Evas_Object *_image_add(Evas_Object *parent);
static Evas_Object *_label_box_add(audio_player_view *view, Evas_Object *parent);
static void _label_set_time(Evas_Object *label, int seconds);
static Evas_Object *_progress_bar_add(Evas_Object *parent);
static void _update_ui(audio_player_view *view);
static void _update_left_label(audio_player_view *view);
static void _update_right_label(audio_player_view *view);
static void _update_progress_bar(audio_player_view *view);
static void _update_toolbar_btns(audio_player_view *view);

static void _timer_start(audio_player_view *view);
static void _timer_stop(audio_player_view *view);

static Eina_Bool _on_timer_upd_cb(void *data);
static void _on_player_completed_cb(void *user_data);
static void _on_toolbar_play_pause_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info);
static void _on_toolbar_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 _on_pause_cb(void *user_data);
static void _on_resume_cb(void *user_data);

audio_player_view *audio_player_view_add(app_data *app, Evas_Object *navi, const char *file_path)
{
    RETVM_IF(!file_path, NULL, "audio file path is null");

    audio_player_view *view = calloc(1, sizeof(audio_player_view));

    if(view)
    {
        view->navi = navi;
        view->app = app;
        view->box = _main_layout_add(view, navi);
        view->file_path = strdup(file_path);

        if(view->box)
        {
            view->timer = ecore_timer_add(TIMER_UPD_PERIOD, _on_timer_upd_cb, view);
            evas_object_event_callback_add(view->box, EVAS_CALLBACK_DEL, _on_main_layout_del_cb, view);

            _register_app_cbs(view);
            _player_create(view);
            _player_start(view);
            _push(view);
            _update_ui(view);
        }
        else
        {
            _destroy(view);
            return NULL;
        }
    }

    return view;
}

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

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

static void _destroy(audio_player_view *view)
{
    app_reset_cbs(view->app);

    if(view->timer)
    {
        ecore_timer_del(view->timer);
        view->timer = NULL;
    }

    _player_destroy(view);
    free(view->file_path);
    view->file_path = NULL;
    free(view);
}

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

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

static player_h _player_create(audio_player_view *view)
{
    if(player_create(&view->player) == PLAYER_ERROR_NONE)
    {
        player_set_uri(view->player, view->file_path);
        player_set_sound_type(view->player, SOUND_TYPE_MEDIA);
        player_set_volume(view->player, 1.0, 1.0);
        player_prepare(view->player);
        player_set_completed_cb(view->player, _on_player_completed_cb, view);
    }

    return view->player;
}

static bool _player_is_playing(audio_player_view *view)
{
    bool res = false;

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

        res = state == PLAYER_STATE_PLAYING;
    }

    return res;
}

static void _player_destroy(audio_player_view *view)
{
    if(view->player)
    {
        player_stop(view->player);
        player_unprepare(view->player);
        player_destroy(view->player);
        view->player = NULL;
    }
}

static void _player_start(audio_player_view *view)
{
    if(view->player)
    {
        if(!_player_is_playing(view))
        {
            player_start(view->player);
        }

        if(_player_is_playing(view))
        {
            _timer_start(view);
        }
        _update_toolbar_btns(view);
    }
}

static void _player_resume(audio_player_view *view)
{
    if(view->player)
    {
        player_state_e state;
        player_get_state(view->player, &state);

        if(state == PLAYER_STATE_PAUSED)
        {
            _player_start(view);
        }
    }
}

static void _player_stop(audio_player_view *view)
{
    if(view->player)
    {
        player_state_e state;
        player_get_state(view->player, &state);

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

        if(!_player_is_playing(view))
        {
            _timer_stop(view);
        }

        _update_left_label(view);
        _update_progress_bar(view);
        _update_toolbar_btns(view);
    }
}

static void _player_pause(audio_player_view *view)
{
    if(view->player)
    {
        player_state_e state;
        player_get_state(view->player, &state);

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

        if(!_player_is_playing(view))
        {
            _timer_stop(view);
        }

        _update_toolbar_btns(view);
    }
}

static void _update_left_label(audio_player_view *view)
{
    int msec = 0;

    if(view->player)
    {
        player_get_play_position(view->player, &msec);
    }
    _label_set_time(view->left_label, (msec / 1000.0) + 0.5);
}

static void _update_right_label(audio_player_view *view)
{
    int msec = 0;

    if(view->player)
    {
        player_get_duration(view->player, &msec);
    }
    _label_set_time(view->right_label, (msec / 1000.0) + 0.5);
}

static void _update_progress_bar(audio_player_view *view)
{
    int dur_msec = 0;
    int pos_msec = 0;

    if(view->player)
    {
        player_get_duration(view->player, &dur_msec);
        player_get_play_position(view->player, &pos_msec);
    }

    if(dur_msec != 0)
    {
        elm_progressbar_value_set(view->progressbar, (double)pos_msec / dur_msec);
    }
    else
    {
        elm_progressbar_value_set(view->progressbar, 0.0);
    }
}

static void _update_toolbar_btns(audio_player_view *view)
{
    const char *play_pause = STR_PLAY;
    bool stop_disabled = true;

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

        stop_disabled = state != PLAYER_STATE_PLAYING && state != PLAYER_STATE_PAUSED;

        if(state == PLAYER_STATE_PLAYING)
        {
            play_pause = STR_PAUSE;
        }
    }

    elm_object_item_disabled_set(view->stop_item, stop_disabled);
    elm_object_item_text_set(view->play_pause_item, play_pause);
}

static void _update_ui(audio_player_view *view)
{
    _update_left_label(view);
    _update_right_label(view);
    _update_progress_bar(view);
    _update_toolbar_btns(view);
}

static void _push(audio_player_view *view)
{
    Elm_Object_Item *item = elm_naviframe_item_push(view->navi, STR_TITLE, NULL, NULL, view->box, NULL);

    view->toolbar = _toolbar_add(view, view->navi);
    elm_object_item_part_content_set(item, "toolbar", view->toolbar);

    view->frame_item = item;
}

static Evas_Object *_main_layout_add(audio_player_view *view, Evas_Object *parent)
{
    view->box = elm_box_add(parent);
    evas_object_show(view->box);
    evas_object_size_hint_weight_set(view->box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
    evas_object_size_hint_align_set(view->box, EVAS_HINT_FILL, EVAS_HINT_FILL);
    elm_box_horizontal_set(view->box, EINA_FALSE);

    Evas_Object *image = _image_add(view->box);
    Evas_Object *label_box = _label_box_add(view, view->box);
    view->progressbar = _progress_bar_add(view->box);

    elm_box_pack_end(view->box, image);
    elm_box_pack_end(view->box, label_box);
    elm_box_pack_end(view->box, view->progressbar);

    return view->box;
}

static Evas_Object *_image_add(Evas_Object *parent)
{
    Evas_Object *image = elm_image_add(parent);

    evas_object_size_hint_weight_set(image, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
    evas_object_size_hint_align_set(image, EVAS_HINT_FILL, EVAS_HINT_FILL);
    evas_object_show(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);

    return image;
}

static Evas_Object *_label_box_add(audio_player_view *view, Evas_Object *parent)
{
    Evas_Object *box = elm_box_add(parent);
    evas_object_show(box);
    evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, 0.0);
    evas_object_size_hint_align_set(box, EVAS_HINT_FILL, EVAS_HINT_FILL);
    elm_box_horizontal_set(box, EINA_TRUE);

    view->left_label = elm_label_add(box);
    evas_object_size_hint_weight_set(view->left_label, EVAS_HINT_EXPAND, 0.0);
    evas_object_size_hint_align_set(view->left_label, LABEL_LEFT_ALIGN, 0.5);
    evas_object_show(view->left_label);

    view->right_label = elm_label_add(box);
    evas_object_size_hint_weight_set(view->right_label, EVAS_HINT_EXPAND, 0.0);
    evas_object_size_hint_align_set(view->right_label, LABEL_RIGHT_ALIGN, 0.5);
    evas_object_show(view->right_label);

    elm_box_pack_start(box, view->left_label);
    elm_box_pack_end(box, view->right_label);

    return box;
}

static void _label_set_time(Evas_Object *label, int seconds)
{
    int h = seconds / 3600;
    int m = (seconds / 60) % 60;
    int s = seconds % 60;

    char buf[MAX_LABEL_LEN] = {'\0'};
    snprintf(buf, MAX_LABEL_LEN, "%02d:%02d:%02d", h, m, s);

    elm_object_text_set(label, buf);
}

static Evas_Object *_progress_bar_add(Evas_Object *parent)
{
    Evas_Object *progress = elm_progressbar_add(parent);

    evas_object_show(progress);
    evas_object_size_hint_weight_set(progress, EVAS_HINT_EXPAND, PROGRESS_BAR_WEIGHT);
    evas_object_size_hint_align_set(progress, EVAS_HINT_FILL, EVAS_HINT_FILL);
    elm_progressbar_horizontal_set(progress, EINA_TRUE);
    elm_progressbar_value_set(progress, 0.0);

    return progress;
}

static Evas_Object *_toolbar_add(audio_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->stop_item = elm_toolbar_item_append(toolbar, NULL, STR_STOP, _on_toolbar_stop_btn_pressed_cb, view);
    view->play_pause_item = elm_toolbar_item_append(toolbar, NULL, STR_PLAY, _on_toolbar_play_pause_btn_pressed_cb, view);

    return toolbar;
}

static void _on_player_completed_cb(void *user_data)
{
    audio_player_view *view = user_data;
    if(view)
    {
        _player_stop(view);
    }
}

static void _on_toolbar_play_pause_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info)
{
    audio_player_view *view = data;
    if(view)
    {
        if(_player_is_playing(view))
        {
            _player_pause(view);
        }
        else
        {
            _player_start(view);
        }
    }
}

static void _on_toolbar_stop_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info)
{
    audio_player_view *view = data;
    if(view)
    {
        _player_stop(view);
    }
}

static void _on_resume_cb(void *user_data)
{
    audio_player_view *view = user_data;
    if(view)
    {
        _player_resume(view);
    }
}

static void _on_pause_cb(void *user_data)
{
    audio_player_view *view = user_data;
    if(view)
    {
        _player_pause(view);
    }
}

static void _on_main_layout_del_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
    audio_player_view *view = data;
    if(view)
    {
        _destroy(view);
    }
}

static Eina_Bool _on_timer_upd_cb(void *data)
{
    audio_player_view *view = data;
    if(view)
    {
        _update_left_label(view);
        _update_progress_bar(view);
    }

    return EINA_TRUE;
}
