
#pragma once

#include <list>

#include <cstdint>

#include <waypp/waypp.h>

class WestonCapture;

class WestonCaptureObserver {
 public:
  virtual ~WestonCaptureObserver() = default;

  virtual void notify_weston_capture_format(
      void* data,
      struct weston_capture_source_v1* weston_capture_source_v1,
      uint32_t drm_format) = 0;

  virtual void notify_weston_capture_size(
      void* data,
      weston_capture_source_v1* weston_capture_source_v1,
      int32_t width,
      int32_t height) = 0;

  virtual void notify_weston_capture_complete(
      void* data,
      weston_capture_source_v1* weston_capture_source_v1) = 0;

  virtual void notify_weston_capture_retry(
      void* data,
      weston_capture_source_v1* weston_capture_source_v1) = 0;

  virtual void notify_weston_capture_failed(
      void* data,
      weston_capture_source_v1* weston_capture_source_v1,
      const char* msg) = 0;
};

class WestonCapture {
 public:
  WestonCapture(weston_capture_v1* weston_capture_v1,
                wl_output* output,
                weston_capture_v1_source source,
                WestonCaptureObserver* observer = nullptr,
                void* user_data = nullptr);

  ~WestonCapture();

  void register_observer(WestonCaptureObserver* observer) {
    observers_.push_back(observer);
  }

  void unregister_observer(WestonCaptureObserver* observer) {
    observers_.remove(observer);
  }

  [[nodiscard]] void* get_user_data() const { return user_data_; }

  void set_user_data(void* user_data) { user_data_ = user_data; }

 private:
  weston_capture_v1* weston_capture_v1_;
  wl_output* wl_output_;
  uint32_t source_;
  void* user_data_;

  weston_capture_source_v1* weston_capture_source_v1_;
  std::list<WestonCaptureObserver*> observers_{};

  /**
   * pixel format for a buffer
   *
   * This event delivers the pixel format that should be used for
   * the image buffer. Any buffer is incompatible if it does not have
   * this pixel format.
   *
   * The format modifier is linear (DRM_FORMAT_MOD_LINEAR).
   *
   * This is an initial event, and sent whenever the required format
   * changes.
   * @param drm_format DRM pixel format code
   */
  static void handle_format(void* data,
                            weston_capture_source_v1* weston_capture_source_v1,
                            uint32_t drm_format);

  /**
   * dimensions for a buffer
   *
   * This event delivers the size that should be used for the image
   * buffer. Any buffer is incompatible if it does not have this
   * size.
   *
   * Row alignment of the buffer must be 4 bytes, and it must not
   * contain further row padding. Otherwise the buffer is
   * unsupported.
   *
   * This is an initial event, and sent whenever the required size
   * changes.
   * @param width width in pixels
   * @param height height in pixels
   */
  static void handle_size(void* data,
                          weston_capture_source_v1* weston_capture_source_v1,
                          int32_t width,
                          int32_t height);

  /**
   * capture has completed
   *
   * This event is emitted as a response to 'capture' request when
   * it has successfully completed.
   *
   * If the buffer used in the shot is a dmabuf, the client also
   * needs to wait for any implicit fences on it before accessing the
   * contents.
   */
  static void handle_complete(
      void* data,
      weston_capture_source_v1* weston_capture_source_v1);

  /**
   * retry image capture with a different buffer
   *
   * This event is emitted as a response to 'capture' request when
   * it cannot succeed due to an incompatible buffer. The client has
   * already received the events delivering the new buffer
   * parameters. The client should retry the capture with the new
   * buffer parameters.
   */
  static void handle_retry(
      void* data,
      struct weston_capture_source_v1* weston_capture_source_v1);

  /**
   * capture failed
   *
   * This event is emitted as a response to 'capture' request when
   * it has failed for reasons other than an incompatible buffer. The
   * reasons may include: unsupported buffer type, unsupported buffer
   * stride, unsupported image source, the image source (output) was
   * removed, or compositor policy denied the capture.
   *
   * The string 'msg' may contain a human-readable explanation of the
   * failure to aid debugging.
   * @param msg human-readable hint
   */
  static void handle_failed(
      void* data,
      struct weston_capture_source_v1* weston_capture_source_v1,
      const char* msg);

  static constexpr struct weston_capture_source_v1_listener listener_ = {
      .format = handle_format,
      .size = handle_size,
      .complete = handle_complete,
      .retry = handle_retry,
      .failed = handle_failed,
  };
};
