/* Copyright 2021 NVIDIA Corporation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *  * Neither the name of NVIDIA CORPORATION nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#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.
  void 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);

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);

  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_property; // Volume property that was rendered.

  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.

  mi::Float64 m_whole_bounds[6]; // Whole volume bounds.

  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
