// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef SRC_UI_EXAMPLES_SHADERTOY_SERVICE_SHADERTOY_STATE_H_
#define SRC_UI_EXAMPLES_SHADERTOY_SERVICE_SHADERTOY_STATE_H_

// clang-format off
#include "src/ui/lib/glm_workaround/glm_workaround.h"
// clang-format on

#include <fuchsia/examples/shadertoy/cpp/fidl.h>
#include <fuchsia/images/cpp/fidl.h>
#include <lib/zx/eventpair.h>
#include "src/lib/fxl/memory/ref_counted.h"
#include "src/lib/fxl/memory/weak_ptr.h"

#include "src/ui/lib/escher/escher.h"
#include "src/ui/lib/escher/resources/resource.h"
#include "src/ui/lib/escher/util/stopwatch.h"

namespace shadertoy {

class Compiler;
class Pipeline;
class Renderer;
class App;
using PipelinePtr = fxl::RefPtr<Pipeline>;

// Core implementation of the Shadertoy API.  Subclasses must provide some
// functionality, such as the method for obtaining a framebuffer to render into.
class ShadertoyState : public escher::Resource {
 public:
  // Factory constructor.
  static fxl::RefPtr<ShadertoyState> NewForImagePipe(
      App* app, fidl::InterfaceHandle<fuchsia::images::ImagePipe> image_pipe);

  // Factory constructor.
  static fxl::RefPtr<ShadertoyState> NewForView(App* app, zx::eventpair view_token,
                                                bool handle_input_events);

  virtual ~ShadertoyState();

  void SetPaused(bool paused);

  void SetShaderCode(fidl::StringPtr glsl,
                     fuchsia::examples::shadertoy::Shadertoy::SetShaderCodeCallback callback);

  void SetResolution(uint32_t width, uint32_t height);

  void SetMouse(glm::vec4 i_mouse);

  void SetImage(uint32_t channel, fidl::InterfaceRequest<fuchsia::images::ImagePipe> request);

 protected:
  explicit ShadertoyState(App* app);

  // Tell the app to close the connection to this Shadertoy, and destroy it.
  void Close();

  // Subclasses must call this from DrawFrame().
  void OnFramePresented(fuchsia::images::PresentationInfo info);

  uint32_t width() const { return width_; }
  uint32_t height() const { return height_; }
  escher::Escher* escher() const { return escher_.get(); }
  Renderer* renderer() const { return renderer_; }
  const PipelinePtr& pipeline() const { return pipeline_; }
  escher::Texture* channel0() const { return nullptr; }
  escher::Texture* channel1() const { return nullptr; }
  escher::Texture* channel2() const { return nullptr; }
  escher::Texture* channel3() const { return nullptr; }
  glm::vec4 i_mouse() const { return i_mouse_; }
  fxl::WeakPtrFactory<ShadertoyState>* weak_ptr_factory() { return &weak_ptr_factory_; }

 private:
  // Subclasses must implement this, and call OnFramePresented() from it.
  virtual void DrawFrame(uint64_t presentation_time, float animation_time) = 0;

  // Requests a frame to be drawn.
  void RequestFrame(uint64_t presentation_time);

  // Subclasses must implement this to react when the resolution changes.
  virtual void OnSetResolution() = 0;

  static constexpr uint32_t kMaxWidth = 2048;
  static constexpr uint32_t kMaxHeight = 2048;

  App* const app_;
  const escher::EscherWeakPtr escher_;
  Compiler* const compiler_;
  Renderer* const renderer_;
  fxl::WeakPtrFactory<ShadertoyState> weak_ptr_factory_;
  PipelinePtr pipeline_;
  uint32_t width_ = 0;
  uint32_t height_ = 0;
  glm::vec4 i_mouse_ = {0, 0, 0, 0};
  bool is_paused_ = true;
  bool is_drawing_ = false;
  bool is_closed_ = false;
  escher::Stopwatch stopwatch_;
};

}  // namespace shadertoy

#endif  // SRC_UI_EXAMPLES_SHADERTOY_SERVICE_SHADERTOY_STATE_H_
