/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkParallelCoordinatesRepresentation.h

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*-------------------------------------------------------------------------
  Copyright 2009 Sandia Corporation.
  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
  the U.S. Government retains certain rights in this software.
-------------------------------------------------------------------------*/
/**
 * @class   vtkParallelCoordinatesRepresentation
 * @brief   Data representation that
 *  takes generic multivariate data and produces a parallel coordinates plot.
 *
 *
 *  A parallel coordinates plot represents each variable in a multivariate
 *  data set as a separate axis.  Individual samples of that data set are
 *  represented as a polyline that pass through each variable axis at
 *  positions that correspond to data values.  vtkParallelCoordinatesRepresentation
 *  generates this plot when added to a vtkParallelCoordinatesView, which handles
 *  interaction and highlighting.  Sample polylines can alternatively
 *  be represented as s-curves by enabling the UseCurves flag.
 *
 *  There are three selection modes: lasso, angle, and function. Lasso selection
 *  picks sample lines that pass through a polyline.  Angle selection picks sample
 *  lines that have similar slope to a line segment.  Function selection picks
 *  sample lines that are near a linear function defined on two variables.  This
 *  function specified by passing two (x,y) variable value pairs.
 *
 *  All primitives are plotted in normalized view coordinates [0,1].
 *
 * @sa
 *  vtkParallelCoordinatesView vtkParallelCoordinatesHistogramRepresentation
 *  vtkSCurveSpline
 *
 * @par Thanks:
 *  Developed by David Feng at Sandia National Laboratories
 */

#ifndef vtkParallelCoordinatesRepresentation_h
#define vtkParallelCoordinatesRepresentation_h

#include "vtkRenderedRepresentation.h"
#include "vtkViewsInfovisModule.h" // For export macro

class vtkActor;
class vtkActor2D;
class vtkArrayData;
class vtkAxisActor2D;
class vtkBivariateLinearTableThreshold;
class vtkCollection;
class vtkCoordinate;
class vtkFieldData;
class vtkDataArray;
class vtkDataObject;
class vtkDoubleArray;
class vtkIdList;
class vtkIdTypeArray;
class vtkIntArray;
class vtkLookupTable;
class vtkOutlineCornerSource;
class vtkPoints;
class vtkPolyData;
class vtkPolyDataMapper2D;
class vtkPropCollection;
class vtkSelection;
class vtkSelectionNode;
class vtkTextMapper;
class vtkTimeStamp;
class vtkUnsignedIntArray;
class vtkViewport;
class vtkWindow;

class VTKVIEWSINFOVIS_EXPORT vtkParallelCoordinatesRepresentation : public vtkRenderedRepresentation
{
public:
  static vtkParallelCoordinatesRepresentation* New();
  vtkTypeMacro(vtkParallelCoordinatesRepresentation, vtkRenderedRepresentation);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  /**
   * Apply the theme to this view.  CellColor is used for line coloring
   * and titles.  EdgeLabelColor is used for axis color. CellOpacity is
   * used for line opacity.
   */
  void ApplyViewTheme(vtkViewTheme* theme) override;

  /**
   * Returns the hover text at an x,y location.
   */
  virtual std::string GetHoverString(vtkView* view, int x, int y);

  ///@{
  /**
   * Change the position of the plot
   */
  int SetPositionAndSize(double* position, double* size);
  int GetPositionAndSize(double* position, double* size);
  ///@}

  ///@{
  /**
   * Set/Get the axis titles
   */
  void SetAxisTitles(vtkStringArray*);
  void SetAxisTitles(vtkAlgorithmOutput*);
  ///@}

  /**
   * Set the title for the entire plot
   */
  void SetPlotTitle(const char*);

  ///@{
  /**
   * Get the number of axes in the plot
   */
  vtkGetMacro(NumberOfAxes, int);
  ///@}

  ///@{
  /**
   * Get the number of samples in the plot
   */
  vtkGetMacro(NumberOfSamples, int);
  ///@}

  ///@{
  /**
   * Set/Get the number of labels to display on each axis
   */
  void SetNumberOfAxisLabels(int num);
  vtkGetMacro(NumberOfAxisLabels, int);
  ///@}

