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

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

#define STR_STOP "Stop"
#define STR_PLAY "Play"
#define STR_TITLE "Audio equalizer"
#define STR_PRESET "Preset"
#define STR_NONE "None"
#define STR_USER_DEF "User defined"

struct _equalizer_view
{
    Elm_Object_Item *frame_item;
    Evas_Object *navi;
    Evas_Object *layout;
    app_data *app;
    Evas_Object *radio_group;
    Evas_Object **sliders; // slider list
    int bands_count;

    player_h player; // player handle


    Elm_Object_Item *play_item;
    Elm_Object_Item *stop_item;
};

#define PRESET_SIZE 7
static const int _preset_data_0[PRESET_SIZE] = {  0,  0,  0,  0,  0,  0,   0};
static const int _preset_data_1[PRESET_SIZE] = { 12,  6,  0, -3,  0,  6,  12};
static const int _preset_data_2[PRESET_SIZE] = {-12, -6,  0,  3,  0, -6, -12};
static const int _preset_data_3[PRESET_SIZE] = {-12, -6, -3,  0,  3,  6,  12};
static const int _preset_data_4[PRESET_SIZE] = { 12,  6,  3,  0, -3, -6, -12};

#define BAND_INDEX_KEY "band"
#define RADIO_POINTER_KEY "rd"

#define HZ_LABEL_LEN 64
#define HZ_LABEL_WIDTH 130

static void _create_player(equalizer_view *view);
static void _destroy_player(equalizer_view *view);
static bool _set_equalizer_band_level(equalizer_view *view, int index, int level);
static bool _start_player(equalizer_view *view);
static bool _stop_player(equalizer_view *view);

static void _push(equalizer_view *view);
static void _register_app_cbs(equalizer_view *view);
static Evas_Object *_add_main_layout(equalizer_view *view, Evas_Object *parent);
static Evas_Object *_add_toolbar(equalizer_view *view, Evas_Object *parent);
static Evas_Object *_add_label(Evas_Object *parent, const char *text);
static Evas_Object *_add_slider(equalizer_view *view, Evas_Object *parent, int band_index, int min, int max);
static Evas_Object *_add_band_item(equalizer_view *view, Evas_Object *parent, int index, int hz, int min, int max);
static Evas_Object *_add_radio_panel(equalizer_view *view, Evas_Object *parent);
static Evas_Object *_add_radio_with_lable(equalizer_view *view, Evas_Object *parent, const char *lable, const int *preset, int radio_val);
static void _set_equalizer_layout(equalizer_view *view, Evas_Object *parent);
static void _set_radio_layout(equalizer_view *view, Evas_Object *parent);
static void _apply_preset(equalizer_view *view, const int *preset);
static void _disable_btn_toolbar(equalizer_view *view, bool play, bool stop);

static void _on_radio_changed_cb(void *data, Evas_Object *obj, void *event_info);
static void _on_slider_change_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_toolbar_play_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_pause_cb(void *user_data);

equalizer_view *equalizer_view_add(app_data *app, Evas_Object *navi)
{
    equalizer_view *view = calloc(1, sizeof(equalizer_view));

    if(view)
    {
        _create_player(view);

        view->navi = navi;
        view->app = app;
        view->layout = _add_main_layout(view, navi);

        if(view->layout)
        {
            _register_app_cbs(view);
            evas_object_event_callback_add(view->layout, EVAS_CALLBACK_DEL, _on_main_layout_del_cb, view);
            _push(view);
        }
        else
        {
            _destroy_player(view);
            free(view);
            return NULL;
        }
    }

    return view;
}

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

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

static void _create_player(equalizer_view *view)
{
    if(player_create(&view->player) == PLAYER_ERROR_NONE)
    {
        player_set_uri(view->player, get_resource_path(SAMPLEAAC));
        player_set_sound_type(view->player, SOUND_TYPE_MEDIA);
        player_set_volume(view->player, 1.0, 1.0);
        player_set_looping(view->player, true);
        player_prepare(view->player);
    }
}

static void _destroy_player(equalizer_view *view)
{
    _stop_player(view);
    player_unprepare(view->player);
    player_destroy(view->player);
}

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

    if(state != PLAYER_STATE_READY)
    {
        player_prepare(view->player);
    }

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

    player_get_state(view->player, &state);

    return state == PLAYER_STATE_PLAYING;
}

static bool _stop_player(equalizer_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);
    }

    player_get_state(view->player, &state);

    return state != PLAYER_STATE_PLAYING && state != PLAYER_STATE_PAUSED;
}

static bool _set_equalizer_band_level(equalizer_view *view, int index, int level)
{
    bool res = false;
    player_state_e state;
    player_get_state(view->player, &state);

    if(state != PLAYER_STATE_NONE)
    {
        int err = player_audio_effect_set_equalizer_band_level(view->player, index, level);
        res = err == PLAYER_ERROR_NONE;
    }

    return res;
}

