/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef MOZ_DMABUF_FORMATS_H_
#define MOZ_DMABUF_FORMATS_H_

#include "nsTArray.h"
#include "mozilla/gfx/Types.h"

#ifdef MOZ_WAYLAND
struct zwp_linux_dmabuf_v1;
struct zwp_linux_dmabuf_feedback_v1;
struct wl_surface;
#endif

namespace mozilla::widget {

class DMABufFormatTable;
class DMABufFeedback;

#ifndef DRM_FORMAT_MOD_INVALID
#  define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
#endif

// DRMFormat (fourcc) and available modifiers for it.
// Modifiers are sorted from the most preffered one.
class DRMFormat final {
 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DRMFormat)

  explicit DRMFormat(uint32_t aFormat) : mFormat(aFormat) {};
  DRMFormat(uint32_t aFormat, uint64_t aModifier) : mFormat(aFormat) {
    mModifiers.AppendElement(aModifier);
  }
  DRMFormat(uint32_t aFormat, const nsTArray<uint64_t>& aModifiers)
      : mFormat(aFormat), mModifiers(aModifiers.Clone()) {};
  DRMFormat(const DRMFormat& aSrc)
      : mFormat(aSrc.mFormat), mModifiers(aSrc.mModifiers.Clone()) {}
  uint32_t GetFormat() const { return mFormat; }
  bool Matches(uint32_t aFormat) const { return mFormat == aFormat; }
  bool IsFormatModifierSupported(uint64_t aModifier) const {
    return mModifiers.Contains(aModifier);
  }
  void AddModifier(uint64_t aModifier) {
    MOZ_ASSERT(!IsFormatModifierSupported(aModifier), "Added modifier twice?");
    mModifiers.AppendElement(aModifier);
  }
  bool UseModifiers() const {
    // Don't use modifiers if we don't have any or we have an invalid one.
    return !(mModifiers.IsEmpty() || (mModifiers.Length() == 1 &&
                                      mModifiers[0] == DRM_FORMAT_MOD_INVALID));
  }
  const uint64_t* GetModifiers(uint32_t& aModifiersNum) {
    aModifiersNum = mModifiers.Length();
    return mModifiers.Elements();
  }
  nsTArray<uint64_t>* GetModifiers() { return &mModifiers; }

 private:
  ~DRMFormat() = default;

  uint32_t mFormat = 0;
  AutoTArray<uint64_t, 15> mModifiers;
};

class DMABufFormats;
using DMABufFormatsCallback = std::function<void(DMABufFormats*)>;

class DMABufFormats final {
 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DMABufFormats)

#ifdef MOZ_WAYLAND
  void InitFeedback(zwp_linux_dmabuf_v1* aDMABuf,
                    const DMABufFormatsCallback& aFormatRefreshCB,
                    wl_surface* aSurface = nullptr);
  void InitV3(zwp_linux_dmabuf_v1* aDMABuf);
  void InitV3Done();
#endif

  DMABufFeedback* GetDMABufFeedback() { return mDMABufFeedback.get(); }
  DMABufFeedback* GetPendingDMABufFeedback();
  void PendingDMABufFeedbackDone();

  DRMFormat* GetFormat(uint32_t aFormat, bool aRequestScanoutFormat = false);
  DMABufFormats();

  void EnsureBasicFormats();

 private:
  ~DMABufFormats();

  void EnsureBasicFormat(uint32_t aDrmFourcc);

  DMABufFormatsCallback mFormatRefreshCallback;
#ifdef MOZ_WAYLAND
  zwp_linux_dmabuf_feedback_v1* mWaylandFeedback = nullptr;
#endif
  UniquePtr<DMABufFeedback> mDMABufFeedback;
  UniquePtr<DMABufFeedback> mPendingDMABufFeedback;
};

#ifdef MOZ_WAYLAND
RefPtr<DMABufFormats> CreateDMABufFeedbackFormats(
    wl_surface* aSurface,
    const std::function<void(DMABufFormats*)>& aFormatRefreshCB = nullptr);
#endif

class GlobalDMABufFormats final {
 public:
  DRMFormat* GetDRMFormat(int32_t aFOURCCFormat);

  GlobalDMABufFormats();

  bool SupportsDirectComposition(mozilla::gfx::SurfaceFormat aFormat) const;

 private:
  void LoadFormatModifiers();
  void SetModifiersToGfxVars();
  void GetModifiersFromGfxVars();

  bool ConfigureFormat(RefPtr<DMABufFormats> aFormats,
                       RefPtr<DRMFormat>& aTargetFormat, uint32_t aDrmFourcc);

  // Formats passed to RDD process to WebGL process
  // where we can't get formats/modifiers from Wayland display.
  // RGBA formats are mandatory, YUM optional (for direct HDR composition only).
  RefPtr<DRMFormat> mFormatRGBA;
  RefPtr<DRMFormat> mFormatRGBX;
  RefPtr<DRMFormat> mFormatP010;
  RefPtr<DRMFormat> mFormatNV12;
  RefPtr<DRMFormat> mFormatYUV420;
};

GlobalDMABufFormats* GetGlobalDMABufFormats();

}  // namespace mozilla::widget

#endif  // MOZ_DMABUF_FORMATS_H_
