/* Copyright 2018 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_representation_h
#define vtknvindex_representation_h

#include <map>

#include <mi/math/bbox.h>

#include "vtkImageVolumeRepresentation.h"

#include "vtknvindex_rtc_kernel_params.h"

#include "vtkIndeXRepresentationsModule.h"

class vtkMultiProcessController;

class vtknvindex_affinity;
class vtknvindex_config_settings;
class vtknvindex_cluster_properties;

// The class vtknvindex_cached_bounds collects and holds the dataset boundary data used for
// time variant datasets if all data is already cached and no more boundary
// information can be queried.

struct vtknvindex_cached_bounds
{
  double data_bounds[6];
  int whole_extent[6];
  double origin[3];
  double spacing[3];

  vtknvindex_cached_bounds();
  vtknvindex_cached_bounds(const vtknvindex_cached_bounds& cached_bound);
  vtknvindex_cached_bounds(const double _data_bounds[6], const int _whole_extent[6],
    const double _origin[3], const double _spacing[3]);
};

// The class vtknvindex_representation represents the base class for distributing and rendering
// of regular volume data (structured volume grids) in NVIDIA IndeX.

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

  // Overrides from vtkImageVolumeRepresentation.
  int ProcessViewRequest(vtkInformationRequestKey* request_type, vtkInformation* inInfo,
    vtkInformation* outInfo) override;

  bool AddToView(vtkView* view) override;
  bool RemoveFromView(vtkView* view) override;

  // Overrides for correct ghost cell.
  int RequestDataBase(vtkInformation* request, vtkInformationVector** inputVector,
    vtkInformationVector* outputVector);

  int RequestData(vtkInformation* request, vtkInformationVector** inputVector,
    vtkInformationVector* outputVector) override;

  // Overrides from vtkPVDataRepresentation.
  int RequestUpdateExtent(vtkInformation* request, vtkInformationVector** inputVector,
    vtkInformationVector* outputVector) override;

  //
  // Configuration options set by ParaView GUI.
  // These values are stored in an instance of the vtknvindex_config_settings class.
  //

  // NVIDIA IndeX's regular volume parameters.
  // Set subcube size.
  void set_subcube_size(unsigned x, unsigned y, unsigned z);

  // Set subcube border size.
  void set_subcube_border(int border);

  // Set filtering mode.
  void set_filter_mode(int filter_mode);

  // Set pre-integration mode.
  void set_preintegration(bool enable_preint);

  // Set volume step size.
  void set_volume_step_size(double step_size);

  // Set opacity reference mode.
  void set_opacity_reference_mode(int opacity_reference_mode);

  // Set opacity reference.
  void set_opacity_reference(double opacity_reference);

  // Set dump internal state of NVIDIA IndeX.
  void set_dump_internal_state(bool is_dump);

  // Set flag to log performance values.
  void set_log_performance(bool is_log);

  // Set slice parameters.
  void show_volume(bool enable);
  void set_selected_slice(int selected_slice);

  void enable_slice1(bool enable);
  void set_slice_mode1(int slice_mode);
  void set_slice_pos1(double pos);
  void enable_slice2(bool enable);
  void set_slice_mode2(int slice_mode);
  void set_slice_pos2(double pos);
  void enable_slice3(bool enable);
  void set_slice_mode3(int slice_mode);
  void set_slice_pos3(double pos);

  // Set CUDA code parameter.
  void set_volume_filter(int filter);

  // Set common lighting parameter.
  void set_light_type(int light_type);
  void set_light_angle(double light_angle);
  void set_light_elevation(double light_elevation);

  // Set common specular parameters.
  void set_surf_ambient(double ambient);
  void set_surf_specular(double specular);
  void set_surf_specular_power(double specular_power);

  // Set iso-surface parameters.
  void set_iso_min(double iso_min);
  void set_iso_max(double iso_max);
  void set_iso_fill_mode(int mode);
  void set_iso_use_shading(bool use_shading);

  // Set depth enhancement parameters.
  void set_depth_samples(int depth_samples);
  void set_depth_gamma(double gamma);

  // Set edge enhancement parameters.
  void set_edge_range(double edge_range);
  void set_edge_samples(int edge_samples);

  // Set region of interest.
  void update_index_roi();

  void set_roi_minI(double val);
  void set_roi_maxI(double val);
  vtkGetVector2Macro(m_roi_range_I, double);
  vtkSetVector2Macro(m_roi_range_I, double);

  void set_roi_minJ(double val);
  void set_roi_maxJ(double val);
  vtkGetVector2Macro(m_roi_range_J, double);
  vtkSetVector2Macro(m_roi_range_J, double);

  void set_roi_minK(double val);
  void set_roi_maxK(double val);
  vtkGetVector2Macro(m_roi_range_K, double);
  vtkSetVector2Macro(m_roi_range_K, double);

protected:
  vtknvindex_representation();
  ~vtknvindex_representation();

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

  vtknvindex_cached_bounds* get_cached_bounds(mi::Sint32 time);
  void set_cached_bounds(mi::Sint32 time);

  // Utility function for searching the lower bound time stamp inside a sorted time steps list.
  mi::Sint32 find_time_step(
    mi::Float64 time, const mi::Float64* time_steps, mi::Sint32 nb_tsteps) const;

  // Update current kernel
  void update_current_kernel();

  // For the GUI
  double m_roi_range_I[2];
  double m_roi_range_J[2];
  double m_roi_range_K[2];

  vtkMultiProcessController* m_controller;           // MPI controller from ParaView.
  vtknvindex_config_settings* m_app_config_settings; // Application side config settings.
  vtknvindex_cluster_properties*
    m_cluster_properties; // Cluster wide properties, refer class documentation.
  mi::math::Bbox_struct<mi::Float32, 3> m_roi_gui; // Region of interest set in the GUI.
  mi::math::Vector<mi::Uint32, 3> m_volume_size;   // Cached volume size.

  // backup of original Image Reduction Factors
  mi::Sint32 m_still_image_reduction_factor;
  mi::Sint32 m_interactive_image_reduction_factor;

  // boundary caching for time series
  mi::Sint32 m_has_time_steps;
  mi::Sint32 m_cur_time;
  std::map<mi::Sint32, vtknvindex_cached_bounds> m_time_to_cached_bounds;

  // rtc kernel params
  vtknvindex_isosurface_params m_isosurface_params;
  vtknvindex_depth_enhancement_params m_depth_enhancement_params;
  vtknvindex_edge_enhancement_params m_edge_enhancement_params;
};

// Schwartz counter to manage initialization.
class VTKINDEXREPRESENTATIONS_EXPORT vtknvindex_representation_initializer
{
public:
  vtknvindex_representation_initializer();
  ~vtknvindex_representation_initializer();

private:
  vtknvindex_representation_initializer(const vtknvindex_representation_initializer&) = delete;
  void operator=(const vtknvindex_representation_initializer&) = delete;
};
static vtknvindex_representation_initializer vtknvindex_representation_initializer_instance;
#endif // vtknvindex_representation_h
