#include "options_window.hpp"
#include <backend.hpp>
#include <options.hpp>
#include <optional>
Glib::RefPtr<UIBackendColumns> UIBackendColumns::create(const Glib::ustring &id, const Glib::ustring &name) {
    return Glib::make_refptr_for_instance<UIBackendColumns>(new UIBackendColumns(id, name));
}
UIBackendColumns::UIBackendColumns(const Glib::ustring &id, const Glib::ustring &name)
: backendId(id),
  backendName(name)
{ }
void OptionsWindow::add_option(Glib::ustring title, Gtk::Widget *widget, std::string key) {
    mainBox.insert_row(mainBoxRow);
    Gtk::Label *label = new Gtk::Label();
    label->set_css_classes({"option-label"});
    label->set_justify(Gtk::Justification::LEFT);
    label->set_hexpand(false);
    label->set_text(title);
    mainBox.attach(*label, 0, mainBoxRow);
    optionWidgets.push_back(label);
    Gtk::Label *emptyLabel = new Gtk::Label();
    emptyLabel->set_hexpand(true);
    emptyLabel->set_text("");
    mainBox.attach(*emptyLabel, 1, mainBoxRow);
    optionWidgets.push_back(emptyLabel);
    auto widget_css_classes = widget->get_css_classes();
    widget_css_classes.push_back("option-editor");
    widget->set_css_classes(widget_css_classes);
    if (!widget->get_hexpand()) {
        widget->set_halign(Gtk::Align::END);
    }

    mainBox.attach(*widget, 2, mainBoxRow);
    optionWidgets.push_back(widget);
    mainBoxRow++;
    modifiableOptions.insert(key);
}
void OptionsWindow::revert() {
    Looper::Options::load_options();
    for (auto option : modifiableOptions) {
        optionChanged.emit(option);
    }
    needsRestartLabel.set_visible(savedOptionRequiresRestart);
}
void OptionsWindow::save() {
    Looper::Options::save_options();
    optionsSaved.emit();
    savedOptionRequiresRestart = needsRestartLabel.get_visible();
}
OptionsWindow::OptionsWindow() {
    set_title("Options...");
    set_icon_name("preferences-other");
    set_name("options-window");
    rootBox.set_name("options-window-root");
    mainBox.set_expand();
    mainBox.insert_column(0);
    mainBox.insert_column(1);
    mainBox.insert_column(2);
    mainBox.set_name("options-main-box");
    needsRestartLabel.set_name("options-needs-restart-label");
    needsRestartLabel.set_text("A restart is needed to apply some changes.");
    needsRestartLabel.set_visible(false);
    needsRestartLabel.insert_at_start(rootBox);
    Gtk::DropDown *uiBackendBox = new Gtk::DropDown();
    Glib::RefPtr<Gio::ListStore<UIBackendColumns>> listStore = Gio::ListStore<UIBackendColumns>::create();
    for (auto kv : UIBackend::backends) {
        auto backend = kv.second;
        Glib::ustring id(backend->get_id().c_str());
        Glib::ustring name(backend->get_name().c_str());
        listStore->append(UIBackendColumns::create(id, name));
    }
    
    uiBackendBox->set_model(listStore);
    auto factory = Gtk::SignalListItemFactory::create();
    factory->signal_setup().connect([this](const Glib::RefPtr<Gtk::ListItem>& list_item) {
        auto box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 10);
        box->append(*Gtk::make_managed<Gtk::Label>());
        box->append(*Gtk::make_managed<Gtk::Label>());
        list_item->set_child(*box);
    });
    factory->signal_bind().connect([this](const Glib::RefPtr<Gtk::ListItem>& list_item) {
        auto col = std::dynamic_pointer_cast<UIBackendColumns>(list_item->get_item());
        if (!col) return;
        auto box = dynamic_cast<Gtk::Box*>(list_item->get_child());
        if (!box) return;
        box->set_css_classes({"ui-backend-option"});
        auto name_label = dynamic_cast<Gtk::Label*>(box->get_first_child());
        name_label->set_hexpand();
        name_label->set_text(col->backendName);
        name_label->set_css_classes({"ui-backend-name", "primary"});
        if (!name_label) return;
        auto id_label = dynamic_cast<Gtk::Label*>(name_label->get_next_sibling());
        if (!id_label) return;
        id_label->set_text(col->backendId);
        id_label->set_css_classes({"ui-backend-id", "secondary"});
    });
    uiBackendBox->set_factory(factory);
    uiBackendBox->property_selected().signal_changed().connect([uiBackendBox, listStore, this]() {
        std::string backendId = listStore->get_item(uiBackendBox->get_selected())->backendId;
        this->change_option("ui.frontend", backendId);
        this->needsRestartLabel.set_visible();
    });
    add_option("UI frontend", uiBackendBox, "ui.frontend");
    Gtk::Switch *darkModeSwitch = new Gtk::Switch();
    darkModeSwitch->set_active(Looper::Options::get_option<bool>("ui.gtk.dark_mode", false));
    darkModeSwitch->property_active().signal_changed().connect([this, darkModeSwitch]() {
        Looper::Options::set_option<bool>("ui.gtk.dark_mode", darkModeSwitch->get_active());
    });
    add_option("Dark mode", darkModeSwitch, "ui.gtk.dark_mode");
    optionChanged.connect([this, uiBackendBox, listStore, darkModeSwitch](std::string key) {
        if (key == "ui.frontend") {
            std::string frontend = Looper::Options::get_option<std::string>(key);
            for (int i = 0; i < listStore->get_n_items(); i++) {
                if (std::string(listStore->get_item(i)->backendId.c_str()) == frontend) {
                    uiBackendBox->set_selected(i);
                    break;
                }
            }
        } else if (key == "ui.gtk.dark_mode") {
            darkModeSwitch->set_active(Looper::Options::get_option<bool>(key));
        }
    });
    mainBox.set_row_spacing(2);
    mainBox.set_column_spacing(2);
    mainBox.insert_at_end(rootBox);
    okBtn.set_name("options-ok-btn");
    okBtn.set_css_classes({"options-window-btn"});
    okBtn.set_label("OK");
    cancelBtn.set_name("options-cancel-btn");
    cancelBtn.set_css_classes({"options-window-btn"});
    cancelBtn.set_label("Cancel");
    saveBtn.set_name("options-save-btn");
    saveBtn.set_css_classes({"options-window-btn"});
    saveBtn.set_label("Save");
    revertBtn.set_name("options-revert-btn");
    revertBtn.set_css_classes({"options-window-btn"});
    revertBtn.set_label("Revert");
    okBtn.signal_clicked().connect([this]() {
        this->save();
        this->close();
    });
    cancelBtn.signal_clicked().connect([this]() {
        this->revert();
        this->close();
    });
    revertBtn.signal_clicked().connect([this]() {
        this->revert();
    });
    saveBtn.signal_clicked().connect([this]() {
        this->save();
    });
    okBtn.insert_at_end(btnBox);
    saveBtn.insert_at_end(btnBox);
    revertBtn.insert_at_end(btnBox);
    cancelBtn.insert_at_end(btnBox);
    rootBox.set_spacing(8);
    btnBox.set_spacing(2);
    btnBox.set_orientation(Gtk::Orientation::HORIZONTAL);
    btnBox.set_name("options-window-btns");
    btnBox.set_halign(Gtk::Align::END);
    btnBox.insert_at_end(rootBox);
    rootBox.set_orientation(Gtk::Orientation::VERTICAL);
    rootBox.set_expand();
    set_child(rootBox);
    Gtk::Requisition min;
    Gtk::Requisition nat;
    rootBox.get_preferred_size(min, nat);
    set_default_size(nat.get_width(), nat.get_height());
}
OptionsWindow::~OptionsWindow() {
    for (auto widget : optionWidgets) {
        delete widget;
    }
}