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

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

#define PLAYERS_COUNT 4
#define GENLIST_THIS_KEY "this"
#define CHECKBOX_PART "elm.icon.left"
#define MAINTEXT_PART "elm.text.main.left.top"
#define SUBTEXT_PART "elm.text.sub.left.bottom"

#define STR_TITLE "Multi play"
#define STR_READY "Ready"
#define STR_PLAYING "Playing"
#define STR_PAUSED "Paused"
#define STR_RESET "Reset"
#define STR_RESUME "Resume"

typedef struct
{
    const char *file_name;
    const char *player_name;
} player_in_data;

static player_in_data _player_in_data_list[PLAYERS_COUNT] =
{
    {SAMPLEMP3, "SampleMP3.mp3"},
    {SAMPLEAMR, "SampleAMR.amr"},
    {SAMPLEAAC, "SampleAAC.aac"},
    {SAMPLEWAV, "SampleWAV.wav"}
};

typedef struct
{
    player_h handle;
    const char * name;
} player_data;

struct _multi_play_view
{
    app_data *app;
    Elm_Object_Item *frame_item;
    Evas_Object *navi;
    Evas_Object *genlist;
    Evas_Object *toolbar;
    Elm_Genlist_Item_Class *genlist_item_class;

    player_data *players[PLAYERS_COUNT];

    Elm_Object_Item *reset_item;
    Elm_Object_Item *resume_item;
};

typedef void (*player_func)(player_data *player);

static void _destroy(multi_play_view *view);

static void _player_foreach(player_func player_fn, player_data *players[]);
static player_data *_player_create(const char *file_name, const char * player_name);
static void _player_destroy(player_data *player);
static void _player_start(player_data *player);
static void _player_stop(player_data *player);
static void _player_pause(player_data *player);
static void _player_resume(player_data *player);
static const char *_player_get_status(player_data *player);
static bool _players_is_playing(player_data *players[]);

static void _push(multi_play_view *view);
static void _register_app_cbs(multi_play_view *view);
static Evas_Object *_main_layout_add(multi_play_view *view, Evas_Object *parent);
static Evas_Object *_toolbar_add(multi_play_view *view, Evas_Object *parent);
static void _toolbar_resume_btn_show(multi_play_view *view, bool show);

static void _genlist_update(Evas_Object *obj);
static Evas_Object *_genlist_add(multi_play_view *view, Evas_Object *parent);
static Elm_Genlist_Item_Class *_genlist_item_class_add();
static void _on_genlist_item_selected_cb(void *data, Evas_Object *obj, void *event_info);
static char *_genlist_text_get(void *data, Evas_Object *obj, const char *part);
static Evas_Object *_genlist_content_get(void *data, Evas_Object *obj, const char *part);
static void _genlist_fill(multi_play_view *view);
static void _genlist_append_item(multi_play_view *view, player_data *player);

static void _on_toolbar_reset_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info);
static void _on_toolbar_resume_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);

multi_play_view *multi_play_view_add(app_data *app, Evas_Object *navi)
{
    multi_play_view *view = calloc(1, sizeof(multi_play_view));

    if(view)
    {
        int i;
        for(i = 0; i < PLAYERS_COUNT; ++i)
        {
            view->players[i] = _player_create(get_resource_path(_player_in_data_list[i].file_name), _player_in_data_list[i].player_name);
        }

        view->navi = navi;
        view->app = app;
        view->genlist = _main_layout_add(view, navi);

        if(view->genlist)
        {
            _register_app_cbs(view);
            evas_object_event_callback_add(view->genlist, EVAS_CALLBACK_DEL, _on_main_layout_del_cb, view);
            _push(view);
        }
        else
        {
            _destroy(view);
            return NULL;
        }
    }

    return view;
}

static void _destroy(multi_play_view *view)
{
    app_reset_cbs(view->app);
    _player_foreach(_player_destroy, view->players);
    elm_genlist_item_class_free(view->genlist_item_class);
    free(view);
}

