#include "gpanthera.hh"
#include <iostream>
#include <utility>
#include <gtk/gtk.h>

namespace gPanthera {
    DockablePane::DockablePane(std::shared_ptr<LayoutManager> layout, Gtk::Widget &child, const Glib::ustring &name, const Glib::ustring &label, Gtk::Image *icon, DockStack *stack, Gtk::Widget *custom_header)
        : Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0), name(name) {
        if(icon) {
            this->icon = icon;
        }
        if(stack) {
            this->stack = stack;
        }
        this->layout = std::move(layout);
        this->label.set_text(label);
        header = std::make_unique<Gtk::HeaderBar>();
        header->set_show_close_button(false);
        if(custom_header) {
            header->set_custom_title(*custom_header);
        } else {
            header->set_custom_title(this->label);
        }
        auto header_menu_button = Gtk::make_managed<Gtk::MenuButton>();
        auto header_menu = Gtk::make_managed<Gtk::Menu>();
        header_menu_button->set_direction(Gtk::ARROW_NONE);
        header_menu_button->set_popup(*header_menu);
        auto close_menu_item = Gtk::make_managed<Gtk::MenuItem>("Close");
        auto pop_out_menu_item = Gtk::make_managed<Gtk::MenuItem>("Pop out");
        auto move_menu = Gtk::make_managed<Gtk::Menu>();
        close_menu_item->signal_activate().connect([this]() {
            if(this->stack) {
                this->stack->set_visible_child("empty");
            }
        });
        pop_out_menu_item->signal_activate().connect([this]() {
            if(this->stack) {
                this->pop_out();
            }
        });
        for(auto &this_stack: this->layout->stacks) {
            auto menu_item = Gtk::make_managed<Gtk::MenuItem>(this_stack->name);
            menu_item->signal_activate().connect([this, this_stack]() {
                this_stack->add_pane(*this);
            });
            move_menu->append(*menu_item);
        }
        auto move_menu_item = Gtk::make_managed<Gtk::MenuItem>("Move");
        move_menu_item->set_submenu(*move_menu);
        header_menu->append(*close_menu_item);
        header_menu->append(*pop_out_menu_item);
        header_menu->append(*move_menu_item);
        header_menu->show_all();
        header->pack_end(*header_menu_button);
        this->pack_start(*header, Gtk::PACK_SHRINK);
        this->child = &child;
        this->pack_end(child, Gtk::PACK_EXPAND_WIDGET);
        show_all_children();
    }

    void DockablePane::redock(DockStack *stack) {
        if(this->window != nullptr) {
            this->window->hide();
            gtk_window_set_titlebar(GTK_WINDOW(this->window->gobj()), nullptr);
            // TODO: Why is it changing style?
            this->header->unset_background_color();
            this->window->remove();
            this->window->set_decorated(false);
            this->window->close();
            delete this->window;
            this->window = nullptr;
            this->pack_start(*this->header, Gtk::PACK_SHRINK);
            this->header->show_all();
        }
        this->stack = stack;
        this->stack->add(*this, this->get_identifier());
        this->show_all();
    }

    void DockablePane::pop_out() {
        if(this->stack != nullptr) {
            this->stack->remove(*this);
            this->stack = nullptr;
        }

        if(this->window == nullptr) {
            this->remove(*this->header);
            this->window = new DockWindow(this);
            this->window->set_titlebar(*this->header);
            this->window->show_all();
        }
    }

    Glib::ustring DockablePane::get_identifier() const {
        return name;
    }

    Gtk::Image *DockablePane::get_icon() const {
        return icon;
    }

    Gtk::Widget *DockablePane::get_child() const {
        return child;
    }

    Gtk::Label *DockablePane::get_label() {
        return &label;
    }

    LayoutManager::LayoutManager() : Glib::ObjectBase("LayoutManager") {
    }

    void LayoutManager::add_pane(DockablePane *pane) {
        panes.push_back(pane);
    }

    void LayoutManager::add_stack(DockStack *stack) {
        stacks.push_back(stack);
    }

    DockStack::DockStack(std::shared_ptr<LayoutManager> layout, const Glib::ustring &name) : Gtk::Stack(), layout(layout), name(name) {
        auto *empty_child = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_VERTICAL, 0);
        add(*empty_child, "empty");
        // Add the stack to a layout manager
        this->layout->add_stack(this);
    }

    DockWindow::DockWindow(DockablePane *pane) {
        this->pane = pane;
        this->add(*pane);
    }

    DockStackSwitcher::DockStackSwitcher(DockStack *stack) : Gtk::ButtonBox(Gtk::ORIENTATION_HORIZONTAL), stack(stack) {
        this->stack->signal_add().connect([this](Gtk::Widget *child) {
            this->update_buttons();
        });
        this->stack->signal_remove().connect([this](Gtk::Widget *child) {
            this->update_buttons();
        });
    }

    void DockStackSwitcher::update_buttons() {
        for(auto &child: get_children()) {
            if(child) {
                remove(*child);
            }
        }
        for(auto const &widget : stack->get_children()) {
            if(auto pane = dynamic_cast<DockablePane*>(widget)) {
                auto *button = Gtk::make_managed<Gtk::Button>();
                if(pane->get_icon()) {
                    button->set_image(*pane->get_icon());
                }
                button->signal_clicked().connect([this, pane]() {
                    if(stack->get_visible_child_name() == pane->get_identifier()) {
                        stack->set_visible_child("empty");
                    } else {
                        stack->set_visible_child(pane->get_identifier());
                    }
                });
                if(pane->get_identifier() != Glib::ustring("empty")) {
                    pack_start(*button);
                }
            }
        }
        this->show_all();
    }

    void DockStack::add_pane(DockablePane &child) {
        child.redock(this);
    }
} // namespace gPanthera
