#pragma once

#include <giomm/dbusconnection.h>
#include <gtkmm/icontheme.h>
#include <libupower-glib/upower.h>

#include <unordered_map>

#include "AIconLabel.hpp"

namespace waybar::modules {

class UPower final : public AIconLabel {
 public:
  UPower(const std::string &, const Json::Value &);
  virtual ~UPower();
  auto update() -> void override;

 private:
  const std::string NO_BATTERY{"battery-missing-symbolic"};

  // Config
  bool showIcon_{true};
  bool hideIfEmpty_{true};
  int iconSize_{20};
  int tooltip_spacing_{4};
  int tooltip_padding_{4};
  Gtk::Box contentBox_;  // tooltip box
  std::string tooltipFormat_;

  // UPower device info
  struct upDevice_output {
    UpDevice *upDevice{NULL};
    double percentage{0.0};
    double temperature{0.0};
    guint64 time_full{0u};
    guint64 time_empty{0u};
    gchar *icon_name{(char *)'\0'};
    bool upDeviceValid{false};
    UpDeviceState state;
    UpDeviceKind kind;
    char *nativePath{(char *)'\0'};
    char *model{(char *)'\0'};
  };

  // Technical variables
  std::string nativePath_;
  std::string model_;
  std::string lastStatus_;
  Glib::ustring label_markup_;
  std::mutex mutex_;
  Glib::RefPtr<Gtk::IconTheme> gtkTheme_;
  bool sleeping_;

  // Technical functions
  void addDevice(UpDevice *);
  void removeDevice(const gchar *);
  void removeDevices();
  void resetDevices();
  void setDisplayDevice();
  const Glib::ustring getText(const upDevice_output &upDevice_, const std::string &format);
  bool queryTooltipCb(int, int, bool, const Glib::RefPtr<Gtk::Tooltip> &);

  // DBUS variables
  guint watcherID_;
  Glib::RefPtr<Gio::DBus::Connection> conn_;
  guint subscrID_{0u};

  // UPower variables
  UpClient *upClient_;
  upDevice_output upDevice_;  // Device to display
  typedef std::unordered_map<std::string, upDevice_output> Devices;
  Devices devices_;
  bool upRunning_{true};

  // DBus callbacks
  void getConn_cb(Glib::RefPtr<Gio::AsyncResult> &result);
  void onAppear(const Glib::RefPtr<Gio::DBus::Connection> &, const Glib::ustring &,
                const Glib::ustring &);
  void onVanished(const Glib::RefPtr<Gio::DBus::Connection> &, const Glib::ustring &);
  void prepareForSleep_cb(const Glib::RefPtr<Gio::DBus::Connection> &connection,
                          const Glib::ustring &sender_name, const Glib::ustring &object_path,
                          const Glib::ustring &interface_name, const Glib::ustring &signal_name,
                          const Glib::VariantContainerBase &parameters);

  // UPower callbacks
  static void deviceAdded_cb(UpClient *client, UpDevice *device, gpointer data);
  static void deviceRemoved_cb(UpClient *client, const gchar *objectPath, gpointer data);
  static void deviceNotify_cb(UpDevice *device, GParamSpec *pspec, gpointer user_data);
  // UPower secondary functions
  void getUpDeviceInfo(upDevice_output &upDevice_);
};

}  // namespace waybar::modules