#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
#define ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_

#include <pdx/file_handle.h>
#include <pdx/service.h>
#include <private/dvr/buffer_hub_queue_client.h>
#include <private/dvr/display_protocol.h>
#include <private/dvr/ring_buffer.h>

#include <functional>
#include <iterator>
#include <memory>
#include <string>
#include <vector>

#include "acquired_buffer.h"

namespace android {
namespace dvr {

class DisplayService;

enum class SurfaceType {
  Direct,
  Application,
};

class DisplaySurface : public pdx::Channel {
 public:
  static pdx::Status<std::shared_ptr<DisplaySurface>> Create(
      DisplayService* service, int surface_id, int process_id, int user_id,
      const display::SurfaceAttributes& attributes);

  ~DisplaySurface() override;

  DisplayService* service() const { return service_; }
  SurfaceType surface_type() const { return surface_type_; }
  int surface_id() const { return surface_id_; }
  int process_id() const { return process_id_; }
  int user_id() const { return user_id_; }

  bool visible() const { return visible_; }
  int z_order() const { return z_order_; }

  const display::SurfaceAttributes& attributes() const { return attributes_; }
  display::SurfaceUpdateFlags update_flags() const { return update_flags_; }

  virtual std::vector<int32_t> GetQueueIds() const { return {}; }

  bool IsUpdatePending() const {
    return update_flags_.value() != display::SurfaceUpdateFlags::None;
  }

 protected:
  DisplaySurface(DisplayService* service, SurfaceType surface_type,
                 int surface_id, int process_id, int user_id,
                 const display::SurfaceAttributes& attributes);

  // Utility to retrieve a shared pointer to this channel as the desired derived
  // type.
  template <
      typename T = DisplaySurface,
      typename = std::enable_if_t<std::is_base_of<DisplaySurface, T>::value>>
  std::shared_ptr<T> Self() {
    return std::static_pointer_cast<T>(shared_from_this());
  }

  virtual pdx::Status<pdx::LocalChannelHandle> OnCreateQueue(
      pdx::Message& message, size_t meta_size_bytes) = 0;

  // Registers a consumer queue with the event dispatcher in DisplayService. The
  // OnQueueEvent callback below is called to handle queue events.
  pdx::Status<void> RegisterQueue(
      const std::shared_ptr<ConsumerQueue>& consumer_queue);
  pdx::Status<void> UnregisterQueue(
      const std::shared_ptr<ConsumerQueue>& consumer_queue);

  // Called by the event dispatcher in DisplayService when a registered queue
  // event triggers. Executes on the event dispatcher thread.
  virtual void OnQueueEvent(
      const std::shared_ptr<ConsumerQueue>& consumer_queue, int events);

  void SurfaceUpdated(display::SurfaceUpdateFlags update_flags);
  void ClearUpdate();

  // Synchronizes access to mutable state below between message dispatch thread
  // and frame post thread.
  mutable std::mutex lock_;

 private:
  friend class DisplayService;
  friend class DisplayManagerService;

  // Dispatches display surface messages to the appropriate handlers. This
  // handler runs on the VrFlinger message dispatch thread.
  pdx::Status<void> HandleMessage(pdx::Message& message);

  pdx::Status<void> OnSetAttributes(
      pdx::Message& message, const display::SurfaceAttributes& attributes);
  pdx::Status<display::SurfaceInfo> OnGetSurfaceInfo(pdx::Message& message);

  DisplayService* service_;
  SurfaceType surface_type_;
  int surface_id_;
  int process_id_;
  int user_id_;

  display::SurfaceAttributes attributes_;
  display::SurfaceUpdateFlags update_flags_ = display::SurfaceUpdateFlags::None;

  // Subset of attributes that may be interpreted by the display service.
  bool visible_ = false;
  int z_order_ = 0;

  DisplaySurface(const DisplaySurface&) = delete;
  void operator=(const DisplaySurface&) = delete;
};

class ApplicationDisplaySurface : public DisplaySurface {
 public:
  ApplicationDisplaySurface(DisplayService* service, int surface_id,
                            int process_id, int user_id,
                            const display::SurfaceAttributes& attributes)
      : DisplaySurface(service, SurfaceType::Application, surface_id,
                       process_id, user_id, attributes) {}

  std::shared_ptr<ConsumerQueue> GetQueue(int32_t queue_id);
  std::vector<int32_t> GetQueueIds() const override;

 private:
  pdx::Status<pdx::LocalChannelHandle> OnCreateQueue(
      pdx::Message& message, size_t meta_size_bytes) override;
  void OnQueueEvent(const std::shared_ptr<ConsumerQueue>& consumer_queue,
                    int events) override;

  std::unordered_map<int32_t, std::shared_ptr<ConsumerQueue>> consumer_queues_;
};

class DirectDisplaySurface : public DisplaySurface {
 public:
  DirectDisplaySurface(DisplayService* service, int surface_id, int process_id,
                       int user_id,
                       const display::SurfaceAttributes& attributes)
      : DisplaySurface(service, SurfaceType::Direct, surface_id, process_id,
                       user_id, attributes),
        acquired_buffers_(kMaxPostedBuffers) {}
  bool IsBufferAvailable();
  bool IsBufferPosted();
  AcquiredBuffer AcquireCurrentBuffer();

  // Get the newest buffer. Up to one buffer will be skipped. If a buffer is
  // skipped, it will be stored in skipped_buffer if non null.
  AcquiredBuffer AcquireNewestAvailableBuffer(AcquiredBuffer* skipped_buffer);

 private:
  pdx::Status<pdx::LocalChannelHandle> OnCreateQueue(
      pdx::Message& message, size_t meta_size_bytes) override;
  void OnQueueEvent(const std::shared_ptr<ConsumerQueue>& consumer_queue,
                    int events) override;

  // The capacity of the pending buffer queue. Should be enough to hold all the
  // buffers of this DisplaySurface, although in practice only 1 or 2 frames
  // will be pending at a time.
  static constexpr int kSurfaceBufferMaxCount = 4;
  static constexpr int kSurfaceViewMaxCount = 4;
  static constexpr int kMaxPostedBuffers =
      kSurfaceBufferMaxCount * kSurfaceViewMaxCount;

  // Returns whether a frame is available without locking the mutex.
  bool IsFrameAvailableNoLock() const;

  // Dequeue all available buffers from the consumer queue.
  void DequeueBuffersLocked();

  // In a triple-buffered surface, up to kMaxPostedBuffers buffers may be
  // posted and pending.
  RingBuffer<AcquiredBuffer> acquired_buffers_;

  std::shared_ptr<ConsumerQueue> direct_queue_;
};

}  // namespace dvr
}  // namespace android

#endif  // ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_