text: #pragma once
  
  #include <AK/HashMap.h>
  #include <AK/HashTable.h>
  #include <AK/InlineLinkedList.h>
  #include <AK/WeakPtr.h>
  #include <LibCore/CConfigFile.h>
  #include <LibCore/CElapsedTimer.h>
  #include <LibDraw/Color.h>
  #include <LibDraw/DisjointRectSet.h>
  #include <LibDraw/Painter.h>
  #include <LibDraw/Palette.h>
  #include <LibDraw/Rect.h>
  #include <WindowServer/WSCursor.h>
  #include <WindowServer/WSEvent.h>
  #include <WindowServer/WSMenuBar.h>
  #include <WindowServer/WSMenuManager.h>
  #include <WindowServer/WSWindow.h>
  #include <WindowServer/WSWindowSwitcher.h>
  #include <WindowServer/WSWindowType.h>
  
  class WSScreen;
  class WSMenuBar;
  class WSMouseEvent;
  class WSClientWantsToPaintMessage;
  class WSWindow;
  class WSClientConnection;
  class WSWindowSwitcher;
  class GraphicsBitmap;
  class WSButton;
  
  enum class ResizeDirection {
      None,
      Left,
      UpLeft,
      Up,
      UpRight,
      Right,
      DownRight,
      Down,
      DownLeft
  };
  
  class WSWindowManager : public CObject {
      C_OBJECT(WSWindowManager)
  
      friend class WSCompositor;
      friend class WSWindowFrame;
      friend class WSWindowSwitcher;
  
  public:
      static WSWindowManager& the();
  
      explicit WSWindowManager(const Palette&);
      virtual ~WSWindowManager() override;
  
      const Palette& palette() const { return *m_palette; }
  
      RefPtr<CConfigFile> wm_config() const
      {
          return m_wm_config;
      }
      void reload_config(bool);
  
      void add_window(WSWindow&);
      void remove_window(WSWindow&);
  
      void notify_title_changed(WSWindow&);
      void notify_rect_changed(WSWindow&, const Rect& oldRect, const Rect& newRect);
      void notify_minimization_state_changed(WSWindow&);
      void notify_opacity_changed(WSWindow&);
      void notify_occlusion_state_changed(WSWindow&);
      void notify_client_changed_app_menubar(WSClientConnection&);
  
      Rect maximized_window_rect(const WSWindow&) const;
  
      WSClientConnection* dnd_client() { return m_dnd_client.ptr(); }
      const String& dnd_text() const { return m_dnd_text; }
      const String& dnd_data_type() const { return m_dnd_data_type; }
      const String& dnd_data() const { return m_dnd_data; }
      const GraphicsBitmap* dnd_bitmap() const { return m_dnd_bitmap; }
      Rect dnd_rect() const;
  
      void start_dnd_drag(WSClientConnection&, const String& text, GraphicsBitmap*, const String& data_type, const String& data);
      void end_dnd_drag();
  
      WSWindow* active_window() { return m_active_window.ptr(); }
      const WSClientConnection* active_client() const;
      bool active_window_is_modal() const { return m_active_window && m_active_window->is_modal(); }
  
      WSWindow* highlight_window() { return m_highlight_window.ptr(); }
      void set_highlight_window(WSWindow*);
  
      void move_to_front_and_make_active(WSWindow&);
  
      void draw_window_switcher();
  
      WSMenuManager& menu_manager() { return m_menu_manager; }
      const WSMenuManager& menu_manager() const { return m_menu_manager; }
  
      Rect menubar_rect() const;
      WSMenuBar* current_menubar() { return m_current_menubar.ptr(); }
      void set_current_menubar(WSMenuBar*);
      WSMenu* system_menu() { return m_system_menu.ptr(); }
  
      const WSCursor& active_cursor() const;
      const WSCursor& arrow_cursor() const { return *m_arrow_cursor; }
      const WSCursor& hand_cursor() const { return *m_hand_cursor; }
      const WSCursor& resize_horizontally_cursor() const { return *m_resize_horizontally_cursor; }
      const WSCursor& resize_vertically_cursor() const { return *m_resize_vertically_cursor; }
      const WSCursor& resize_diagonally_tlbr_cursor() const { return *m_resize_diagonally_tlbr_cursor; }
      const WSCursor& resize_diagonally_bltr_cursor() const { return *m_resize_diagonally_bltr_cursor; }
      const WSCursor& i_beam_cursor() const { return *m_i_beam_cursor; }
      const WSCursor& disallowed_cursor() const { return *m_disallowed_cursor; }
      const WSCursor& move_cursor() const { return *m_move_cursor; }
      const WSCursor& drag_cursor() const { return *m_drag_cursor; }
  
      void invalidate(const WSWindow&);
      void invalidate(const WSWindow&, const Rect&);
      void invalidate(const Rect&);
      void invalidate();
      void flush(const Rect&);
  
      const Font& font() const;
      const Font& window_title_font() const;
      const Font& menu_font() const;
      const Font& app_menu_font() const;
  
      void close_menu(WSMenu&);
      void close_menubar(WSMenuBar&);
      Color menu_selection_color() const { return m_menu_selection_color; }
      int menubar_menu_margin() const;
  
      void set_resolution(int width, int height);
  
      void set_active_window(WSWindow*);
      void set_hovered_button(WSButton*);
  
      WSButton* cursor_tracking_button() { return m_cursor_tracking_button.ptr(); }
      void set_cursor_tracking_button(WSButton*);
  
      void set_resize_candidate(WSWindow&, ResizeDirection);
      void clear_resize_candidate();
      ResizeDirection resize_direction_of_window(const WSWindow&);
  
      bool any_opaque_window_contains_rect(const Rect&);
      bool any_opaque_window_above_this_one_contains_rect(const WSWindow&, const Rect&);
  
      void tell_wm_listeners_window_state_changed(WSWindow&);
      void tell_wm_listeners_window_icon_changed(WSWindow&);
      void tell_wm_listeners_window_rect_changed(WSWindow&);
  
      void start_window_resize(WSWindow&, const Point&, MouseButton);
      void start_window_resize(WSWindow&, const WSMouseEvent&);
  
      const WSWindow* active_fullscreen_window() const { return (m_active_window && m_active_window->is_fullscreen()) ? m_active_window : nullptr; }
      WSWindow* active_fullscreen_window() { return (m_active_window && m_active_window->is_fullscreen()) ? m_active_window : nullptr; }
  
      template<typename Callback>
      void for_each_active_menubar_menu(Callback callback)
      {
          if (callback(*m_system_menu) == IterationDecision::Break)
              return;
          if (m_current_menubar)
              m_current_menubar->for_each_menu(callback);
      }
  
      WSMenu* find_internal_menu_by_id(int);
  
      int theme_index() const { return m_theme_index; }
  
  private:
      NonnullRefPtr<WSCursor> get_cursor(const String& name);
      NonnullRefPtr<WSCursor> get_cursor(const String& name, const Point& hotspot);
  
      void process_mouse_event(WSMouseEvent&, WSWindow*& hovered_window);
      void process_event_for_doubleclick(WSWindow& window, WSMouseEvent& event);
      void deliver_mouse_event(WSWindow& window, WSMouseEvent& event);
      bool process_ongoing_window_resize(const WSMouseEvent&, WSWindow*& hovered_window);
      bool process_ongoing_window_move(WSMouseEvent&, WSWindow*& hovered_window);
      bool process_ongoing_drag(WSMouseEvent&, WSWindow*& hovered_window);
      void start_window_move(WSWindow&, const WSMouseEvent&);
      void set_hovered_window(WSWindow*);
      template<typename Callback>
      IterationDecision for_each_visible_window_of_type_from_back_to_front(WSWindowType, Callback, bool ignore_highlight = false);
      template<typename Callback>
      IterationDecision for_each_visible_window_of_type_from_front_to_back(WSWindowType, Callback, bool ignore_highlight = false);
      template<typename Callback>
      IterationDecision for_each_visible_window_from_front_to_back(Callback);
      template<typename Callback>
      IterationDecision for_each_visible_window_from_back_to_front(Callback);
      template<typename Callback>
      void for_each_window_listening_to_wm_events(Callback);
      template<typename Callback>
      void for_each_window(Callback);
  
      virtual void event(CEvent&) override;
      void paint_window_frame(const WSWindow&);
      void tell_wm_listener_about_window(WSWindow& listener, WSWindow&);
      void tell_wm_listener_about_window_icon(WSWindow& listener, WSWindow&);
      void tell_wm_listener_about_window_rect(WSWindow& listener, WSWindow&);
      void pick_new_active_window();
  
      void recompute_occlusions();
  
      RefPtr<WSCursor> m_arrow_cursor;
      RefPtr<WSCursor> m_hand_cursor;
      RefPtr<WSCursor> m_resize_horizontally_cursor;
      RefPtr<WSCursor> m_resize_vertically_cursor;
      RefPtr<WSCursor> m_resize_diagonally_tlbr_cursor;
      RefPtr<WSCursor> m_resize_diagonally_bltr_cursor;
      RefPtr<WSCursor> m_i_beam_cursor;
      RefPtr<WSCursor> m_disallowed_cursor;
      RefPtr<WSCursor> m_move_cursor;
      RefPtr<WSCursor> m_drag_cursor;
  
      Color m_background_color;
      Color m_active_window_border_color;
      Color m_active_window_border_color2;
      Color m_active_window_title_color;
      Color m_inactive_window_border_color;
      Color m_inactive_window_border_color2;
      Color m_inactive_window_title_color;
      Color m_moving_window_border_color;
      Color m_moving_window_border_color2;
      Color m_moving_window_title_color;
      Color m_highlight_window_border_color;
      Color m_highlight_window_border_color2;
      Color m_highlight_window_title_color;
  
      InlineLinkedList<WSWindow> m_windows_in_order;
  
      struct DoubleClickInfo {
          struct ClickMetadata {
              CElapsedTimer clock;
              Point last_position;
          };
  
          ClickMetadata& metadata_for_button(MouseButton);
  
          void reset()
          {
              m_left = {};
              m_right = {};
              m_middle = {};
          }
  
          WeakPtr<WSWindow> m_clicked_window;
  
      private:
          ClickMetadata m_left;
          ClickMetadata m_right;
          ClickMetadata m_middle;
      };
      DoubleClickInfo m_double_click_info;
      int m_double_click_speed { 0 };
      int m_max_distance_for_double_click { 4 };
  
      WeakPtr<WSWindow> m_active_window;
      WeakPtr<WSWindow> m_hovered_window;
      WeakPtr<WSWindow> m_highlight_window;
      WeakPtr<WSWindow> m_active_input_window;
  
      WeakPtr<WSWindow> m_move_window;
      Point m_move_origin;
      Point m_move_window_origin;
  
      WeakPtr<WSWindow> m_resize_window;
      WeakPtr<WSWindow> m_resize_candidate;
      MouseButton m_resizing_mouse_button { MouseButton::None };
      Rect m_resize_window_original_rect;
      Point m_resize_origin;
      ResizeDirection m_resize_direction { ResizeDirection::None };
  
      u8 m_keyboard_modifiers { 0 };
  
      RefPtr<WSMenu> m_system_menu;
      Color m_menu_selection_color;
      WeakPtr<WSMenuBar> m_current_menubar;
  
      int m_theme_index { 0 };
  
      WSWindowSwitcher m_switcher;
      WSMenuManager m_menu_manager;
  
      WeakPtr<WSButton> m_cursor_tracking_button;
      WeakPtr<WSButton> m_hovered_button;
  
      NonnullRefPtr<Palette> m_palette;
  
      RefPtr<CConfigFile> m_wm_config;
  
      struct AppMetadata {
          String executable;
          String name;
          String icon_path;
          String category;
      };
      Vector<AppMetadata> m_apps;
      HashMap<String, NonnullRefPtr<WSMenu>> m_app_category_menus;
  
      struct ThemeMetadata {
          String name;
          String path;
      };
      Vector<ThemeMetadata> m_themes;
      RefPtr<WSMenu> m_themes_menu;
  
      WeakPtr<WSClientConnection> m_dnd_client;
      String m_dnd_text;
      String m_dnd_data_type;
      String m_dnd_data;
      RefPtr<GraphicsBitmap> m_dnd_bitmap;
  };
  
  template<typename Callback>
  IterationDecision WSWindowManager::for_each_visible_window_of_type_from_back_to_front(WSWindowType type, Callback callback, bool ignore_highlight)
  {
      bool do_highlight_window_at_end = false;
      for (auto& window : m_windows_in_order) {
          if (!window.is_visible())
              continue;
          if (window.is_minimized())
              continue;
          if (window.type() != type)
              continue;
          if (!ignore_highlight && m_highlight_window == &window) {
              do_highlight_window_at_end = true;
              continue;
          }
          if (callback(window) == IterationDecision::Break)
              return IterationDecision::Break;
      }
      if (do_highlight_window_at_end) {
          if (callback(*m_highlight_window) == IterationDecision::Break)
              return IterationDecision::Break;
      }
      return IterationDecision::Continue;
  }
  
  template<typename Callback>
  IterationDecision WSWindowManager::for_each_visible_window_from_back_to_front(Callback callback)
  {
      if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Normal, callback) == IterationDecision::Break)
          return IterationDecision::Break;
      if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Taskbar, callback) == IterationDecision::Break)
          return IterationDecision::Break;
      if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Tooltip, callback) == IterationDecision::Break)
          return IterationDecision::Break;
      if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Menubar, callback) == IterationDecision::Break)
          return IterationDecision::Break;
      if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Menu, callback) == IterationDecision::Break)
          return IterationDecision::Break;
      return for_each_visible_window_of_type_from_back_to_front(WSWindowType::WindowSwitcher, callback);
  }
  
  template<typename Callback>
  IterationDecision WSWindowManager::for_each_visible_window_of_type_from_front_to_back(WSWindowType type, Callback callback, bool ignore_highlight)
  {
      if (!ignore_highlight && m_highlight_window && m_highlight_window->type() == type && m_highlight_window->is_visible()) {
          if (callback(*m_highlight_window) == IterationDecision::Break)
              return IterationDecision::Break;
      }
  
      for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) {
          if (!window->is_visible())
              continue;
          if (window->is_minimized())
              continue;
          if (window->type() != type)
              continue;
          if (!ignore_highlight && window == m_highlight_window)
              continue;
          if (callback(*window) == IterationDecision::Break)
              return IterationDecision::Break;
      }
      return IterationDecision::Continue;
  }
  
  template<typename Callback>
  IterationDecision WSWindowManager::for_each_visible_window_from_front_to_back(Callback callback)
  {
      if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::WindowSwitcher, callback) == IterationDecision::Break)
          return IterationDecision::Break;
      if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Menu, callback) == IterationDecision::Break)
          return IterationDecision::Break;
      if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Menubar, callback) == IterationDecision::Break)
          return IterationDecision::Break;
      if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Taskbar, callback) == IterationDecision::Break)
          return IterationDecision::Break;
      if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Tooltip, callback) == IterationDecision::Break)
          return IterationDecision::Break;
      return for_each_visible_window_of_type_from_front_to_back(WSWindowType::Normal, callback);
  }
  
  template<typename Callback>
  void WSWindowManager::for_each_window_listening_to_wm_events(Callback callback)
  {
      for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) {
          if (!window->listens_to_wm_events())
              continue;
          if (callback(*window) == IterationDecision::Break)
              return;
      }
  }
  
  template<typename Callback>
  void WSWindowManager::for_each_window(Callback callback)
  {
      for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) {
          if (callback(*window) == IterationDecision::Break)
              return;
      }
  }
  