static Evas_Object *_genlist_add(multi_play_view *view, Evas_Object *parent)
{
    Evas_Object *genlist = elm_genlist_add(parent);
    elm_genlist_select_mode_set(genlist, ELM_OBJECT_SELECT_MODE_DEFAULT);
    evas_object_size_hint_weight_set(genlist, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
    evas_object_size_hint_align_set(genlist, EVAS_HINT_FILL, EVAS_HINT_FILL);
    evas_object_show(genlist);
    evas_object_data_set(genlist, GENLIST_THIS_KEY, view);
    return genlist;
}

static Elm_Genlist_Item_Class *_genlist_item_class_add()
{
    Elm_Genlist_Item_Class *item_class = elm_genlist_item_class_new();

    item_class->item_style = "2line.top";
    item_class->func.text_get = _genlist_text_get;
    item_class->func.content_get = _genlist_content_get;
    item_class->func.state_get = NULL;
    item_class->func.del = NULL;
    item_class->decorate_all_item_style = NULL;

    return item_class;
}

static char *_genlist_text_get(void *data, Evas_Object *obj, const char *part)
{
    player_data *player= data;
    multi_play_view *view = evas_object_data_get(obj, GENLIST_THIS_KEY);

    if(player)
    {
        if(view && strcmp(part, MAINTEXT_PART) == 0)
        {
            return strdup(player->name);
        }

        if(strcmp(part, SUBTEXT_PART) == 0)
        {
            return strdup(_player_get_status(player));
        }
    }

    return NULL;
}

static Evas_Object *_genlist_content_get(void *data, Evas_Object *obj, const char *part)
{
    player_data *player= data;
    Evas_Object *checkbox = NULL;

    if(strcmp(CHECKBOX_PART, part) == 0)
    {
        bool check = false;
        if(player)
        {
            player_state_e state;
            player_get_state(player->handle, &state);
            check = state == PLAYER_STATE_PLAYING || state == PLAYER_STATE_PAUSED;
        }

        checkbox = elm_check_add(obj);
        elm_check_state_set(checkbox, check);
        evas_object_freeze_events_set(checkbox, EINA_TRUE);
        evas_object_repeat_events_set(checkbox, EINA_TRUE);
    }

    return checkbox;
}

static void _on_genlist_item_selected_cb(void *data, Evas_Object *obj, void *event_info)
{
    Elm_Object_Item *item = (Elm_Object_Item *) event_info;
    player_data *player = elm_object_item_data_get(item);
    Evas_Object *checkbox = elm_object_item_part_content_get(item, CHECKBOX_PART);

    elm_genlist_item_selected_set(item, EINA_FALSE);

    assert(checkbox);
    assert(player);
    if(checkbox && player)
    {
        bool state = elm_check_state_get(checkbox);
        elm_check_state_set(checkbox, !state);
        state = elm_check_state_get(checkbox);

        if(state)
        {
            _player_start(player);
        }
        else
        {
            _player_stop(player);
        }
        elm_genlist_item_fields_update(item, SUBTEXT_PART, ELM_GENLIST_ITEM_FIELD_TEXT);
    }
}

static void _genlist_update(Evas_Object *obj)
{
    Elm_Object_Item *item = elm_genlist_first_item_get(obj);

    while(item)
    {
        elm_genlist_item_fields_update(item, SUBTEXT_PART, ELM_GENLIST_ITEM_FIELD_TEXT);
        elm_genlist_item_fields_update(item, CHECKBOX_PART, ELM_GENLIST_ITEM_FIELD_CONTENT);
        item = elm_genlist_item_next_get(item);
    }
}

static void _genlist_append_item(multi_play_view *view, player_data *player)
{
    elm_genlist_item_append(view->genlist, view->genlist_item_class, player, NULL, ELM_GENLIST_ITEM_NONE,
                            _on_genlist_item_selected_cb, view);
}

static void _genlist_fill(multi_play_view *view)
{
    int i;
    for(i = 0; i < PLAYERS_COUNT; ++i)
    {
        _genlist_append_item(view, view->players[i]);
    }
}

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

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

static void _player_foreach(player_func player_fn, player_data *players[])
{
    if(player_fn)
    {
        int i;
        for(i = 0; i < PLAYERS_COUNT; ++i)
        {
            player_fn(players[i]);
        }
    }
}

static player_data *_player_create(const char *file_name, const char * player_name)
{
    player_h player;

    player_data *pd = calloc(1, sizeof(player_data));

    pd->name = player_name;
    if(player_create(&player) == PLAYER_ERROR_NONE)
    {
        pd->handle = player;
        player_set_uri(player, file_name);
        player_set_sound_type(player, SOUND_TYPE_MEDIA);
        player_set_volume(player, 1.0, 1.0);
        player_set_looping(player, true);
        player_prepare(player);
    }

    return pd;
}

static void _player_destroy(player_data *player)
{
    if(player)
    {
        _player_stop(player);
        player_unprepare(player->handle);
        player_destroy(player->handle);
        free(player);
    }
}

static void _player_start(player_data *player)
{
    if(player)
    {
        player_state_e state;
        player_get_state(player->handle, &state);

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

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

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

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

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

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

        if(state == PLAYER_STATE_PAUSED)
        {
            player_start(player->handle);
        }
    }
}

static bool _players_is_playing(player_data *players[])
{
    int i;
    for(i = 0; i < PLAYERS_COUNT; ++i)
    {
        if(players[i])
        {
            player_state_e state;
            player_get_state(players[i]->handle, &state);

            if(state == PLAYER_STATE_PLAYING || state == PLAYER_STATE_PAUSED)
            {
                return true;
            }
        }
    }

    return false;
}

static const char *_player_get_status(player_data *player)
{
    if(player)
    {
        player_state_e state;
        player_get_state(player->handle, &state);

        switch(state)
        {
            case PLAYER_STATE_READY:
                return STR_READY;
                break;

            case PLAYER_STATE_PLAYING:
                return STR_PLAYING;
                break;

            case PLAYER_STATE_PAUSED:
                return STR_PAUSED;
                break;

            default:
                break;
        }
    }

    return "Error";
}

static void _push(multi_play_view *view)
{
    Elm_Object_Item *item = elm_naviframe_item_push(view->navi, "Multi play", NULL, NULL, view->genlist, 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(multi_play_view *view, Evas_Object *parent)
{
    Evas_Object *genlist = _genlist_add(view, parent);
    view->genlist_item_class = _genlist_item_class_add();

    view->genlist = genlist;

    _genlist_fill(view);

    return genlist;
}

static Evas_Object *_toolbar_add(multi_play_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->reset_item = elm_toolbar_item_append(toolbar, NULL, STR_RESET, _on_toolbar_reset_btn_pressed_cb, view);

    return toolbar;
}

static void _toolbar_resume_btn_show(multi_play_view *view, bool show)
{
    if(show)
    {
        if(!view->resume_item)
        {
            view->resume_item = elm_toolbar_item_append(view->toolbar, NULL, STR_RESUME, _on_toolbar_resume_btn_pressed_cb, view);
        }
    }
    else
    {
        if(view->resume_item)
        {
            elm_object_item_del(view->resume_item);
            view->resume_item = NULL;
        }
    }
}

static void _on_toolbar_reset_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info)
{
    multi_play_view *view = data;
    if(view)
    {
        _player_foreach(_player_stop, view->players);
        _genlist_update(view->genlist);
        _toolbar_resume_btn_show(view, false);
    }
}

static void _on_toolbar_resume_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info)
{
    multi_play_view *view = data;
    if(view)
    {
        _player_foreach(_player_resume, view->players);
        _genlist_update(view->genlist);
        _toolbar_resume_btn_show(view, false);
    }
}

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

    if(view)
    {
        _player_foreach(_player_pause, view->players);
        _genlist_update(view->genlist);

        if(_players_is_playing(view->players))
        {
            _toolbar_resume_btn_show(view, true);
        }
    }
}

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

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