#include "main.h"
#include <data.h>
#include <util.hpp>
#include <assets/assets.h>
#include <filesystem>
#include <options.hpp>
#include "imgui/imgui.h"
#include "ui_backend.hpp"
#include "thirdparty/CLI11.hpp"
#include <web_functions.hpp>
using namespace Looper::Options;
void MainLoop::Init() {
    #ifdef PORTALS
    g_set_application_name("Looper");
    #endif
    // Our state
    show_demo_window = false;

    FileBrowser fileDialog(false, ImGuiFileBrowserFlags_NoTitleBar|ImGuiFileBrowserFlags_NoMove|ImGuiFileBrowserFlags_NoResize);
    #ifndef __EMSCRIPTEN__
    fileDialog.SetPwd(path(userdir) / path("Music"));
    #endif
    fileDialog.SetWindowSize(window_width, window_height);
    //fileDialog.SetWindowPos(0, 0);
    position = 0.0;
    prefs_window = false;
    theme_editor = false;
    stopped = true;
    about_window = false;
    string lang;
    {
        Json::Value config;
        std::ifstream stream;
        path jsonConfigPath = path(prefPath) / "config.json";
        stream.open(jsonConfigPath);
        if (stream.is_open()) {
            stream >> config;
            if (config.isMember("theme_name")) {
                init_option<std::string>("ui.imgui.theme", config["theme_name"].asString());
            }
            if (config.isMember("accent_color")) {
                if (config["accent_color"].isNumeric()) {
                    accent_color.x = config["accent_color"].asFloat() / 360.0;
                } else {
                    Json::Value accentColor = config["accent_color"];
                    accent_color = ImVec4(accentColor["h"].asFloat(), accentColor["s"].asFloat(), accentColor["v"].asFloat(), accentColor["a"].asFloat());
                }
                toml::table accent_color_table;
                accent_color_table.insert("h", accent_color.x);
                accent_color_table.insert("s", accent_color.y);
                accent_color_table.insert("v", accent_color.z);
                accent_color_table.insert("a", accent_color.w);
                init_option<toml::table>("ui.imgui.accent_color", accent_color_table);
            }
            if (config.isMember("demo_window")) {
                init_option<bool>("ui.imgui.demo_window", config["demo_window"].asBool());
            }
            if (config.isMember("vsync")) {
                init_option<bool>("ui.imgui.vsync", config["vsync"].asBool());
            }
            if (config.isMember("framerate")) {
                init_option<int64_t>("ui.imgui.framerate", (int64_t)config["framerate"].asUInt());
            }
            if (config.isMember("lang")) {
                Json::Value langValue;
                if (!langValue.isNull()) {
                    init_option<std::string>("ui.imgui.lang", config["lang"].asString());
                }
            }
            stream.close();
            std::remove(jsonConfigPath.c_str());
        }
        {
            std::string themeName = get_option<std::string>("ui.imgui.theme", "light");
            path themePath = Theme::themeDir / path(themeName + ".toml");
            #ifdef __EMSCRIPTEN__
            try {
                auto newTheme = new Theme(themePath);
                delete theme;
                theme = newTheme;
            } catch (std::exception _) {

            }
            #else
            if (exists(themePath)) {
                delete theme;
                theme = new Theme(themePath);
            }
            #endif
            if (option_set("ui.imgui.lang")) {
                lang = get_option<std::string>("ui.imgui.lang");
            } else {
                lang = DEFAULT_LANG;
            }
            SET_LANG(lang.c_str());
            show_demo_window = get_option<bool>("ui.imgui.demo_window", false);
            vsync = get_option<bool>("ui.imgui.vsync", true);
            framerate = (unsigned)get_option<int64_t>("ui.imgui.framerate", 60);
            accent_color.x = (float)get_option<double>("ui.imgui.accent_color.h", accent_color.x);
            accent_color.y = (float)get_option<double>("ui.imgui.accent_color.s", accent_color.y);
            accent_color.z = (float)get_option<double>("ui.imgui.accent_color.v", accent_color.z);
            accent_color.w = (float)get_option<double>("ui.imgui.accent_color.a", accent_color.w);
            debug_mode = get_option<bool>("ui.imgui.debug_mode", false);
        }
        Theme::updateAvailableThemes();
        if (Theme::availableThemes.empty()) {
            path lightPath = Theme::themeDir / "light.toml";
            path darkPath = Theme::themeDir / "dark.toml";
            string builtinDescription = _TRS_CTX("Built-in themes | Theme default strings | name", "(built-in)");
            if (!exists(lightPath)) {
                Theme light(false);
                ThemeStrings &strings = light.strings["fallback"];
                strings.name = _TRS_CTX("Built-in light theme | Theme default strings | name", "Default light");
                strings.description = builtinDescription;
                light.strings[CURRENT_LANGUAGE] = strings;
                light.Save(lightPath);
            }
            if (!exists(darkPath)) {
                Theme dark(true);
                ThemeStrings &strings = dark.strings["fallback"];
                strings.name = _TRS_CTX("Built-in dark theme | Theme default strings | name", "Default dark");
                strings.description = builtinDescription;
                dark.strings[CURRENT_LANGUAGE] = strings;
                dark.Save(darkPath);
            }
            delete theme;
            theme = new Theme(darkPath);
        }
    }
    theme->Apply(accent_color, (float)scale);
    FileLoaded();
}
void MainLoop::Drop(std::string file) {
    LoadFile(file);
}
void MainLoop::FileLoaded() {
    auto file_maybe = playback->get_current_title();
    if (file_maybe.has_value()) {
        auto name = file_maybe.value();
        SetWindowTitle((name + std::string(" - Looper")).c_str());
    } else {
        SetWindowTitle("Looper");
    }
    streams = playback->get_streams();
    properties = playback->get_property_list();
    boolean_properties.clear();
    double_properties.clear();
}
void MainLoop::GuiFunction() {
#if defined(__EMSCRIPTEN__)||defined(__ANDROID__)
    playback->LoopHook();
#endif
    position = playback->GetPosition();
    length = playback->GetLength();
    // Set the window title if the file changed, or playback stopped.
    if (playback->handle_signals(PlaybackSignalFileChanged|PlaybackSignalStarted|PlaybackSignalStopped)) {
        FileLoaded();
    }
    bool lengthKnown = length > 0.0;
    auto dockid = ImGui::DockSpaceOverViewport(0, nullptr, ImGuiDockNodeFlags_PassthruCentralNode|ImGuiDockNodeFlags_AutoHideTabBar);
    // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
    if (show_demo_window)
        ImGui::ShowDemoWindow(&show_demo_window);
    if (ImGui::BeginMainMenuBar()) {
        if (ImGui::BeginMenu(_TRI_CTX(ICON_FK_FILE, "Main menu", "File"))) {
            if (ImGui::MenuItem(_TRI_CTX(ICON_FK_FOLDER_OPEN, "Main menu | File", "Open"))) {
                // Set translatable strings here so that they are in the correct language even when it changes at runtime.
                fileDialog.SetTitle(_TR_CTX("File dialog title", "Open..."));
                fileDialog.SetTypeFilters(_TR_CTX("File dialog filter name", "Audio files"), { ".wav", ".ogg", ".mp3", ".qoa", ".flac", ".xm", ".mod"});
                fileDialog.Open();
            }
            #ifdef __EMSCRIPTEN__
            if (serviceworker_registered()) {
                if (ImGui::MenuItem(_TRI_CTX(ICON_FK_DOWNLOAD, "Main menu | File", "Update"))) {
                    update();
                }
            }
            if (is_puter_enabled()) {
            #endif
            if (ImGui::MenuItem(_TRI_CTX(ICON_FK_WINDOW_CLOSE, "Main menu | File", "Quit"))) {
                done = true;
            }
            #ifdef __EMSCRIPTEN__
            }
            #endif
            ImGui::EndMenu();
        }
        if (ImGui::BeginMenu(_TRI_CTX(ICON_FK_SCISSORS,"Main menu", "Edit"))) {
            if (ImGui::MenuItem(_TRI_CTX(ICON_FK_COG, "Main menu | Edit", "Preferences..."))) {
                prefs_window = true;
            }
            ImGui::EndMenu();
        }
        #ifndef DEBUG_MODE
        if (debug_mode)
        #endif
        if (ImGui::BeginMenu(_TRI_CTX(ICON_FK_COG, "Main menu (in debug builds)", "Debug"))) {
            if (ImGui::MenuItem(_TR_CTX("Main menu | Debug", "Show ImGui Demo Window"), nullptr, show_demo_window)) {
                show_demo_window = !show_demo_window;
                set_option<double>("ui.imgui.demo_window", show_demo_window);
            }
            if (ImGui::MenuItem(_TR_CTX("Main menu | Debug", "Edit properties"), nullptr, property_editor)) {
            	property_editor = !property_editor;
            }
            ImGui::EndMenu();
        }
        if (ImGui::BeginMenu(_TRI_CTX(ICON_FK_INFO_CIRCLE, "Main menu", "Help"))) {
            if (ImGui::MenuItem(_TRI_CTX(ICON_FK_INFO, "Main menu | Help", "About"), nullptr, about_window)) {
                about_window = !about_window;
            }
            ImGui::EndMenu();
        }
        ImGui::EndMainMenuBar();
    }
    ImGui::SetNextWindowDockID(dockid);
    ImGui::Begin(_TRI_CTX(ICON_FK_PLAY, "Main window title", "Player"), nullptr, 0);
    {
        float centerSpace = ImGui::GetWindowHeight() - ImGui::GetFrameHeightWithSpacing() - ImGui::GetFrameHeight() - ImGui::GetStyle().WindowPadding.y;
        if (streams.size() > 0) {
            static string filter = "";
            ImGui::TextUnformatted(_TR_CTX("Main Window | Stream selector | Filter label", "Filter:")); ImGui::SameLine();
            ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - ImGui::GetStyle().WindowPadding.x);
            ImGui::InputText("##FilterInput", &filter);
            ImGui::TextUnformatted(_TR_CTX("Main Window | Stream selector | Selector label", "Select a stream to switch to..."));
            ImVec2 ChildSize = ImVec2(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f), centerSpace - (ImGui::GetFrameHeightWithSpacing() * 2.0f) - (ImGui::GetStyle().ItemSpacing.y * 2.0f));
            if (ImGui::BeginChildFrame(ImGui::GetID("##StreamsContainer"), ChildSize)) {
                ImVec2 TableSize = ImVec2(0, 0);
                if (ImGui::BeginTable("##Streams", 2, ImGuiTableFlags_SizingFixedFit|ImGuiTableFlags_NoSavedSettings|ImGuiTableFlags_ScrollY, TableSize)) {
                    // Text in TableSetupColumn calls not translated because they're not visible to the user.
                    ImGui::TableSetupColumn("Index", 0);
                    ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
                    for (int i = 0; i < streams.size(); i++) {
                        auto &stream = streams[i];
                        if (stream.name == "" && stream.length == 0) {
                            continue;
                        }
                        if (stream.name.starts_with(filter)) {
                            ImGui::TableNextRow();
                            ImGui::TableSetColumnIndex(0);
                            ImGui::Text("%i", stream.id);
                            ImGui::TableSetColumnIndex(1);
                            const bool is_selected = playback->get_current_stream() == i;
                            if (ImGui::Selectable(stream.name.c_str(), is_selected, 0)) {
                                length = stream.length;
                                playback->play_stream(i);
                            }
                            if (is_selected) {
                                ImGui::SetItemDefaultFocus();
                            }
                        }
                    }
                    ImGui::EndTable();
                }
            }
            ImGui::EndChildFrame();
        }
        ImGui::SetCursorPosY(ImGui::GetWindowHeight() - ImGui::GetFrameHeightWithSpacing() - ImGui::GetFrameHeight() - ImGui::GetStyle().WindowPadding.y);
        if (ImGui::Button(playback->IsPaused() ? ICON_FK_PLAY "##Pause" : ICON_FK_PAUSE "##Pause")) {
            playback->Pause();
        }
        ImGui::SameLine();
        if (ImGui::Button(ICON_FK_REFRESH "##Restart")) {
            playback->Seek(0.0);
        }
        ImGui::SameLine();
        const int NEXT_SLIDER_COUNT = 1;

        double seek_bar_width = -(ImGui::GetFontSize() * (1 + (8 * NEXT_SLIDER_COUNT))) - ((ImGui::GetStyle().ItemSpacing.x + ImGui::GetStyle().FramePadding.x) * (NEXT_SLIDER_COUNT + 1));

        if (lengthKnown) {
            ImGui::SetNextItemWidth(seek_bar_width);
            uint8_t components = TimeToComponentCount(playback->GetLength());
            string time_str = TimeToString(position, components) + "/" + TimeToString(length, components);
            if (ImGui::SliderFloat("##Seek", &position, 0.0f, playback->GetLength(), time_str.c_str(), ImGuiSliderFlags_NoRoundToFormat))
                playback->Seek(position);
        } else {
            ImVec2 size(seek_bar_width, ImGui::GetFrameHeight());
            ImGui::InvisibleButton("##SeekIndeterminate", size);
        }
        ImGui::SameLine();
        if (ImGui::Button(ICON_FK_STOP "##Stop")) {
            playback->Stop();
        }
        float pitch = playback->GetPitch(), tempo = playback->GetTempo(), speed = playback->GetSpeed(), volume = playback->GetVolume();
        ImGui::SameLine();
        ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
        if (ImGui::SliderFloat("##Volume", &volume, 0.0, 100.0, ICON_FK_VOLUME_UP ": %.0f%%")) {
            playback->SetVolume(volume);
        }
        const float items = 3.0f;
        const float between_items = items - 1.0f;
        ImGui::PushItemWidth((ImGui::GetWindowWidth() / items) - (ImGui::GetStyle().ItemSpacing.x / (items / between_items)) - ((ImGui::GetStyle().WindowPadding.x / items) * 2.0f));
        if (ImGui::SliderFloat("##Speed", &speed, 0.25, 4.0, _TR_CTX("Playback controls | slider", "Speed: %.2fx"), ImGuiSliderFlags_Logarithmic)) {
            playback->SetSpeed(speed);
        }
        ImGui::SameLine();
        if (ImGui::SliderFloat("##Tempo", &tempo, 0.25, 4.0, _TR_CTX("Playback controls | slider", "Tempo: %.2fx"), ImGuiSliderFlags_Logarithmic)) {
            playback->SetTempo(tempo);
        }
        ImGui::SameLine();
        if (ImGui::SliderFloat("##Pitch", &pitch, 0.25, 4.0, _TR_CTX("Playback controls | slider", "Pitch: %.2fx"), ImGuiSliderFlags_Logarithmic)) {
            playback->SetPitch(pitch);
        }
        ImGui::PopItemWidth();
    }
    ImGui::End();
    if (property_editor) {
    	ImGui::SetNextWindowDockID(dockid);
     	ImGui::Begin("Property Editor", &property_editor);
      	{
         	for (auto property : properties) {
          		ImGui::PushID(property.path().c_str());
          		bool valid = false;
          		switch (property.type()) {
          			case PropertyType::Double: {
              			std::optional<double> min;
                 		std::optional<double> max;
                   		double value = 0.0;
                     	if (double_properties.contains(property.path())) {
                      		value = double_properties[property.path()];
                      	} else {
                       		auto value_to_resolve = playback->get_property(property.path());
							if (value_to_resolve.has_value()) {
								value = resolve_value<double>(value_to_resolve.value());
							}
                         	double_properties[property.path()] = value;
                       	}
              			if (property.has_hint() && property.hint().has_range()) {
                 			auto range = property.hint().range();
                    		if (range.has_min() && range.has_max()) {
                      			float flt = (float)value;
                      			if (ImGui::SliderFloat(property.path().c_str(), &flt, (float)range.min(), (float)range.max())) {
                         			double_properties[property.path()] = flt;
                         		}
                           		valid = true;
                      		} else {
                        		if (range.has_min()) min = range.min();
                          		if (range.has_max()) max = range.max();
                        	}
                 		}
	                   	if (!valid) {
	                   		ImGui::InputDouble(property.path().c_str(), &value);
	                     	if (min.has_value() && value < min) {
	                      		value = min.value();
	                        }
	                        if (max.has_value() && value > max) {
	                        	value = max.value();
	                        }
	                        double_properties[property.path()] = value;
	                        valid = true;
                   		}
                    } break;
              		case PropertyType::Boolean: {
	                   	bool value = false;
	                   	if (boolean_properties.contains(property.path())) {
	                  		value = boolean_properties[property.path()];
	                   	} else {
							auto value_to_resolve = playback->get_property(property.path());
							if (value_to_resolve.has_value()) {
								value = resolve_value<bool>(value_to_resolve.value());
							}
							boolean_properties[property.path()] = value;
	                   	}
	                    if (ImGui::Checkbox(property.path().c_str(), &value)) {
	                       	boolean_properties[property.path()] = value;
	                    }
						valid = true;
                    } break;
            	}
             	if (valid) {
              		ImGui::SameLine();
                	if (ImGui::Button("Set")) {
	              		switch (property.type()) {
		              		case PropertyType::Double: {
	                  			DoubleProperty property_d;
	                     		property_d.set_value(double_properties[property.path()]);
	                      		google::protobuf::Any value;
	                        	value.PackFrom(property_d);
	                         	playback->set_property(property.path(), value);
	                  		} break;
	                  		case PropertyType::Boolean: {
	                    		BooleanProperty property_b;
	                      		property_b.set_value(boolean_properties[property.path()]);
	                      		google::protobuf::Any value;
	                           	value.PackFrom(property_b);
	                           	playback->set_property(property.path(), value);
	                  		} break;
	                	}
                 	}
              	}
               	ImGui::PopID();
          	}
       	}
        ImGui::End();
    }
    if (prefs_window) {
        ImGui::SetNextWindowDockID(dockid);
        ImGui::Begin(_TRI_CTX(ICON_FK_COG, "Window title, window opened by menu item", "Preferences..."), &prefs_window);
        {

            static std::string set_backend_name = cur_backend->get_name();
            static std::string set_backend_id = cur_backend->get_id();
            if (restart_needed) {
                ImGui::TextUnformatted(_TR("A restart is needed to apply some changes."));
            }
            if (ImGui::BeginCombo(_TR("UI frontend"), set_backend_name.c_str())) {
                for (auto &backend : backends) {
                    bool is_current = set_backend_id == backend->get_id();
                    if (ImGui::Selectable(backend->get_name().c_str(), &is_current)) {
                        set_backend_id = backend->get_id();
                        set_backend_name = backend->get_name();
                        Looper::Options::set_option("ui.frontend", set_backend_id);
                        restart_needed = true;
                    }
                    if (is_current) {
                        ImGui::SetItemDefaultFocus();
                    }
                }
                ImGui::EndCombo();
            }
            if (ImGui::Checkbox(_TR_CTX("Preference | VSync checkbox", "Enable VSync"), &vsync)) {
                set_option<bool>("ui.imgui.vsync", vsync);
            }
            ImGui::SameLine();
            ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - ImGui::GetStyle().WindowPadding.x);
            if (ImGui::SliderInt("##Framerate", &framerate, 10, 480, _TR_CTX("Preferences | Framerate slider", "Max framerate without VSync: %d"))) {
                set_option<int64_t>("ui.imgui.framerate", framerate);
            }
            if (ImGui::Checkbox(_TR_CTX("Preference | Debug menu enable", "Enable debug menu in release builds"), &debug_mode)) {
                set_option<bool>("ui.imgui.debug_mode", debug_mode);
            }
            if (ImGui::Button(_TRI_CTX(ICON_FK_MAGIC, "Preference | Related non-preference button", "Theme Editor"), ImVec2(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f), 0))) {
                theme_editor = true;
            }
            #ifdef __EMSCRIPTEN__
            bool puterEnabled = is_puter_enabled();
            if (ImGui::Checkbox("Enable Puter API", &puterEnabled)) {
                enable_puter(puterEnabled);
            }
            #endif
            static bool override_lang = lang != DEFAULT_LANG;
            if (ImGui::Checkbox(_TR_CTX("Preference | override enable checkbox", "Override language"), &override_lang)) {
                if (!override_lang) {
                    lang = DEFAULT_LANG;
                    SET_LANG(lang.c_str());
                }
                if (lang == DEFAULT_LANG) {
                    delete_option("ui.imgui.lang");
                } else {
                    set_option<std::string>("ui.imgui.lang", lang);
                }
            }
            if (override_lang) {
                ImGui::SameLine();
                ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - (ImGui::GetFontSize()) - ((ImGui::GetStyle().ItemSpacing.x + (ImGui::GetStyle().FramePadding.x * 2.0f))) - (ImGui::GetStyle().WindowPadding.x));
                ImGui::InputText("##LanguageOverrideTextBox", &lang);
                ImGui::SameLine();
                if (ImGui::Button(ICON_FK_CHECK)) {
                    SET_LANG(lang.c_str());
                    if (lang == DEFAULT_LANG) {
                        delete_option("ui.imgui.lang");
                    } else {
                        set_option<std::string>("ui.imgui.lang", lang);
                    }
                }
            }
            bool overrideTouchscreenMode = touchScreenModeOverride.has_value();
            if (ImGui::Checkbox(_TR_CTX("Preference | override enable checkbox", "Override touchscreen mode"), &overrideTouchscreenMode)) {
                if (overrideTouchscreenMode) {
                    touchScreenModeOverride = isTouchScreenMode();
                } else {
                    touchScreenModeOverride = {};
                }
            }
            if (overrideTouchscreenMode) {
                bool touchScreenMode = isTouchScreenMode();
                if (ImGui::Checkbox(_TRI_CTX(ICON_FK_HAND_O_UP, "Preference | Checkbox (Only shown when override enabled)", "Enable touch screen mode"), &touchScreenMode)) {
                    touchScreenModeOverride = touchScreenMode;
                }
            }
            bool overrideScale = scaleOverride.has_value();
            if (ImGui::Checkbox(_TR_CTX("Preference | override enable checkbox", "Override DPI scaling"), &overrideScale)) {
                if (overrideScale) {
                    scaleOverride = scale;
                } else {
                    scaleOverride = {};
                }
                QueueUpdateScale();
            }
            if (overrideScale) {
                float newScale = scale;
                if (ImGui::SliderFloat(_TRI_CTX(ICON_FK_HAND_O_UP, "Preference | Slider (Only shown when override enabled)", "DPI Scaling amount"), &newScale, 0.25, 4, "%.2f", ImGuiSliderFlags_Logarithmic)) {
                    scaleOverride = newScale;
                    QueueUpdateScale();
                }
            }

            static string filter = "";
            ImGui::TextUnformatted(_TR_CTX("Preference | Theme selector | Filter label", "Filter:")); ImGui::SameLine();
            ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - ImGui::GetStyle().WindowPadding.x);
            ImGui::InputText("##FilterInput", &filter);
            ImGui::TextUnformatted(_TR_CTX("Preferences | Theme selector | Selector label", "Select a theme..."));
            ImVec2 ChildSize = ImVec2(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2.0f), -ImGui::GetFrameHeightWithSpacing());
            if (ImGui::BeginChildFrame(ImGui::GetID("##ThemesContainer"), ChildSize)) {
                ImVec2 TableSize = ImVec2(0, 0);
                if (ImGui::BeginTable("##Themes", 2, ImGuiTableFlags_SizingFixedFit|ImGuiTableFlags_NoSavedSettings|ImGuiTableFlags_ScrollY, TableSize)) {
                    // Text in TableSetupColumn calls not translated because they're not visible to the user.
                    ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
                    ImGui::TableSetupColumn("Remove", 0);
                    for (auto themePath : Theme::availableThemes) {
                        string themeStem = themePath.stem().string();
                        if (themeStem.starts_with(filter)) {
                            ImGui::TableNextRow();
                            ImGui::TableSetColumnIndex(0);
                            const bool is_selected = themePath == theme->file_path;
                            if (ImGui::Selectable((theme->themeStrings[themePath].name + string(" (") + string(themeStem) + string(")")).c_str(), is_selected, 0)) {
                                delete theme;
                                theme = new Theme(themePath);
                                theme->Apply(accent_color, (float)scale);
                                path themeFName = themePath.stem();
                                if (!themeFName.empty()) {
                                    set_option<std::string>("ui.imgui.theme", themeFName.string());
                                }
                                break;
                            }
                            if (is_selected) {
                                ImGui::SetItemDefaultFocus();
                            } else {
                                ImGui::TableSetColumnIndex(1);
                                if (ImGui::SmallButton((string(ICON_FK_WINDOW_CLOSE "##") + themeStem).c_str())) {
                                    std::filesystem::remove(themePath);
                                    Theme::updateAvailableThemes();
                                    break;
                                }
                            }
                        }
                    }
                    ImGui::EndTable();
                }
            }
            ImGui::EndChildFrame();
            ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2));
            if (ImGui::ColorEdit4("##AccentColor", &accent_color.x, ImGuiColorEditFlags_InputHSV|ImGuiColorEditFlags_DisplayHSV|ImGuiColorEditFlags_Float)) {
                set_option<double>("ui.imgui.accent_color.h", accent_color.x);
                set_option<double>("ui.imgui.accent_color.s", accent_color.y);
                set_option<double>("ui.imgui.accent_color.v", accent_color.z);
                set_option<double>("ui.imgui.accent_color.a", accent_color.w);
            }
            theme->Apply(accent_color, (float)scale);
        }
        ImGui::End();
    }
    if (about_window) {
        ImGui::SetNextWindowDockID(dockid);
        if (ImGui::Begin(_TRI_CTX(ICON_FK_INFO, "Window title, window opened by menu item", "About and Licenses"), &about_window)) {
            ImGui::PushFont(title);
            static const string APP_NAME_STR = _TR_CTX("Application name.", "Looper");
            ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, 0.0f, APP_NAME_STR.c_str()).x) / 2.0f);
            ImGui::TextUnformatted(APP_NAME_STR.c_str());
            ImGui::PopFont();
            static const string VER_STRING = _TR_CTX("Version string format specifier", "Version ") + string(TAG) + _TRS_CTX("Suffix to the version string in the about window, if needed", " ");
            ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, 0.0f, VER_STRING.c_str()).x) / 2.0f);
            ImGui::TextUnformatted(VER_STRING.c_str());
            ImGui::NewLine();
            auto &license_data = get_license_data();
            // Left
            static LicenseData selected = *license_data.begin();
            {
                ImGui::BeginGroup();
                ImGui::TextUnformatted(_TR_CTX("Project selector label.", "Project"));
                // Next string is internal.
                ImGui::BeginChild("project selector", ImVec2(150, 0), true);
                for (auto project : license_data)
                {
                    if (ImGui::Selectable(project.Project.c_str(), selected.Project == project.Project))
                        selected = project;
                }
                ImGui::EndChild();
                ImGui::EndGroup();
            }
            ImGui::SameLine();
            // Right
            {
                ImGui::BeginGroup();
                ImGui::TextUnformatted(_TR_CTX("License viewer label", "License"));
                // Next string is internal.
                ImGui::BeginChild("license view", ImVec2(0, 0), true); // *don't* leave room for the nonexistant line below us!
                ImGui::Text(_TR_CTX("License viewer | information above license - string 1: selected project, string 2: SPDX license identifier", "%s: %s"), selected.Project.c_str(), selected.Spdx.c_str());
                ImGui::Separator();
                ImGui::TextWrapped("%s", selected.LicenseContents.c_str());
                ImGui::EndChild();
                ImGui::EndGroup();
            }
        }
        ImGui::End();
    }
    // Display the theme editor.
    if (theme_editor) {
        Theme::ShowEditor(&theme_editor, theme, dockid, window_width, window_height);
        // Immediately apply any changes made in the theme editor.
        theme->Apply(accent_color, (float)scale);
    }
    if (fileDialog.IsOpened()) {
        // Make the fallback file dialog fill the window.
        fileDialog.SetWindowSize(window_width, window_height);
        fileDialog.SetWindowPos(0, 0);
    }
    // Display the file dialog
    fileDialog.Display();

    // Load a new file when it has been selected.
    if (fileDialog.HasSelected()) {
        playback->Start(fileDialog.GetSelected().string());
        // Make sure to not load the file unnecessarily.
        fileDialog.ClearSelected();
    }
    if (exit_flag.load()) {
        done = true;
    }
}
void MainLoop::LoadFile(std::string file) {
    playback->Start(file);
}
void MainLoop::Deinit() {

    {
        path themePath(theme->file_path);
        themePath = themePath.stem();
        if (!themePath.empty()) {
            set_option<std::string>("ui.imgui.theme", themePath.string());
        }
        set_option<double>("ui.imgui.accent_color.h", accent_color.x);
        set_option<double>("ui.imgui.accent_color.s", accent_color.y);
        set_option<double>("ui.imgui.accent_color.v", accent_color.z);
        set_option<double>("ui.imgui.accent_color.a", accent_color.w);
        set_option<double>("ui.imgui.demo_window", show_demo_window);
        set_option<bool>("ui.imgui.vsync", vsync);
        set_option<int64_t>("ui.imgui.framerate", framerate);
        if (lang == DEFAULT_LANG) {
            delete_option("ui.imgui.lang");
        } else {
            set_option<std::string>("ui.imgui.lang", lang);
        }
    }
}
MainLoop::MainLoop() : RendererBackend() {
    for (auto &kv : UIBackend::backends) {
        backends.push_back(kv.second);
    }
    cur_backend = UIBackend::get_backend(UI_BACKEND()).value_or(UIBackend::get_first_backend());
}
std::string ImGuiUIBackend::get_id() {
    return "imgui";
}
std::string ImGuiUIBackend::get_name() {
    return "Dear ImGui frontend";
}
void ImGuiUIBackend::QuitHandler() {
    if (main_loop == nullptr) {
        return;
    }
    ((MainLoop*)main_loop)->exit_flag.store(true);
}
// Main code
int ImGuiUIBackend::run(std::vector<std::string> realArgs, int argc, char** argv)
{
    int possible_error = UIBackend::run(realArgs, argc, argv);
    if (possible_error != 0) {
        return possible_error;
    }
    MainLoop *loop = new MainLoop();
    loop->playback = playback;
    loop->args = args;
    main_loop = loop;
    return loop->Run();
}
void ImGuiUIBackend::add_licenses() {
    auto &license_data = get_license_data();
    auto glib = LicenseData("Glib", "lgpl-2.1-or-later");
    auto imgui = LicenseData("Dear ImGui", "MIT");
    auto libintl = LicenseData("libintl", "LGPL-2.1-only");
    auto imfb = LicenseData("imgui-filebrowser", "MIT");
    auto noto = LicenseData("Noto Sans", "OFL-1.1-RFN");
    auto noto_jp = LicenseData("Noto Sans JP", "OFL-1.1-RFN");
    auto fork_awesome = LicenseData("Fork Awesome", "OFL-1.1-RFN");
    auto ifch = LicenseData("IconFontCppHeaders", "Zlib");
    LOAD_LICENSE(glib, lgpl_2_1);
    LOAD_LICENSE(imgui, imgui);
    LOAD_LICENSE(libintl, lgpl_2_1);
    LOAD_LICENSE(imfb, imgui_filebrowser);
    LOAD_LICENSE(noto, notosans);
    LOAD_LICENSE(noto_jp, notosansjp);
    LOAD_LICENSE(fork_awesome, forkawesome);
    LOAD_LICENSE(ifch, icnfntcpphdrs);
    license_data.insert(glib);
    license_data.insert(imgui);
    license_data.insert(libintl);
    license_data.insert(imfb);
    license_data.insert(noto);
    license_data.insert(noto_jp);
    license_data.insert(fork_awesome);
    license_data.insert(ifch);
#ifdef PORTALS
    auto libportal = LicenseData("libportal", "LGPL-3.0-only");
    LOAD_LICENSE(libportal, lgpl_3_0);
    license_data.insert(libportal);
#endif
}