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

 Program:   Visualization Toolkit
 Module:    vtkAMRSliceFilter.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.

 =========================================================================*/
/**
 * @class   vtkAMRSliceFilter
 *
 *
 *  A concrete instance of vtkOverlappingAMRAlgorithm which implements
 *  functionality for extracting slices from AMR data. Unlike the conventional
 *  slice filter, the output of this filter is a 2-D AMR dataset itself.
 */

#ifndef vtkAMRSliceFilter_h
#define vtkAMRSliceFilter_h

#include "vtkFiltersAMRModule.h" // For export macro
#include "vtkOverlappingAMRAlgorithm.h"

#include <vector> // For STL vector

class vtkInformation;
class vtkInformationVector;
class vtkOverlappingAMR;
class vtkMultiProcessController;
class vtkPlane;
class vtkAMRBox;
class vtkUniformGrid;

class VTKFILTERSAMR_EXPORT vtkAMRSliceFilter
    : public vtkOverlappingAMRAlgorithm {
public:
  static vtkAMRSliceFilter* New();
  vtkTypeMacro(vtkAMRSliceFilter, vtkOverlappingAMRAlgorithm);
  void PrintSelf(ostream &os, vtkIndent indent) override;

  // Inline Gettters & Setters

  //@{
  /**
   * Set/Get the offset-from-origin of the slicing plane.
   */
  vtkSetMacro(OffsetFromOrigin, double);
  vtkGetMacro(OffsetFromOrigin, double);
  //@}

  //@{
  /**
   * Set/Get the maximum resolution used in this instance.
   */
  vtkSetMacro(MaxResolution, unsigned int);
  vtkGetMacro(MaxResolution, unsigned int);
  //@}

  /**
   * Tags to identify normals along the X, Y and Z directions.
   */
  enum NormalTag : char { X_NORMAL = 1, Y_NORMAL = 2, Z_NORMAL = 4 };

  //@{
  /**
   * Set/Get the Axis normal. The accpetable values are defined in the
   * NormalTag enum.
   */
  vtkSetMacro(Normal, int);
  vtkGetMacro(Normal, int);
  //@}

  //@{
  /**
   * Set/Get a multiprocess controller for paralle processing.
   * By default this parameter is set to nullptr by the constructor.
   */
  vtkSetMacro(Controller, vtkMultiProcessController *);
  vtkGetMacro(Controller, vtkMultiProcessController *);
  //@}

  // Standard Pipeline methods
  int RequestData(vtkInformation *, vtkInformationVector **,
                  vtkInformationVector *) override;
  int FillInputPortInformation(int port, vtkInformation *info) override;
  int FillOutputPortInformation(int port, vtkInformation *info) override;

  /**
   * Makes upstream request to a source, typically, a concrete instance of
   * vtkAMRBaseReader, for which blocks to load.
   */
  int RequestInformation(vtkInformation *rqst,
                         vtkInformationVector **inputVector,
                         vtkInformationVector *outputVector) override;

  /**
   * Performs upstream requests to the reader
   */
  int RequestUpdateExtent(vtkInformation *, vtkInformationVector **,
                          vtkInformationVector *) override;

protected:
  vtkAMRSliceFilter();
  ~vtkAMRSliceFilter() override = default;

  /**
   * Returns the cell index w.r.t. the given input grid which contains
   * the query point x. A -1 is returned if the point is not found.
   */
  int GetDonorCellIdx(double x[3], vtkUniformGrid *ug);

  /**
   * Returns the point index w.r.t. the given input grid which contains the
   * query point x. A -1 is returned if the point is not found.
   */
  int GetDonorPointIdx(double x[3], vtkUniformGrid *ug);

  /**
   * Computes the cell center of the cell corresponding to the supplied
   * cell index w.r.t. the input uniform grid.
   */
  void ComputeCellCenter(vtkUniformGrid *ug, const int cellIdx,
                         double centroid[3]);

  /**
   * Gets the slice from the given grid given the plane origin & the
   * user-supplied normal associated with this class instance.
   */
  vtkUniformGrid *GetSlice(double origin[3], int *dims, double *gorigin,
                           double *spacing);

  /**
   * Copies the cell data for the cells in the slice from the 3-D grid.
   */
  void GetSliceCellData(vtkUniformGrid *slice, vtkUniformGrid *grid3D);

  /**
   * Copies the point data for the cells in the slice from the 3-D grid.
   */
  void GetSlicePointData(vtkUniformGrid *slice, vtkUniformGrid *grid3D);

  /**
   * Determines if a plane intersects with an AMR box
   */
  bool PlaneIntersectsAMRBox(double plane[4], double bounds[6]);

  /**
   * Given the cut-plane and the metadata provided by a module upstream,
   * this method generates the list of linear AMR block indices that need
   * to be loaded.
   */
  void ComputeAMRBlocksToLoad(vtkPlane *p, vtkOverlappingAMR *metadata);

  /**
   * Extracts a 2-D AMR slice from the dataset.
   */
  void GetAMRSliceInPlane(vtkPlane *p, vtkOverlappingAMR *inp,
                          vtkOverlappingAMR *out);

  /**
   * A utility function that checks if the input AMR data is 2-D.
   */
  bool IsAMRData2D(vtkOverlappingAMR *input);

  /**
   * Returns the axis-aligned cut plane.
   */
  vtkPlane *GetCutPlane(vtkOverlappingAMR *input);

  double OffsetFromOrigin;
  int Normal;
  unsigned int MaxResolution;
  vtkMultiProcessController *Controller;

  std::vector<int> BlocksToLoad;

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

#endif /* vtkAMRSliceFilter_h */
