// Copyright 2025 DeepMind Technologies Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_SCENE_BRIDGE_H_
#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_SCENE_BRIDGE_H_

#include <functional>
#include <memory>
#include <optional>
#include <vector>

#include <math/mat4.h>
#include <math/vec3.h>
#include <mujoco/mjrfilament.h>
#include <mujoco/mjvisualize.h>
#include <mujoco/mujoco.h>
#include "experimental/filament/compat/scene_objects.h"
#include "render/filament/mjrfilament_cpp.h"
#include "render/filament/support/model_objects.h"
#include "render/filament/support/light_manager.h"

namespace mujoco {

// Manages all mjModel data and updates a SceneView using an mjvScene.
class SceneBridge {
 public:
  SceneBridge(mjrfContext* ctx, const mjModel* model);
  ~SceneBridge();

  // Updates the Entities in the filament Scene to match the current mjvScene
  // state.
  void Update(const mjrRect& viewport, const mjvScene* scene);

  // Creates the filament objects from the mjModel.
  void UploadMesh(const mjModel* model, int id);
  void UploadTexture(const mjModel* model, int id);
  void UploadHeightField(const mjModel* model, int id);

  using DrawTextAtFn = std::function<void(const char*, float, float, float)>;
  void SetDrawTextFunction(DrawTextAtFn fn);

  // Returns the managed scene.
  mjrfScene* GetScene() const;
  mjrCamera GetCamera() const;

  SceneBridge(const SceneBridge&) = delete;
  SceneBridge& operator=(const SceneBridge&) = delete;

 private:
  // Converts a point in world space to clip space, eg. in the range [-1,-1, 0]
  // to [1, 1, 1]. Returns std::nullopt if the point is behind the camera.
  std::optional<filament::math::float3> ClipFromWorld(
      const filament::math::float3& pos) const;

  mjrfContext* ctx_ = nullptr;
  std::unique_ptr<ModelObjects> model_objects_;
  std::unique_ptr<SceneObjects> scene_objects_;
  std::unique_ptr<LightManager> light_manager_;
  mjrCamera camera_;
  DrawTextAtFn draw_text_callback_;
  UniquePtr<mjrfScene> scene_{nullptr, nullptr};
  std::vector<UniquePtr<mjrfRenderable>> renderables_;
  filament::math::mat4 clip_from_world_;
};

}  // namespace mujoco

#endif  // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_SCENE_BRIDGE_H_