  ///@{
  /**
   * Move an axis to a particular screen position.  Using these
   * methods requires an Update() before they will work properly.
   */
  virtual int SwapAxisPositions(int position1, int position2);
  int SetXCoordinateOfPosition(int position, double xcoord);
  double GetXCoordinateOfPosition(int axis);
  void GetXCoordinatesOfPositions(double* coords);
  int GetPositionNearXCoordinate(double xcoord);
  ///@}

  ///@{
  /**
   * Whether or not to display using curves
   */
  vtkSetMacro(UseCurves, vtkTypeBool);
  vtkGetMacro(UseCurves, vtkTypeBool);
  vtkBooleanMacro(UseCurves, vtkTypeBool);
  ///@}

  ///@{
  /**
   * Resolution of the curves displayed, enabled by setting UseCurves
   */
  vtkSetMacro(CurveResolution, int);
  vtkGetMacro(CurveResolution, int);
  ///@}

  ///@{
  /**
   * Access plot properties
   */
  vtkGetMacro(LineOpacity, double);
  vtkGetMacro(FontSize, double);
  vtkGetVector3Macro(LineColor, double);
  vtkGetVector3Macro(AxisColor, double);
  vtkGetVector3Macro(AxisLabelColor, double);
  vtkSetMacro(LineOpacity, double);
  vtkSetMacro(FontSize, double);
  vtkSetVector3Macro(LineColor, double);
  vtkSetVector3Macro(AxisColor, double);
  vtkSetVector3Macro(AxisLabelColor, double);
  ///@}

  ///@{
  /**
   * Maximum angle difference (in degrees) of selection using angle/function brushes
   */
  vtkSetMacro(AngleBrushThreshold, double);
  vtkGetMacro(AngleBrushThreshold, double);
  ///@}

  ///@{
  /**
   * Maximum angle difference (in degrees) of selection using angle/function brushes
   */
  vtkSetMacro(FunctionBrushThreshold, double);
  vtkGetMacro(FunctionBrushThreshold, double);
  ///@}

  ///@{
  /**
   * Set/get the value range of the axis at a particular screen position
   */
  int GetRangeAtPosition(int position, double range[2]);
  virtual int SetRangeAtPosition(int position, double range[2]);
  ///@}

  /**
   * Reset the axes to their default positions and orders
   */
  void ResetAxes();

  ///@{
  /**
   * Do a selection of the lines.  See the main description for how to use these functions.
   * RangeSelect is currently stubbed out.
   */
  virtual void LassoSelect(int brushClass, int brushOperator, vtkPoints* brushPoints);
  virtual void AngleSelect(int brushClass, int brushOperator, double* p1, double* p2);
  virtual void FunctionSelect(
    int brushClass, int brushOperator, double* p1, double* p2, double* q1, double* q2);
  virtual void RangeSelect(int brushClass, int brushOperator, double* p1, double* p2);
  ///@}

  enum InputPorts
  {
    INPUT_DATA = 0,
    INPUT_TITLES,
    NUM_INPUT_PORTS
  };

protected:
  vtkParallelCoordinatesRepresentation();
  ~vtkParallelCoordinatesRepresentation() override;

  int FillInputPortInformation(int port, vtkInformation* info) override;

  int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override;

  ///@{
  /**
   * Add/remove the props and actors to/from a view
   */
  bool AddToView(vtkView* view) override;
  bool RemoveFromView(vtkView* view) override;
  void PrepareForRendering(vtkRenderView* view) override;
  ///@}

  /**
   * This function is not actually used, but as left as a stub in case
   * it becomes useful at some point.
   */
  void UpdateHoverHighlight(vtkView* view, int x, int y);

  /**
   * Allocate the cells/points/scalars for a vtkPolyData
   */
  virtual int AllocatePolyData(vtkPolyData* polyData, int numLines, int numPointsPerLine,
    int numStrips, int numPointsPerStrip, int numQuads, int numPoints, int numCellScalars,
    int numPointScalars);

  /**
   * Put the axis actors in their correct positions.
   */
  int PlaceAxes();

