#pragma once

#include <gtkmm.h>

#include "../../widget.hpp"
#include "toplevel.hpp"
#include "layout.hpp"

#ifdef HAVE_DMABUF
    #include <gbm.h>
#endif // HAVE_DMABUF

struct WayfireListToplevel
{
    std::string title;
    std::string app_id;
    std::string identifier;
};

class WayfireToplevel;

class WayfireWindowList : public Gtk::Box, public WayfireWidget
{
    wl_display *display;
    wl_registry *registry;
    WfOption<int> user_size{"panel/window_list_size"};
    std::shared_ptr<WayfireWindowListLayout> layout;

  public:
    std::map<zwlr_foreign_toplevel_handle_v1*,
        std::unique_ptr<WayfireToplevel>> toplevels;
    std::map<ext_foreign_toplevel_handle_v1*,
        std::unique_ptr<WayfireListToplevel>> list_toplevels;

    zwlr_foreign_toplevel_manager_v1 *manager = NULL;
    ext_foreign_toplevel_list_v1 *foreign_toplevel_list     = NULL;
    ext_image_copy_capture_manager_v1 *copy_capture_manager = NULL;
    ext_foreign_toplevel_image_capture_source_manager_v1 *toplevel_capture_manager = NULL;
    WayfireOutput *output;
    Gtk::ScrolledWindow scrolled_window;

    WayfireWindowList(WayfireOutput *output);
    virtual ~WayfireWindowList();

    void handle_toplevel_manager(zwlr_foreign_toplevel_manager_v1 *manager);
    void handle_new_toplevel(zwlr_foreign_toplevel_handle_v1 *toplevel);
    void handle_toplevel_closed(zwlr_foreign_toplevel_handle_v1 *handle);

    uint64_t get_view_id_from_full_app_id(const std::string& app_id);

    wayfire_config *get_config();

    void init(Gtk::Box *container) override;

    /**
     * Set the widget which should always be rendered on top of the other child
     * widgets */
    void set_top_widget(Gtk::Widget *top = nullptr);
    /** Set the absolute position of the top widget */
    void set_top_x(int x);

    /**
     * @param x the x-axis position, relative to ref
     * @param ref The widget that x is relative to. ref must be a child
     * widget of this box
     * @return the position x, but relative to the box
     */
    int get_absolute_position(int x, Gtk::Widget& ref);

    /** Find the direct child widget at the given box-relative coordinates,
     * ignoring the top widget if possible, i.e if the top widget and some
     * other widget are at the given coordinates, then the bottom widget will
     * be returned
     *
     * @return The direct child widget or none if it doesn't exist
     */
    Gtk::Widget *get_widget_at(int x);
    /** Find the direct child widget before the given box-relative coordinates,
     * ignoring the top widget if possible, i.e if the top widget and some
     * other widget are at the given coordinates, then the bottom widget will
     * be returned
     *
     * @return The direct child widget or none if it doesn't exist
     */
    Gtk::Widget *get_widget_before(int x);

    WfOption<bool> live_window_previews{"panel/window_list_live_window_previews"};
    void handle_new_wl_output(wl_output *output);

    zwp_linux_dmabuf_feedback_v1 *feedback = nullptr;
    zwp_linux_dmabuf_v1 *dmabuf = nullptr;
    gbm_device *dmabuf_device   = nullptr;

  private:
    int get_default_button_width();
    int get_target_button_width();
};
