/*
 *
 * Conky, a system monitor, based on torsmo
 *
 * Please see COPYING for details
 *
 * Copyright (c) 2005-2024 Brenden Matthews, Philip Kovacs, et. al.
 *  (see AUTHORS)
 * All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef CONKY_X11_H
#define CONKY_X11_H

#include "config.h"

#ifndef BUILD_X11
#error x11.h included when BUILD_X11 is disabled
#endif

#include <X11/Xatom.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wvariadic-macros"
#include <X11/Xlib.h>
#pragma GCC diagnostic pop

#ifdef BUILD_XFT
#include <X11/Xft/Xft.h>
#endif
#ifdef BUILD_XDBE
#include <X11/extensions/Xdbe.h>
#endif
#ifdef BUILD_XDAMAGE
#include <X11/extensions/Xdamage.h>
#endif

#include <cstdint>
#include <functional>
#include <vector>

// TODO: remove lua requirement from x11_init_window
#include "../lua/llua.h"

#include "../geometry.h"
#include "gui.h"
#include "x11-event.h"

#define ATOM(a) XInternAtom(display, #a, False)

/// @brief Display where conky is placed
extern Display *display;
/// @brief Screen with conky
extern int screen;

constexpr int argb8888_color_depth = 32;

#ifndef BUILD_XDAMAGE
using Damage = XID;
using XserverRegion = XID;
#endif

struct conky_x11_window {
  /// XID of x11 root window
  Window root;
  /// XID of Conky window
  Window window;
  /// XID of DE desktop window (or root if none)
  Window desktop;
  Drawable drawable;
  Visual *visual;
  Colormap colourmap;
  GC gc;

  /// Background opacity of the window (0-255).
  ///
  /// This is set directly by lua settings - read them directly from
  /// backend-agnostic code.
  uint8_t opacity;
  /// Window buffer color depth.
  ///
  /// Value `32` means X11 supports ARGB8888 (has a compositor).
  /// Value `24` means only full background transparency is supported (no
  /// compositor). Value `0` means `CopyFromParent`, i.e. default value, which
  /// is always `24`.
  ///
  /// It can be something other than those 3 values (e.g. monochrome displays),
  /// but that's exceedingly rare.
  uint8_t color_depth;

  // Mask containing all events captured by conky
  int64_t event_mask;

  /// XInput2 event base index; 0 if unavailable.
  int xdamage_event_base = 0;
  /// XDamage error base index; 0 if unavailable.
  int xdamage_error_base = 0;

  /// Client-side region accumulating areas needing repaint (from Expose
  /// events).
  Region repaint_region = 0;

  /// XDamage handle for conky's window; `None` if XDamage is unavailable.
  Damage window_damage = 0;
  /// Server-side region accumulating damaged areas reported by XDamage.
  XserverRegion damage_region = 0;
  /// Server-side scratch region holding a single XDamageNotify area before it
  /// is unioned into `damage_region`.
  XserverRegion damage_scratch = 0;

#ifdef BUILD_XDBE
  XdbeBackBuffer back_buffer;
#else  /*BUILD_XDBE*/
  Pixmap back_buffer;
#endif /*BUILD_XDBE*/
#ifdef BUILD_XFT
  XftDraw *xftdraw;
#endif /*BUILD_XFT*/
  /// XInput2 extension opcode; 0 if unavailable.
  std::int32_t xi_opcode;

  /// @brief Window geometry in screen coordinate space
  conky::rect<int> geometry;

  bool cursor_over_window = false;
};

extern struct conky_x11_window window;

void update_x11_resource_db(bool first_run = false);
void update_x11_workarea();
void init_x11();
void destroy_window(void);
void create_gc(void);
void set_transparent_background(conky_x11_window *win);
void get_x11_desktop_info(Display *current_display, Atom atom);
/// @brief Sets reserved area atoms for the conky window to avoid other windows
/// covering it.
///
/// Prints out a warning if user is using one of sessions that are known not to
/// work.
void set_struts();
void x11_init_window(lua::state &l, bool own);
void deinit_x11();

/// @brief Forwards argument event to the top-most window at event positon that
/// isn't conky.
///
/// Calling this function is time sensitive as it will query window at event
/// position **at invocation time**.
/// @param event event to forward
/// @param cookie optional cookie data
void propagate_x11_event(conky::x11::event &event);

/// @brief Returns a list of window values for the given atom.
/// @param display display with which the atom is associated
/// @param window window to query for the atom value
/// @param atom atom to query for
/// @return a list of window values for the given atom
std::vector<Window> x11_atom_window_list(Display *display, Window window,
                                         Atom atom);

/// @brief Tries getting a list of windows ordered from bottom to top.
///
/// Whether the list is correctly ordered depends on WM/DE providing the
/// `_NET_CLIENT_LIST_STACKING` atom. If only `_NET_CLIENT_LIST` is defined,
/// this function assumes the WM/DE is a tiling one without stacking order.
///
/// If neither of the atoms are provided, this function tries traversing the
/// window graph in order to collect windows. In this case, map state of windows
/// is ignored.
///
/// @param display which display to query for windows
/// @param eager fallback to very slow tree traversal to ensure a list of
/// windows is returned even if window list atoms aren't defined
/// @return a (likely) ordered list of windows
std::vector<Window> query_x11_windows(Display *display, bool eager = false);

/// @brief Finds the last ascendant of a window (trunk) before root.
///
/// If provided `child` is root or has no windows between root and itself, the
/// `child` is returned.
///
/// @param display display of parent
/// @param child window whose parents to query
/// @return the top level ascendant window
Window query_x11_top_parent(Display *display, Window child);

/// @brief Returns the top-most window overlapping provided screen coordinates.
///
/// @param display display of parent
/// @param x screen X position contained by window
/// @param y screen Y position contained by window
/// @param device_id pointer device id to be queried
/// @return a top-most window at provided screen coordinates, or root
Window query_x11_window_at_pos(Display *display, conky::vec2i pos,
                               int device_id);

/// @brief Returns a list of windows overlapping provided screen coordinates.
///
/// Vector returned by this function will never contain root because it's
/// assumed to always cover the entire display.
///
/// @param display display of parent
/// @param x screen X position contained by window
/// @param y screen Y position contained by window
/// @param predicate any additional predicates to apply for XWindowAttributes
/// (besides bounds testing).
/// @return a vector of windows at provided screen coordinates
std::vector<Window> query_x11_windows_at_pos(
    Display *display, conky::vec2i pos,
    std::function<bool(XWindowAttributes &)> predicate =
        [](XWindowAttributes &a) { return true; },
    bool eager = false);

#ifdef BUILD_XDBE
void xdbe_swap_buffers(void);
#else
void xpmdb_swap_buffers(void);
#endif /* BUILD_XDBE */

#endif /* CONKY_X11_H */
