/* 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 MOZILLA_LAYERS_KNOWSCOMPOSITOR
#define MOZILLA_LAYERS_KNOWSCOMPOSITOR

#include "mozilla/layers/LayersTypes.h"  // for LayersBackend
#include "mozilla/layers/CompositorTypes.h"
#include "mozilla/DataMutex.h"
#include "mozilla/layers/SyncObject.h"

namespace mozilla::layers {

class TextureForwarder;
class LayersIPCActor;
class ImageBridgeChild;

/**
 * An abstract interface for classes that are tied to a specific Compositor
 * across IPDL and uses TextureFactoryIdentifier to describe this Compositor.
 */
class KnowsCompositor {
 public:
  NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING

  KnowsCompositor();
  virtual ~KnowsCompositor();

  void IdentifyTextureHost(const TextureFactoryIdentifier& aIdentifier);

  // The sync object for the global content device.
  RefPtr<SyncObjectClient> GetSyncObject() {
    auto lock = mData.Lock();
    if (lock.ref().mSyncObject) {
      lock.ref().mSyncObject->EnsureInitialized();
    }
    return lock.ref().mSyncObject;
  }

  /// And by "thread-safe" here we merely mean "okay to hold strong references
  /// to from multiple threads". Not all methods actually are thread-safe.
  virtual bool IsThreadSafe() const { return true; }

  virtual RefPtr<KnowsCompositor> GetForMedia() {
    return RefPtr<KnowsCompositor>(this);
  }

  int32_t GetMaxTextureSize() const {
    auto lock = mData.Lock();
    return lock.ref().mTextureFactoryIdentifier.mMaxTextureSize;
  }

  /**
   * Returns the type of backend that is used off the main thread.
   * We only don't allow changing the backend type at runtime so this value can
   * be queried once and will not change until Gecko is restarted.
   */
  LayersBackend GetCompositorBackendType() const {
    auto lock = mData.Lock();
    return lock.ref().mTextureFactoryIdentifier.mParentBackend;
  }

  WebRenderCompositor GetWebRenderCompositorType() const {
    auto lock = mData.Lock();
    return lock.ref().mTextureFactoryIdentifier.mWebRenderCompositor;
  }

  bool SupportsTextureBlitting() const {
    auto lock = mData.Lock();
    return lock.ref().mTextureFactoryIdentifier.mSupportsTextureBlitting;
  }

  bool SupportsPartialUploads() const {
    auto lock = mData.Lock();
    return lock.ref().mTextureFactoryIdentifier.mSupportsPartialUploads;
  }

  bool SupportsComponentAlpha() const {
    auto lock = mData.Lock();
    return lock.ref().mTextureFactoryIdentifier.mSupportsComponentAlpha;
  }

  bool SupportsD3D11NV12() const {
    auto lock = mData.Lock();
    return lock.ref().mTextureFactoryIdentifier.mSupportsD3D11NV12;
  }

  bool SupportsD3D11() const {
    auto lock = mData.Lock();
    return SupportsD3D11(lock.ref().mTextureFactoryIdentifier);
  }

  static bool SupportsD3D11(
      const TextureFactoryIdentifier aTextureFactoryIdentifier) {
    return aTextureFactoryIdentifier.mParentBackend ==
               layers::LayersBackend::LAYERS_WR &&
           (aTextureFactoryIdentifier.mCompositorUseANGLE ||
            aTextureFactoryIdentifier.mWebRenderCompositor ==
                layers::WebRenderCompositor::D3D11);
  }

  bool GetCompositorUseANGLE() const {
    auto lock = mData.Lock();
    return lock.ref().mTextureFactoryIdentifier.mCompositorUseANGLE;
  }

  bool GetCompositorUseDComp() const {
    auto lock = mData.Lock();
    return lock.ref().mTextureFactoryIdentifier.mCompositorUseDComp;
  }

  bool GetUseLayerCompositor() const {
    auto lock = mData.Lock();
    return lock.ref().mTextureFactoryIdentifier.mUseLayerCompositor;
  }

  bool GetUseCompositorWnd() const {
    auto lock = mData.Lock();
    return lock.ref().mTextureFactoryIdentifier.mUseCompositorWnd;
  }

  WebRenderBackend GetWebRenderBackend() const {
    auto lock = mData.Lock();
    MOZ_ASSERT(lock.ref().mTextureFactoryIdentifier.mParentBackend ==
               layers::LayersBackend::LAYERS_WR);
    return lock.ref().mTextureFactoryIdentifier.mWebRenderBackend;
  }

