// SPDX-FileCopyrightText: Copyright (c) Copyright 2021 NVIDIA Corporation
// SPDX-License-Identifier: BSD-3-Clause

#ifndef vtknvindex_volumemapper_h
#define vtknvindex_volumemapper_h

#include <map>
#include <set>
#include <string>

#include <nv/index/icamera.h>
#include <nv/index/icolormap.h>
#include <nv/index/iconfig_settings.h>
#include <nv/index/iscene.h>
#include <nv/index/isession.h>
#include <nv/index/iviewport.h>

#include "vtkCamera.h"
#include "vtkIndeXRepresentationsModule.h"
#include "vtkMultiProcessController.h"
#include "vtkSmartVolumeMapper.h"
#include "vtkTransform.h"

#include "vtknvindex_colormap_utility.h"
#include "vtknvindex_performance_values.h"
#include "vtknvindex_rtc_kernel_params.h"
#include "vtknvindex_scene.h"

class vtknvindex_cluster_properties;
class vtknvindex_instance;

// The class vtknvindex_volumemapper is responsible for all NVIDIA IndeX data preparation, the scene
// creation,
// the update and rendering of regular volumes.

class VTKINDEXREPRESENTATIONS_EXPORT vtknvindex_volumemapper : public vtkSmartVolumeMapper
{
public:
  static vtknvindex_volumemapper* New();
  vtkTypeMacro(vtknvindex_volumemapper, vtkSmartVolumeMapper);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  vtknvindex_volumemapper();
  ~vtknvindex_volumemapper();

  // Get dataset bounding box.
  double* GetBounds() override;
  using Superclass::GetBounds;

  // Set the whole volume bounds.
  void set_whole_bounds(const mi::math::Bbox<mi::Float64, 3>& bounds);

  // Shutdown forward loggers, NVIDIA IndeX library and unload libraries.
  void shutdown();

  // Overriding from vtkSmartVolumeMapper.
  void Render(vtkRenderer* ren, vtkVolume* vol) override;

  // Prepare data for the importer.
  bool prepare_data(mi::Sint32 time_step);

  // Get the local host id from NVIDIA IndeX running on this machine.
  mi::Sint32 get_local_hostid();

  // Set the cluster properties from Representation.
  void set_cluster_properties(vtknvindex_cluster_properties* cluster_properties);

  // Returns true if NVIDIA IndeX is initialized by this mapper.
  bool is_mapper_initialized() { return m_is_mapper_initialized; }

  // Update render canvas.
  void update_canvas(vtkRenderer* ren);

  // The configuration settings needs to be updated on changes applied in the GUI.
  void config_settings_changed();

  // The volume opacity needs to be updated on changes applied in the GUI.
  void opacity_changed();

  // Slices need to be updated on changes applied in the GUI.
  void slices_changed();

  // The CUDA code need to be updated on changes applied in the GUI.
  bool rtc_kernel_changed(vtknvindex_rtc_kernels kernel, const std::string& kernel_program,
    const void* params_buffer, mi::Uint32 buffer_size);

  // Initialize the mapper.
  bool initialize_mapper(vtkVolume* vol);

  // Set/get caching state.
  void is_caching(bool is_caching);
  bool is_caching() const;

  // Set volume visibility
  void set_visibility(bool visibility);

  // Region of interest used when cropping is disabled and "roi" is not empty. For
  // backward-compatibility with old state files.
  void set_region_of_interest_deprecated(const mi::math::Bbox<mi::Float32, 3>& roi);

private:
  vtknvindex_volumemapper(const vtknvindex_volumemapper&) = delete;
  void operator=(const vtknvindex_volumemapper&) = delete;

  // Is data prepared for given time step?
  bool is_data_prepared(mi::Sint32 time_step);

  // Converts scalar array to a compatible format, returns nullptr if no conversion necessary or
  // possible.
  vtkDataArray* convert_scalar_array(vtkDataArray* scalar_array) const;

  bool m_is_caching;              // True when ParaView is caching data on animation loops.
  bool m_is_mapper_initialized;   // True if mapper was initialized.
  bool m_config_settings_changed; // True if some parameter changed on the GUI.
  bool m_opacity_changed;         // True if volume opacity changed.
  bool m_slices_changed;          // True if any slice parameter changed.
  bool m_volume_changed;          // True when switching between properties.

  bool m_rtc_kernel_changed; // True when switching between CUDA code.
  bool m_rtc_param_changed;  // True when a kernel parameter changed.

  vtkMTimeType m_last_MTime;     // last MTime when volume was modified
  std::string m_prev_array_name; // Volume array name used for last rendering.
  int m_prev_vector_component;   // Vector component used for last rendering.
  int m_prev_vector_mode;        // Vector mode used for last rendering.

  std::map<mi::Sint32, bool>
    m_time_step_data_prepared; // Is data for given frame ready for importer?
  vtknvindex_scene m_scene;    // NVIDIA IndeX scene.
  vtknvindex_cluster_properties* m_cluster_properties; // Cluster properties gathered from ParaView.
  vtknvindex_performance_values m_performance_values;  // Performance values logger.
  vtkMultiProcessController* m_controller;             // MPI controller from ParaView.
  bool m_is_mpi_rendering;          // True when multiple MPI ranks are used for rendering.
  vtkDataArray* m_scalar_array;     // Scalar array containing actual data.
  std::vector<void*> m_subset_ptrs; // Array with pointers to scalars data for all time steps.

  // Converted scalar arrays for all time steps, m_subset_ptrs will point to their scalar data.
  // Entries will be null if no conversion took place (and m_scalar_array can be used directly).
  std::vector<vtkSmartPointer<vtkDataArray>> m_converted_scalar_arrays;

  mi::Float64 m_whole_bounds[6];                                  // Whole volume bounds.
  mi::math::Bbox<mi::Float32, 3> m_region_of_interest_deprecated; // For backward-compatibility.
  bool m_region_of_interest_deprecated_warning_printed;           // A warning was already printed.

  std::set<std::string> m_data_array_warning_printed; // A warning was already printed for these.

  vtknvindex_rtc_params_buffer m_volume_rtc_kernel; // The CUDA code applied to the current volume.

  vtknvindex_instance* m_index_instance; // Global index instance pointer.
};

#endif
