/*=auto=========================================================================

  Portions (c) Copyright 2005 Brigham and Women's Hospital (BWH) All Rights Reserved.

  See COPYRIGHT.txt
  or http://www.slicer.org/copyright/copyright.txt for details.

  Program:   3D Slicer
  Module:    $RCSfile: vtkMRMLScalarVolumeDisplayNode.h,v $
  Date:      $Date: 2006/03/19 17:12:29 $
  Version:   $Revision: 1.3 $

=========================================================================auto=*/

#ifndef __vtkMRMLScalarVolumeDisplayNode_h
#define __vtkMRMLScalarVolumeDisplayNode_h

// MRML includes
#include "vtkMRMLVolumeDisplayNode.h"
#include "vtkImageMapToWindowLevelAddon.h"

// VTK includes
class vtkImageAlgorithm;
class vtkImageAppendComponents;
class vtkImageHistogramStatistics;
class vtkImageCast;
class vtkImageLogic;
class vtkImageMapToColors;
class vtkImageStencil;
class vtkImageThreshold;
class vtkImageExtractComponents;
class vtkImageMathematics;
class vtkScalarsToColors;

// STD includes
#include <vector>

/// \brief MRML node for representing a volume display attributes.
///
/// vtkMRMLScalarVolumeDisplayNode nodes describe how volume is displayed.
class VTK_MRML_EXPORT vtkMRMLScalarVolumeDisplayNode : public vtkMRMLVolumeDisplayNode
{
public:
  static vtkMRMLScalarVolumeDisplayNode* New();
  vtkTypeMacro(vtkMRMLScalarVolumeDisplayNode, vtkMRMLVolumeDisplayNode);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  vtkMRMLNode* CreateNodeInstance() override;

  ///
  /// Read node attributes from XML file
  void ReadXMLAttributes(const char** atts) override;

  ///
  /// Write this node's information to a MRML file in XML format.
  void WriteXML(ostream& of, int indent) override;

  /// Copy node content (excludes basic data, such as name and node references).
  /// \sa vtkMRMLNode::CopyContent
  vtkMRMLCopyContentMacro(vtkMRMLScalarVolumeDisplayNode);

  ///
  /// Get node XML tag name (like Volume, Model)
  const char* GetNodeTagName() override { return "VolumeDisplay"; }

  //--------------------------------------------------------------------------
  /// Display Information
  //--------------------------------------------------------------------------

  /// \deprecated
  bool GetWindowLevelLocked()
  {
    vtkWarningMacro(
      "vtkMRMLScalarVolumeDisplayNode::GetWindowLevelLocked method is deprecated. To check if the mouse mode cannot change window/level, get info from the interaction node. "
      "e.g. slicer.app.applicationLogic().GetInteractionNode().GetCurrentInteractionMode() == slicer.vtkMRMLInteractionNode.AdjustWindowLevel");
    return false;
  };

  /// \deprecated
  virtual void SetWindowLevelLocked(bool)
  {
    vtkWarningMacro("vtkMRMLScalarVolumeDisplayNode::SetWindowLevelLocked method is deprecated. To prevent the mouse from changing window/level, set the interaction node to "
                    "something other than AdjustWindowLevel. "
                    "e.g. slicer.app.applicationLogic().GetInteractionNode().SetCurrentInteractionMode(slicer.vtkMRMLInteractionNode.ViewTransform)");
  };

  ///
  /// Specifies whether windowing and leveling are to be performed automatically
  vtkBooleanMacro(AutoWindowLevel, int);
  vtkGetMacro(AutoWindowLevel, int);
  vtkSetMacro(AutoWindowLevel, int);

  ///
  /// The window value to use when autoWindowLevel is 'no'
  double GetWindow();
  virtual void SetWindow(double);

  ///
  /// The level value to use when autoWindowLevel is 'no'
  double GetLevel();
  virtual void SetLevel(double);

  /// Sets the window and level at once, generates only 1 modified event if
  /// needed.
  virtual void SetWindowLevel(double window, double level);
  /// Utility function that sets the window/level by passing the min and max
  /// of the window.
  void SetWindowLevelMinMax(double min, double max);

  /// Utility function that returns the minimum value of the window level
  double GetWindowLevelMin();
  /// Utility function that returns the maximum value of the window level
  double GetWindowLevelMax();

  ///
  /// Specifies whether to apply the threshold
  vtkBooleanMacro(ApplyThreshold, int);
  vtkGetMacro(ApplyThreshold, int);
  virtual void SetApplyThreshold(int);

  ///
  /// Specifies whether the threshold should be set automatically
  vtkBooleanMacro(AutoThreshold, int);
  vtkGetMacro(AutoThreshold, int);
  vtkSetMacro(AutoThreshold, int);

  ///
  /// The lower threshold value to use when autoThreshold is 'no'
  /// Defaults to VTK_SHORT_MIN
  virtual double GetLowerThreshold();
  virtual void SetLowerThreshold(double lower);

  ///
  /// The upper threshold value to use when autoThreshold is 'no'
  /// Defaults to VTK_SHORT_MAX
  virtual double GetUpperThreshold();
  virtual void SetUpperThreshold(double upper);

  virtual void SetThreshold(double lower, double upper);

  ///
  /// Set/Get interpolate reformatted slices
  vtkGetMacro(Interpolate, int);
  vtkSetMacro(Interpolate, int);
  vtkBooleanMacro(Interpolate, int);

