/*
 * This file is part of vimix - video live mixer
 *
 * **Copyright** (C) 2019-2023 Bruno Herbelin <bruno.herbelin@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
**/

// ImGui
#include "Toolkit/ImGuiToolkit.h"
#include "imgui_internal.h"

#include "defines.h"
#include "Settings.h"
#include "Toolkit/GstToolkit.h"
#include "Metronome.h"

#include "TimerMetronomeWindow.h"



TimerMetronomeWindow::TimerMetronomeWindow() : WorkspaceWindow("Timer")
{
    // timer modes : 0 Metronome, 1 Stopwatch
    timer_menu = { "Metronome", "Stopwatch" };
    // clock times
    start_time_      = gst_util_get_timestamp ();
    start_time_hand_ = gst_util_get_timestamp ();
    duration_hand_   = Settings::application.timer.stopwatch_duration *  GST_SECOND;
}


void TimerMetronomeWindow::setVisible(bool on)
{
    // restore workspace to show the window
    if (WorkspaceWindow::clear_workspace_enabled) {
        WorkspaceWindow::restoreWorkspace(on);
        // do not change status if ask to hide (consider user asked to toggle because the window was not visible)
        if (!on)  return;
    }

    if (Settings::application.widget.timer_view > 0 && Settings::application.widget.timer_view != Settings::application.current_view){
        Settings::application.widget.timer_view = -1;
        on = true;
    }

    Settings::application.widget.timer = on;
}

bool TimerMetronomeWindow::Visible() const
{
    return ( Settings::application.widget.timer
             && (Settings::application.widget.timer_view < 0 || Settings::application.widget.timer_view == Settings::application.current_view )
             );
}