static void _apply_preset(equalizer_view *view, const int *preset)
{
    if(preset)
    {
        int count = MIN(view->bands_count, PRESET_SIZE);

        int i;
        for(i = 0; i < count; ++i)
        {
            int level = preset[i];
            Evas_Object *slider = view->sliders[i];
            if(_set_equalizer_band_level(view, i, level))
            {
                elm_slider_value_set(slider, level);
                elm_object_disabled_set(slider, EINA_TRUE);
            }
        }
    }
    else
    {
        int i;
        for(i = 0; i < view->bands_count; ++i)
        {
            elm_object_disabled_set(view->sliders[i], EINA_FALSE);
        }
    }
}

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

    Evas_Object *toolbar = _add_toolbar(view, view->navi);
    elm_object_item_part_content_set(item, "toolbar", toolbar);

    view->frame_item = item;
}

static Evas_Object *_add_main_layout(equalizer_view *view, Evas_Object *parent)
{
    Evas_Object *layout = elm_layout_add(parent);
    view->layout = layout;

    elm_layout_file_set(layout, EQUALIZEREDJ, "equalizer");

    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 *label = _add_label(layout, "-12db");
    elm_object_part_content_set(layout, "left_text", label);

    label = _add_label(layout, "0");
    elm_object_part_content_set(layout, "center_text", label);

    label = _add_label(layout, "+12db");
    elm_object_part_content_set(layout, "right_text", label);

    _set_equalizer_layout(view, layout);
    _set_radio_layout(view, layout);

    return layout;
}

static Evas_Object *_add_band_item(equalizer_view *view, Evas_Object *parent, int index, int hz, int min, int max)
{
    Evas_Object *box = elm_box_add(parent);

    elm_box_horizontal_set(box, true);
    evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
    evas_object_size_hint_align_set(box, EVAS_HINT_FILL, 0.5);
    evas_object_show(box);

    char str[HZ_LABEL_LEN] = {'\0'};

    snprintf(str, HZ_LABEL_LEN, "<p align=center>%uHz</p>", hz);

    Evas_Object *lable = _add_label(box, str);
    evas_object_size_hint_min_set(lable, HZ_LABEL_WIDTH, -1);
    evas_object_size_hint_max_set(lable, HZ_LABEL_WIDTH, -1);

    Evas_Object *slider = _add_slider(view, box, index, min, max);
    view->sliders[index] = slider;

    elm_box_pack_start(box, lable);
    elm_box_pack_end(box, slider);

    return box;
}

static void _set_equalizer_layout(equalizer_view *view, Evas_Object *parent)
{
    Evas_Object *box = elm_box_add(parent);
    elm_box_horizontal_set(box, EINA_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);

    int bands_count = 0;
    player_audio_effect_get_equalizer_bands_count(view->player, &bands_count);

    view->bands_count = bands_count;
    view->sliders = calloc(bands_count, sizeof(Evas_Object*));

    int i;
    for(i = 0; i < bands_count; ++i)
    {
        int frequency = 0;
        int min = 0;
        int max = 0;

        player_audio_effect_get_equalizer_band_frequency(view->player, i, &frequency);
        player_audio_effect_get_equalizer_level_range(view->player, &min, &max);

        Evas_Object *item = _add_band_item(view, box, i, frequency, min, max);
        elm_box_pack_end(box, item);
    }

    elm_object_part_content_set(parent, "band_content", box);
}

static void _set_radio_layout(equalizer_view *view, Evas_Object *parent)
{
    Evas_Object *radio_panel = _add_radio_panel(view, parent);
    elm_object_part_content_set(parent, "rd_content", radio_panel);
}

static Evas_Object *_add_toolbar(equalizer_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_shrink_mode_set(toolbar, ELM_TOOLBAR_SHRINK_EXPAND);
    elm_toolbar_transverse_expanded_set(toolbar, EINA_TRUE);
    elm_toolbar_select_mode_set(toolbar, ELM_OBJECT_SELECT_MODE_NONE);

    view->play_item = elm_toolbar_item_append(toolbar, NULL, STR_PLAY, _on_toolbar_play_btn_pressed_cb, view);
    view->stop_item = elm_toolbar_item_append(toolbar, NULL, STR_STOP, _on_toolbar_stop_btn_pressed_cb, view);

    _disable_btn_toolbar(view, false, true);

    return toolbar;
}

static void _disable_btn_toolbar(equalizer_view *view, bool play, bool stop)
{
    elm_object_item_disabled_set(view->play_item, play);
    elm_object_item_disabled_set(view->stop_item, stop);
}

static Evas_Object *_add_slider(equalizer_view *view, Evas_Object *parent, int band_index, int min, int max)
{
    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_min_max_set(slider, min, max);
    elm_slider_value_set(slider, 0);
    elm_slider_indicator_show_set(slider, EINA_TRUE);
    evas_object_smart_callback_add(slider, "changed", _on_slider_change_cb, view);
    evas_object_data_set(slider, BAND_INDEX_KEY, (void*)band_index);

    return slider;
}