  ///@{
  /**
   * Place line primitives into a vtkPolyData from the input data.  idsToPlot
   * is a list of which rows/samples should be plotted.  If nullptr, all
   * rows/samples are plotted.
   */
  virtual int PlaceLines(vtkPolyData* polyData, vtkTable* data, vtkIdTypeArray* idsToPlot);
  virtual int PlaceCurves(vtkPolyData* polyData, vtkTable* data, vtkIdTypeArray* idsToPlot);
  ///@}

  /**
   * Takes the selection list (assumed to be a vtkIdTypeArray) from a vtkSelectionNode
   * and plots lines/curves into polyData for just those row/sample ids.
   */
  virtual int PlaceSelection(
    vtkPolyData* polyData, vtkTable* data, vtkSelectionNode* selectionNode);

  /**
   * Compute the number of axes and their individual ranges
   */
  virtual int ComputeDataProperties();

  /**
   * Set plot actor properties (line thickness, opacity, etc)
   */
  virtual int UpdatePlotProperties(vtkStringArray* inputTitles);

  /**
   * Delete and reallocate the internals, resetting to default values
   */
  virtual int ReallocateInternals();

  ///@{
  /**
   * Compute which screen position a point belongs to (returns the left position)
   */
  int ComputePointPosition(double* p);
  int ComputeLinePosition(double* p1, double* p2);
  ///@}

  ///@{
  /**
   * Select a set of points using the prescribed operator (add, subtract, etc.) and class
   */
  virtual void SelectRows(vtkIdType brushClass, vtkIdType brushOperator, vtkIdTypeArray* rowIds);
  vtkSelection* ConvertSelection(vtkView* view, vtkSelection* selection) override;
  virtual void BuildInverseSelection();
  virtual vtkPolyDataMapper2D* InitializePlotMapper(
    vtkPolyData* input, vtkActor2D* actor, bool forceStandard = false);
  ///@}

  /**
   * Build an s-curve passing through (0,0) and (1,1) with a specified number of
   * values.  This is used as a lookup table when plotting curved primitives.
   */
  void BuildDefaultSCurve(vtkDoubleArray* array, int numValues);

  /**
   * same as public version, but assumes that the brushpoints coming in
   * are all within two neighboring axes.
   */
  virtual void LassoSelectInternal(vtkPoints* brushPoints, vtkIdTypeArray* outIds);

  /**
   * todo
   */
  virtual void UpdateSelectionActors();

  vtkPolyDataMapper2D* GetSelectionMapper(int idx);
  int GetNumberOfSelections();

  vtkSmartPointer<vtkPolyData> PlotData;
  vtkSmartPointer<vtkPolyDataMapper2D> PlotMapper;
  vtkSmartPointer<vtkActor2D> PlotActor;
  vtkSmartPointer<vtkTextMapper> PlotTitleMapper;
  vtkSmartPointer<vtkActor2D> PlotTitleActor;
  vtkSmartPointer<vtkTextMapper> FunctionTextMapper;
  vtkSmartPointer<vtkActor2D> FunctionTextActor;

  vtkSmartPointer<vtkSelection> InverseSelection;
  vtkSmartPointer<vtkBivariateLinearTableThreshold> LinearThreshold;

  class Internals;
  Internals* I;

  int NumberOfAxes;
  int NumberOfAxisLabels;
  int NumberOfSamples;
  double YMin;
  double YMax;

  int CurveResolution;
  vtkTypeBool UseCurves;
  double AngleBrushThreshold;
  double FunctionBrushThreshold;
  double SwapThreshold;

  // Indexed by screen position
  double* Xs;
  double* Mins;
  double* Maxs;
  double* MinOffsets;
  double* MaxOffsets;

  vtkSmartPointer<vtkAxisActor2D>* Axes;
  vtkSmartPointer<vtkTable> InputArrayTable;
  vtkSmartPointer<vtkStringArray> AxisTitles;

  vtkTimeStamp BuildTime;

  double LineOpacity;
  double FontSize;
  double LineColor[3];
  double AxisColor[3];
  double AxisLabelColor[3];

  vtkGetStringMacro(InternalHoverText);
  vtkSetStringMacro(InternalHoverText);
  char* InternalHoverText;

private:
  vtkParallelCoordinatesRepresentation(const vtkParallelCoordinatesRepresentation&) = delete;
  void operator=(const vtkParallelCoordinatesRepresentation&) = delete;
};

#endif