void TimerMetronomeWindow::Render()
{
    // constraint square resizing
    static ImVec2 timer_window_size_min = ImVec2(11.f * ImGui::GetTextLineHeight(), 11.f * ImGui::GetTextLineHeight());
    ImGui::SetNextWindowSizeConstraints(timer_window_size_min, timer_window_size_min * 1.5f, ImGuiToolkit::CustomConstraints::Square);
    ImGui::SetNextWindowPos(ImVec2(600, 20), ImGuiCond_FirstUseEver);
    ImGui::SetNextWindowSize(timer_window_size_min, ImGuiCond_FirstUseEver);

    if ( !ImGui::Begin(name_, &Settings::application.widget.timer,
                       ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoTitleBar |  ImGuiWindowFlags_NoCollapse ))
    {
        ImGui::End();
        return;
    }

    // menu (no title bar)
    if (ImGui::BeginMenuBar())
    {
        // Close and widget menu
        if (ImGuiToolkit::IconButton(4,16))
            Settings::application.widget.timer = false;
        if (ImGui::BeginMenu(IMGUI_TITLE_TIMER))
        {
            // Enable/Disable Ableton Link
            if ( ImGui::MenuItem( ICON_FA_USER_CLOCK " Ableton Link", nullptr, &Settings::application.timer.link_enabled) ) {
                Metronome::manager().setEnabled(Settings::application.timer.link_enabled);
            }

            // output manager menu
            ImGui::Separator();
            bool pinned = Settings::application.widget.timer_view == Settings::application.current_view;
            std::string menutext = std::string( ICON_FA_MAP_PIN "    Stick to ") + Settings::application.views[Settings::application.current_view].name + " view";
            if ( ImGui::MenuItem( menutext.c_str(), nullptr, &pinned) ){
                if (pinned)
                    Settings::application.widget.timer_view = Settings::application.current_view;
                else
                    Settings::application.widget.timer_view = -1;
            }
            if ( ImGui::MenuItem( MENU_CLOSE, SHORTCUT_TIMER) )
                Settings::application.widget.timer = false;

            ImGui::EndMenu();
        }
        // Selection of the timer mode
        if (ImGui::BeginMenu( timer_menu[Settings::application.timer.mode].c_str() ))
        {
            for (size_t i = 0; i < timer_menu.size(); ++i) {
                if (ImGui::MenuItem(timer_menu[i].c_str(), NULL, Settings::application.timer.mode==i))
                    Settings::application.timer.mode = i;
            }
            ImGui::EndMenu();
        }

        ImGui::EndMenuBar();
    }

    // current windowdraw parameters
    ImGuiWindow* window = ImGui::GetCurrentWindow();
    ImDrawList* draw_list = window->DrawList;
    // positions and size of GUI elements
    const float margin = window->MenuBarHeight();
    const float h = 0.4f * ImGui::GetFrameHeight();
    const ImVec2 circle_top_left = window->Pos + ImVec2(margin + h, margin + h);
    const ImVec2 circle_top_right = window->Pos + ImVec2(window->Size.y - margin - h, margin + h);
    const ImVec2 circle_botom_right = window->Pos + ImVec2(window->Size.y - margin - h, window->Size.x - margin - h);
    const ImVec2 circle_center = window->Pos + (window->Size + ImVec2(margin, margin) )/ 2.f;
    const float circle_radius = (window->Size.y - 2.f * margin) / 2.f;

    // color palette
    static ImU32 colorbg = ImGui::GetColorU32(ImGuiCol_FrameBgActive, 0.6f);
    static ImU32 colorfg = ImGui::GetColorU32(ImGuiCol_FrameBg, 2.5f);
    static ImU32 colorline = ImGui::GetColorU32(ImGuiCol_PlotHistogram);

    //
    // METRONOME
    //
    if (Settings::application.timer.mode < 1) {

        // Metronome info
        double t = Metronome::manager().tempo();
        double p = Metronome::manager().phase();
        double q = Metronome::manager().quantum();
        uint np  = (int) Metronome::manager().peers();

        // draw background ring
        draw_list->AddCircleFilled(circle_center, circle_radius, colorbg, PLOT_CIRCLE_SEGMENTS);

        // draw quarter
        static const float resolution = PLOT_CIRCLE_SEGMENTS / (2.f * M_PI);
        static ImVec2 buffer[PLOT_CIRCLE_SEGMENTS];
        float a0 = -M_PI_2 + (floor(p)/floor(q)) * (2.f * M_PI);
        float a1 = a0 + (1.f / floor(q)) * (2.f * M_PI);
        int n = ImMax(3, (int)((a1 - a0) * resolution));
        double da = (a1 - a0) / (n - 1);
        int index = 0;
        buffer[index++] = circle_center;
        for (int i = 0; i < n; ++i) {
            double a = a0 + i * da;
            buffer[index++] = ImVec2(circle_center.x + circle_radius * cos(a), circle_center.y + circle_radius * sin(a));
        }
        draw_list->AddConvexPolyFilled(buffer, index, colorfg);

        // draw clock hand
        a0 = -M_PI_2 + (p/q) * (2.f * M_PI);
        draw_list->AddLine(ImVec2(circle_center.x + margin * cos(a0), circle_center.y + margin * sin(a0)),
                           ImVec2(circle_center.x + circle_radius * cos(a0), circle_center.y + circle_radius * sin(a0)), colorline, 2.f);

        // centered indicator 'x / N'
        draw_list->AddCircleFilled(circle_center, margin, colorfg, PLOT_CIRCLE_SEGMENTS);
        ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
        char text_buf[24];
        snprintf(text_buf, 24, "%d/%d", (int)(p)+1, (int)(q) );
        ImVec2 label_size = ImGui::CalcTextSize(text_buf, NULL);
        ImGui::SetCursorScreenPos(circle_center - label_size/2);
        ImGui::Text("%s", text_buf);
        ImGui::PopFont();

        // left slider : quantum
        float float_value = ceil(Metronome::manager().quantum());
        ImGui::SetCursorPos(ImVec2(0.5f * margin, 1.5f * margin));
        if ( ImGui::VSliderFloat("##quantum", ImVec2(0.5f * margin, 2.f * circle_radius ), &float_value, MIN_BEAT, MAX_BEAT, "", 2.f) ){
            Metronome::manager().setQuantum( ceil(float_value) );
        }
        if (ImGui::IsItemHovered() || ImGui::IsItemActive() )  {
            ImGui::BeginTooltip();
            guint64 time_phase = GST_SECOND * (60.0 * q / t) ;
            ImGui::Text("%d beats per phase\n= %s at %d BPM", (int) ceil(float_value),
                        GstToolkit::time_to_string(time_phase, GstToolkit::TIME_STRING_READABLE).c_str(), (int) t);
            ImGui::EndTooltip();
        }

        // Controls NOT operational if peer connected
        if (np >0 ) {
            // Tempo
            ImGui::SetCursorScreenPos(circle_top_right);
            ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD);
            ImGui::PushStyleColor(ImGuiCol_Text, colorfg);
            snprintf(text_buf, 24, "%d", (int) ceil(t) );
            ImGui::Text("%s", text_buf);
            ImGui::PopStyleColor();
            ImGui::PopFont();
            if (ImGui::IsItemHovered()){
                snprintf(text_buf, 24, "%d BPM\n(set by peer)", (int) ceil(t));
                ImGuiToolkit::ToolTip(text_buf);
            }
        }
        // Controls operational only if no peer
        else {
            // Tempo
            ImGui::SetCursorScreenPos(circle_top_right);
            ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD);
            snprintf(text_buf, 24, "%d", (int) ceil(t) );
            ImGui::Text("%s", text_buf);
            ImGui::PopFont();
            if (ImGui::IsItemClicked())
                ImGui::OpenPopup("bpm_popup");
            else if (ImGui::IsItemHovered()){
                snprintf(text_buf, 24, "%d BPM\n(clic to edit)", (int) ceil(t));
                ImGuiToolkit::ToolTip(text_buf);
            }
            if (ImGui::BeginPopup("bpm_popup", ImGuiWindowFlags_NoMove))
            {
                ImGui::SetNextItemWidth(80);
                ImGui::InputText("BPM", text_buf, 8, ImGuiInputTextFlags_CharsDecimal);
                if (ImGui::IsItemDeactivatedAfterEdit()) {
                    int t = 0;
                    sscanf(text_buf, "%d", &t);
                    t = CLAMP(t, 20, 2000);
                    Metronome::manager().setTempo((double) t);
                    ImGui::CloseCurrentPopup();
                }

                ImGui::EndPopup();
            }
            // restart icon
            ImGui::SetCursorScreenPos(circle_top_left);
            if (ImGuiToolkit::IconButton(9, 13)) {
                Metronome::manager().restart();
            }
        }

        // Network indicator, if link enabled
        if (Settings::application.timer.link_enabled) {
            ImGui::SetCursorScreenPos(circle_botom_right);
            ImGuiToolkit::Icon(16, 5, np > 0);
            if (ImGui::IsItemHovered()){
                snprintf(text_buf, 24, np < 1 ? "Ableton Link\nNo peer" : "Ableton Link\n%d peer%c", np, np < 2 ? ' ' : 's' );
                ImGuiToolkit::ToolTip(text_buf);
            }
        }

    }
    //
    // STOPWATCH
    //
    else {
        guint64 time_ = gst_util_get_timestamp ();

        // draw ring
        draw_list->AddCircle(circle_center, circle_radius, colorbg, PLOT_CIRCLE_SEGMENTS, 12 );
        draw_list->AddCircleFilled(ImVec2(circle_center.x, circle_center.y - circle_radius), 7, colorfg, PLOT_CIRCLE_SEGMENTS);
        // draw indicator time hand
        double da = -M_PI_2 + ( (double) (time_-start_time_hand_) / (double) duration_hand_) * (2.f * M_PI);
        draw_list->AddCircleFilled(ImVec2(circle_center.x + circle_radius * cos(da), circle_center.y + circle_radius * sin(da)), 7, colorline, PLOT_CIRCLE_SEGMENTS);

        // left slider : countdown
        float float_value = (float) Settings::application.timer.stopwatch_duration;
        ImGui::SetCursorPos(ImVec2(0.5f * margin, 1.5f * margin));
        if ( ImGui::VSliderFloat("##duration", ImVec2(0.5f * margin, 2.f * circle_radius ), &float_value, 1, 3600, "", 3.f) ){
            Settings::application.timer.stopwatch_duration = (uint64_t) float_value;
            duration_hand_ = Settings::application.timer.stopwatch_duration * GST_SECOND;
        }
        if (ImGui::IsItemHovered() || ImGui::IsItemActive())  {
            ImGui::BeginTooltip();
            ImGui::Text("%s\ncountdown", GstToolkit::time_to_string(duration_hand_, GstToolkit::TIME_STRING_READABLE).c_str() );
            ImGui::EndTooltip();
        }

        // main text: elapsed time
        ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
        char text_buf[24];
        snprintf(text_buf, 24, "%s", GstToolkit::time_to_string(time_-start_time_, GstToolkit::TIME_STRING_FIXED).c_str() );
        ImVec2 label_size = ImGui::CalcTextSize(text_buf, NULL);
        ImGui::SetCursorScreenPos(circle_center - label_size/2);
        ImGui::Text("%s", text_buf);
        ImGui::PopFont();

        // small text: remaining time
        ImGui::PushStyleColor(ImGuiCol_Text, colorfg);
        ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD);

        guint64 lap = (time_-start_time_hand_)/duration_hand_;
        snprintf(text_buf, 24, "%lu turn%s", (unsigned long) lap, lap > 1 ? "s" : " " );
        label_size = ImGui::CalcTextSize(text_buf, NULL);
        ImGui::SetCursorScreenPos(circle_center + ImVec2(0.f, circle_radius * -0.7f) - label_size/2);
        ImGui::Text("%s", text_buf);

        snprintf(text_buf, 24, "%s", GstToolkit::time_to_string(duration_hand_-(time_-start_time_hand_)%duration_hand_, GstToolkit::TIME_STRING_READABLE).c_str() );
        label_size = ImGui::CalcTextSize(text_buf, NULL);
        ImGui::SetCursorScreenPos(circle_center - ImVec2(0.f, circle_radius * -0.7f) - label_size/2);
        ImGui::Text("%s", text_buf);
        ImGui::PopFont();
        ImGui::PopStyleColor();

        // reset icon
        ImGui::SetCursorScreenPos(circle_top_left);
        if (ImGuiToolkit::IconButton(8, 13))
            start_time_ = start_time_hand_ = time_; // reset timers

        // TODO : pause ?
    }

    ImGui::End();
}