static Evas_Object *_add_radio_with_lable(equalizer_view *view, Evas_Object *parent,
                                          const char *lable, const int *preset, int radio_val)
{
    Evas_Object *box = elm_box_add(parent);
    evas_object_show(box);
    evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, 1.0);
    evas_object_size_hint_align_set(box, 0.0, 0.5);

    elm_box_horizontal_set(box, EINA_TRUE);

    Evas_Object *rd = elm_radio_add(box);
    evas_object_data_set(rd, RADIO_POINTER_KEY, preset);
    evas_object_show(rd);
    elm_radio_state_value_set(rd, radio_val);

    evas_object_smart_callback_add(rd, "changed", _on_radio_changed_cb, view);

    Evas_Object *lab = _add_label(box, lable);
    evas_object_show(lab);

    if(view->radio_group)
    {
        elm_radio_group_add(rd, view->radio_group);
    }
    else
    {
        view->radio_group = rd;
        elm_radio_value_set(rd, radio_val);
    }

    elm_box_pack_start(box, rd);
    elm_box_pack_end(box, lab);

    return box;
}

static Evas_Object *_add_radio_panel(equalizer_view *view, Evas_Object *parent)
{
    Evas_Object *box = elm_box_add(parent);
    elm_box_horizontal_set(box, EINA_TRUE);
    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);

    Evas_Object *left_box = elm_box_add(box);
    evas_object_size_hint_weight_set(left_box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
    evas_object_size_hint_align_set(left_box, EVAS_HINT_FILL, EVAS_HINT_FILL);
    elm_box_horizontal_set(left_box, EINA_FALSE);
    evas_object_show(left_box);

    Evas_Object *right_box = elm_box_add(box);
    evas_object_size_hint_weight_set(right_box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
    evas_object_size_hint_align_set(right_box, EVAS_HINT_FILL, EVAS_HINT_FILL);
    elm_box_horizontal_set(right_box, EINA_FALSE);
    evas_object_show(right_box);

    // Left box:
    Evas_Object *item_ud = _add_radio_with_lable(view, left_box, STR_USER_DEF, NULL, 0);
    Evas_Object *item1 = _add_radio_with_lable(view, left_box, STR_PRESET"1", _preset_data_1, 1);
    Evas_Object *item3 = _add_radio_with_lable(view, left_box, STR_PRESET"3", _preset_data_3, 3);

    elm_box_pack_end(left_box, item1);
    elm_box_pack_end(left_box, item3);
    elm_box_pack_end(left_box, item_ud);

    // Right box:
    Evas_Object *item2 = _add_radio_with_lable(view, right_box, STR_PRESET"2", _preset_data_2, 2);
    Evas_Object *item4 = _add_radio_with_lable(view, right_box, STR_PRESET"4", _preset_data_4, 4);
    Evas_Object *item_none = _add_radio_with_lable(view, right_box, STR_NONE, _preset_data_0, 5);

    elm_box_pack_end(right_box, item2);
    elm_box_pack_end(right_box, item4);
    elm_box_pack_end(right_box, item_none);

    elm_box_pack_start(box, left_box);
    elm_box_pack_end(box, right_box);

    return box;
}

static Evas_Object *_add_label(Evas_Object *parent, const char *text)
{
    Evas_Object *obj = elm_label_add(parent);

    evas_object_size_hint_weight_set(obj, 0.0, EVAS_HINT_EXPAND);
    evas_object_size_hint_align_set(obj, 0.0, EVAS_HINT_FILL);
    evas_object_show(obj);

    elm_object_text_set(obj, text);

    return obj;
}

static void _on_toolbar_play_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info)
{
    equalizer_view *view = data;

    if(view)
    {
        if(_start_player(view))
        {
            _disable_btn_toolbar(view, true, false);
        }
    }
}

static void _on_toolbar_stop_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info)
{
    equalizer_view *view = data;

    if(view)
    {
        if(_stop_player(view))
        {
            _disable_btn_toolbar(view, false, true);
        }
    }
}

static void _on_slider_change_cb(void *data, Evas_Object *obj, void *event_info)
{
    equalizer_view *view = data;

    if(view)
    {
        int level = (int)elm_slider_value_get(obj);
        int index = (int)evas_object_data_get(obj, BAND_INDEX_KEY);
        _set_equalizer_band_level(view, index, level);
    }
}

static void _on_radio_changed_cb(void *data, Evas_Object *obj, void *event_info)
{
    equalizer_view *view = data;

    if(view)
    {
        Evas_Object *selected_obj = elm_radio_selected_object_get(obj);
        const int *preset = (const int *) evas_object_data_get(selected_obj, RADIO_POINTER_KEY);

        _apply_preset(view, preset);
    }
}

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

    if(view && _stop_player(view))
    {
        _disable_btn_toolbar(view, false, true);
    }
}

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

    if(view)
    {
        app_reset_cbs(view->app);
        _destroy_player(view);
        free(view->sliders);
        free(view);
    }
}