  ///
  /// Set/Get invert order of colors
  /// If non-zero then colors at the end of the color table are assigned to low values.
  vtkGetMacro(InvertDisplayScalarRange, int);
  virtual void SetInvertDisplayScalarRange(int invert);
  vtkBooleanMacro(InvertDisplayScalarRange, int);

  /// Get or set the mapping method.
  /// The mapping method determines how scalar values are assigned an integer value within the colormap
  /// \sa WindowMappingMethodType
  vtkGetMacro(WindowMappingMethod, int);
  virtual void SetWindowMappingMethod(int method);

  /// Get the mapping method from/as a string
  /// \sa WindowMappingMethodType
  static int GetWindowMappingMethodFromString(const char* name);
  static const char* GetWindowMappingMethodAsString(int type);
  static vtkImageMapToWindowLevelAddon::WindowMappingMode GetWindowMappingMethodFromIndex(int idx);

  void SetDefaultColorMap() override;

  ///
  /// alternative method to propagate events generated in Display nodes
  void ProcessMRMLEvents(vtkObject* /*caller*/, unsigned long /*event*/, void* /*callData*/) override;

  /// Set the pipeline input
  void SetInputImageDataConnection(vtkAlgorithmOutput* imageDataConnection) override;

  /// Gets the pipeline input
  vtkAlgorithmOutput* GetInputImageDataConnection() override;

  /// Gets the pipeline output
  vtkAlgorithmOutput* GetOutputImageDataConnection() override;

  ///
  /// Get/set background mask stencil
  void SetBackgroundImageStencilDataConnection(vtkAlgorithmOutput* imageDataConnection) override;
  vtkAlgorithmOutput* GetBackgroundImageStencilDataConnection() override;

  ///
  /// Parse a window level preset string (window and level values separated by "|") to numeric values
  static bool GetWindowLevelFromPresetString(const char* preset, double& window, double& level);
  ///
  /// Convert window and level values to preset string (window and level values separated by "|")
  static std::string GetWindowLevelAsPresetString(double window, double level);
  ///
  /// Parse a string with window and level as double|double, and add a preset
  void AddWindowLevelPresetFromString(const char* preset);
  ///
  /// Add a window level preset
  void AddWindowLevelPreset(double window, double level);

  ///
  /// Remove all presets
  void ResetWindowLevelPresets();

  ///
  /// Set Window and Level from preset p
  void SetWindowLevelFromPreset(int p);

  ///
  /// Get the number of window/level presets
  int GetNumberOfWindowLevelPresets();

  ///
  /// Return a specific preset, returns 0 if p out of range
  double GetWindowPreset(int p);
  double GetLevelPreset(int p);

  ///
  /// Defines the expected range of the output data after
  /// having been mapped through the current display options
  /// If no input is set, then it searches the scene to find the associated
  /// Volume node and returns its image data scalar range.
  virtual void GetDisplayScalarRange(double range[2]);

  /// Get lookup table that is used for assigning colors to scalar values.
  /// The returned lookup table is managed by the display node object
  /// and its content should not be modified externally.
  virtual vtkScalarsToColors* GetLookupTable();

protected:
  vtkMRMLScalarVolumeDisplayNode();
  ~vtkMRMLScalarVolumeDisplayNode() override;
  vtkMRMLScalarVolumeDisplayNode(const vtkMRMLScalarVolumeDisplayNode&);
  void operator=(const vtkMRMLScalarVolumeDisplayNode&);

  void SetColorNodeInternal(vtkMRMLColorNode* newColorNode) override;
  void UpdateLookupTable(vtkMRMLColorNode* newColorNode);
  void CalculateAutoLevels();

  /// Return the image data with scalar type, it can be in the middle of the
  /// pipeline, it's typically the input of the Threshold/WindowLevel filters
  vtkImageData* GetScalarImageData();
  virtual vtkAlgorithmOutput* GetScalarImageDataConnection();

  void SetInputToImageDataPipeline(vtkAlgorithmOutput* imageDataConnection) override;

  ///
  /// To hold preset values for window and level, so can restore this display
  /// node's window and level to ones read from DICOM files, or defined by
  /// users
  class WindowLevelPreset
  {
  public:
    double Window;
    double Level;
    WindowLevelPreset(double window, double level)
      : Window(window)
      , Level(level)
    {
    }
  };
  // double Window;
  // double Level;
  // double UpperThreshold;
  // double LowerThreshold;

  /// Set the window level presets from the specified vector.
  /// Calls this->Modified() if there is a change.
  void SetWindowLevelPresets(const std::vector<WindowLevelPreset>& windowLevelPresets);

  /// Booleans
  int Interpolate;
  int AutoWindowLevel;
  int ApplyThreshold;
  int AutoThreshold;
  int InvertDisplayScalarRange;
  int WindowMappingMethod;

  vtkImageLogic* AlphaLogic;
  vtkImageMapToColors* MapToColors;
  vtkImageThreshold* Threshold;
  vtkImageAppendComponents* AppendComponents;
  vtkImageMapToWindowLevelAddon* MapToWindowLevelColors;
  vtkImageExtractComponents* ExtractRGB;
  vtkImageExtractComponents* ExtractAlpha;
  vtkImageStencil* MultiplyAlpha;

  ///
  /// window level presets
  std::vector<WindowLevelPreset> WindowLevelPresets;

  ///
  /// Used internally in CalculateScalarAutoLevels and CalculateStatisticsAutoLevels
  vtkImageHistogramStatistics* HistogramStatistics;
  bool IsInCalculateAutoLevels;
};

#endif
