#include "wf-popover.hpp"
#include <gtkmm.h>
#include <memory>
#include <sigc++/connection.h>
#include <filesystem>
#include <thread>

#ifdef HAVE_DDCUTIL
extern "C" {
    #include <ddcutil_types.h>
    #include <ddcutil_c_api.h>
    #include <ddcutil_status_codes.h>
}
#endif

#include "wf-shell-app.hpp"
#include "widget.hpp"
#include "animated-scale.hpp"

class WayfireBrightness;

class WfLightControl : public Gtk::Box
{
  protected:
    WayfireAnimatedScale scale;
    Gtk::Label label;
    WayfireBrightness *parent;
    std::vector<sigc::connection> signals;

    void update_parent_icon();

    WfOption<int> slider_length{"panel/brightness_slider_length"};

  public:
    WfLightControl(WayfireBrightness *parent);

    virtual std::string get_connector() = 0;
    virtual std::string get_name() = 0;
    WayfireBrightness *get_parent();

    void set_scale_target_value(double value);
    double get_scale_target_value();
    // a double from 0 to 1 for min to max
    virtual void set_brightness(double brightness) = 0;
    virtual double get_brightness() = 0;
};

class LightManager
{
  protected:
    LightManager()
    {}
    // managed widgets
    std::vector<WayfireBrightness*> widgets;

    virtual void catch_up_widget(WayfireBrightness *widget) = 0;
    virtual void strip_widget(WayfireBrightness *widget)    = 0;

  public:
    void add_widget(WayfireBrightness *widget);
    void rem_widget(WayfireBrightness *widget);
};

// singleton that monitors sysfs and calls the necessary functions
// monitors appearance and deletion of backlight devices
// and the brightness of each of them
class SysfsSurveillor : public LightManager
{
  private:
    SysfsSurveillor();
    void handle_inotify_events();
    bool check_perms(std::filesystem::path);
    void add_dev(std::filesystem::path);
    void rem_dev(std::filesystem::path);
    void catch_up_widget(WayfireBrightness *widget);
    void strip_widget(WayfireBrightness *widget);

    static inline std::unique_ptr<SysfsSurveillor> instance;

    int fd; // inotify file descriptor

    // stores the data that goes with the inotify watch descriptor (the int)
    // the controls are all the controls which represent this device, to be updated
    std::map<
        int,
        std::pair<
            std::filesystem::path,
            std::vector<std::shared_ptr<WfLightControl>>
        >
    > wd_to_path_controls;

    // watch descriptors for files (so, a device) being added or removed
    int wd_additions, wd_removal;

    // managed widgets
    std::vector<WayfireBrightness*> widgets;

    // thread on which to run handle_inotify_event on loop
    std::thread inotify_thread;

  public:
    ~SysfsSurveillor();

    static SysfsSurveillor& get();
};

#ifdef HAVE_DDCUTIL
class DdcaSurveillor : public LightManager
{
  private:
    DdcaSurveillor();
    void catch_up_widget(WayfireBrightness *widget);
    void strip_widget(WayfireBrightness *widget);
    static void rescan_mccs_monitors(const int pos, const int rem, const int add);
    void clean_controls();

    sigc::connection mon_ch_sig;
    static inline std::unique_ptr<DdcaSurveillor> instance;

  public:
    ~DdcaSurveillor();

    std::map<DDCA_Display_Ref, std::vector<std::shared_ptr<WfLightControl>>> ref_to_controls;

    static DdcaSurveillor& get();
};
#endif

class WayfireBrightness : public WayfireWidget
{
  private:
    void init(Gtk::Box *container) override;

    WayfireOutput *output;

    Gtk::Image icon;
    Gtk::Box box, display_box, other_box;
    Gtk::Label display_label, other_label;
    Gtk::Separator disp_othr_sep;
    sigc::connection popover_timeout;

    WfOption<int> spacing{"panel/brightness_spacing"};

    void hide_unused();

  public:
    WayfireBrightness(WayfireOutput *output);
    ~WayfireBrightness();

    std::unique_ptr<WayfireMenuWidget> button;

    WfOption<double> popup_timeout{"panel/brightness_popup_timeout"};
    WfOption<double> scroll_sensitivity{"panel/brightness_scroll_sensitivity"};
    WfOption<bool> invert_scroll{"panel/brightness_invert_scroll"};
    WfOption<bool> popup_on_change{"panel/brightness_popup_on_change"};

    std::shared_ptr<WfLightControl> ctrl_this_display;

    std::vector<std::shared_ptr<WfLightControl>> controls;

    void add_control(std::shared_ptr<WfLightControl> control);
    void rem_control(std::shared_ptr<WfLightControl> control);

    void update_icon();
};