  bool UsingHardwareWebRender() const {
    auto lock = mData.Lock();
    return lock.ref().mTextureFactoryIdentifier.mParentBackend ==
               layers::LayersBackend::LAYERS_WR &&
           lock.ref().mTextureFactoryIdentifier.mWebRenderBackend ==
               WebRenderBackend::HARDWARE;
  }

  bool UsingSoftwareWebRender() const {
    auto lock = mData.Lock();
    return lock.ref().mTextureFactoryIdentifier.mParentBackend ==
               layers::LayersBackend::LAYERS_WR &&
           lock.ref().mTextureFactoryIdentifier.mWebRenderBackend ==
               WebRenderBackend::SOFTWARE;
  }

  bool UsingSoftwareWebRenderD3D11() const {
    auto lock = mData.Lock();
    return lock.ref().mTextureFactoryIdentifier.mParentBackend ==
               layers::LayersBackend::LAYERS_WR &&
           lock.ref().mTextureFactoryIdentifier.mWebRenderBackend ==
               WebRenderBackend::SOFTWARE &&
           lock.ref().mTextureFactoryIdentifier.mWebRenderCompositor ==
               layers::WebRenderCompositor::D3D11;
  }

  bool UsingSoftwareWebRenderOpenGL() const {
    auto lock = mData.Lock();
    return lock.ref().mTextureFactoryIdentifier.mParentBackend ==
               layers::LayersBackend::LAYERS_WR &&
           lock.ref().mTextureFactoryIdentifier.mWebRenderBackend ==
               WebRenderBackend::SOFTWARE &&
           lock.ref().mTextureFactoryIdentifier.mWebRenderCompositor ==
               layers::WebRenderCompositor::OPENGL;
  }

  TextureFactoryIdentifier GetTextureFactoryIdentifier() const {
    auto lock = mData.Lock();
    return lock.ref().mTextureFactoryIdentifier;
  }

  int32_t GetSerial() const { return mSerial; }

  /**
   * Sends a synchronous ping to the compsoitor.
   *
   * This is bad for performance and should only be called as a last resort if
   * the compositor may be blocked for a long period of time, to avoid that the
   * content process accumulates resource allocations that the compositor is not
   * consuming and releasing.
   */
  virtual void SyncWithCompositor(
      const Maybe<uint64_t>& aWindowID = Nothing()) {
    MOZ_ASSERT_UNREACHABLE("Unimplemented");
  }

  /**
   * Helpers for finding other related interface. These are infallible.
   */
  virtual RefPtr<TextureForwarder> GetTextureForwarder() = 0;
  virtual LayersIPCActor* GetLayersIPCActor() = 0;

 protected:
  struct SharedData {
    TextureFactoryIdentifier mTextureFactoryIdentifier;
    RefPtr<SyncObjectClient> mSyncObject;
  };
  mutable DataMutex<SharedData> mData;

  const int32_t mSerial;
  static mozilla::Atomic<int32_t> sSerialCounter;
};

/// Some implementations of KnowsCompositor can be used off their IPDL thread
/// like the ImageBridgeChild, and others just can't. Instead of passing them
/// we create a proxy KnowsCompositor that has information about compositor
/// backend but proxies allocations to the ImageBridge.
/// This is kind of specific to the needs of media which wants to allocate
/// textures, usually through the Image Bridge accessed by KnowsCompositor but
/// also wants access to the compositor backend information that ImageBridge
/// doesn't know about.
///
/// This is really a band aid to what turned into a class hierarchy horror show.
/// Hopefully we can come back and simplify this some way.
class KnowsCompositorMediaProxy : public KnowsCompositor {
 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(KnowsCompositorMediaProxy, override);

  explicit KnowsCompositorMediaProxy(
      const TextureFactoryIdentifier& aIdentifier);

  RefPtr<TextureForwarder> GetTextureForwarder() override;

  LayersIPCActor* GetLayersIPCActor() override;

  void SyncWithCompositor(
      const Maybe<uint64_t>& aWindowID = Nothing()) override;

 protected:
  virtual ~KnowsCompositorMediaProxy();

  RefPtr<ImageBridgeChild> mThreadSafeAllocator;
};

}  // namespace mozilla::layers

#endif
