// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-FileCopyrightText: Copyright (c) Sandia Corporation
// SPDX-License-Identifier: BSD-3-Clause

/**
 * @class   vtkPDistributedDataFilter
 * @brief   Distribute data among processors
 *
 *
 * This filter redistributes data among processors in a parallel
 * application into spatially contiguous vtkUnstructuredGrids.
 * The execution model anticipated is that all processes read in
 * part of a large vtkDataSet. Each process sets the input of
 * filter to be that DataSet. When executed, this filter builds
 * in parallel a k-d tree, decomposing the space occupied by the
 * distributed DataSet into spatial regions.  It assigns each
 * spatial region to a processor.  The data is then redistributed
 * and the output is a single vtkUnstructuredGrid containing the
 * cells in the process' assigned regions.
 *
 * This filter is sometimes called "D3" for "distributed data decomposition".
 *
 * Enhancement: You can set the k-d tree decomposition, rather than
 * have D3 compute it.  This allows you to divide a dataset using
 * the decomposition computed for another dataset.  Obtain a description
 * of the k-d tree cuts this way:
 *
 * @code{cpp}
 *    vtkBSPCuts *cuts = D3Object1->GetCuts()
 * @endcode
 *
 * And set it this way:
 *
 * @code{cpp}
 *     D3Object2->SetCuts(cuts)
 * @endcode
 *
 *
 * It is desirable to have a field array of global node IDs
 * for two reasons:
 *
 * 1. When merging together sub grids that were distributed
 *    across processors, global node IDs can be used to remove
 *    duplicate points and significantly reduce the size of the
 *    resulting output grid.  If no such array is available,
 *    D3 will use a tolerance to merge points, which is much
 *    slower.
 *
 * 2. If ghost cells have been requested, D3 requires a
 *    global node ID array in order to request and transfer
 *    ghost cells in parallel among the processors.  If there
 *    is no global node ID array, D3 will in parallel create
 *    a global node ID array, and the time to do this can be
 *    significant.
 *
 * D3 uses `vtkPointData::GetGlobalIds` to access global
 * node ids from the input. If none is found,
 * and ghost cells have been requested, D3 will create a
 * temporary global node ID array before acquiring ghost cells.
 *
 * It is also desirable to have global element IDs (vtkCellData::GetGlobalIds).
 * However, if they don't exist D3 can create them relatively quickly.
 *
 * @warning
 * The Execute() method must be called by all processes in the
 * parallel application, or it will hang.  If you are not certain
 * that your pipeline will execute identically on all processors,
 * you may want to use this filter in an explicit execution mode.
 *
 * @sa
 * vtkKdTree vtkPKdTree vtkBSPCuts
 */

#ifndef vtkPDistributedDataFilter_h
#define vtkPDistributedDataFilter_h

#include "vtkDistributedDataFilter.h"
#include "vtkFiltersParallelGeometryModule.h" // For export macro

VTK_ABI_NAMESPACE_BEGIN
class vtkBSPCuts;
class vtkDataArray;
class vtkFloatArray;
class vtkIdList;
class vtkIdTypeArray;
class vtkIntArray;
class vtkMultiProcessController;
class vtkPDistributedDataFilterSTLCloak;
class vtkPKdTree;
class vtkUnstructuredGrid;

class VTKFILTERSPARALLELGEOMETRY_EXPORT vtkPDistributedDataFilter : public vtkDistributedDataFilter
{
public:
  vtkTypeMacro(vtkPDistributedDataFilter, vtkDistributedDataFilter);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  static vtkPDistributedDataFilter* New();

protected:
  vtkPDistributedDataFilter();
  ~vtkPDistributedDataFilter() override;

  /**
   * Build a vtkUnstructuredGrid for a spatial region from the
   * data distributed across processes.  Execute() must be called
   * by all processes, or it will hang.
   */

  int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override;
  void SingleProcessExecute(vtkDataSet* input, vtkUnstructuredGrid* output);

  /**
   * Implementation for request data.
   */
  int RequestDataInternal(vtkDataSet* input, vtkUnstructuredGrid* output);

private:
  enum
  {
    DeleteNo = 0,
    DeleteYes = 1
  };

  enum
  {
    DuplicateCellsNo = 0,
    DuplicateCellsYes = 1
  };

  enum
  {
    GhostCellsNo = 0,
    GhostCellsYes = 1
  };

  enum
  {
    UnsetGhostLevel = 99
  };

  /**
   * ?
   */
  int PartitionDataAndAssignToProcesses(vtkDataSet* set);

  /**
   * ?
   */
  vtkUnstructuredGrid* RedistributeDataSet(
    vtkDataSet* set, vtkDataSet* input, int filterOutDuplicateCells);

  /**
   * ?
   */
  int ClipGridCells(vtkUnstructuredGrid* grid);

  /**
   * ?
   */
  vtkUnstructuredGrid* AcquireGhostCells(vtkUnstructuredGrid* grid);

  /**
   * ?
   */
  void ComputeMyRegionBounds();

  /**
   * ?
   */
  int CheckFieldArrayTypes(vtkDataSet* set);

  /**
   * If any processes have 0 cell input data sets, then
   * spread the input data sets around (quickly) before formal
   * redistribution.
   */
  vtkDataSet* TestFixTooFewInputFiles(vtkDataSet* input, int& duplicateCells);

  /**
   * ?
   */
  vtkUnstructuredGrid* MPIRedistribute(
    vtkDataSet* in, vtkDataSet* input, int filterOutDuplicateCells);

  /**
   * ?
   */
  vtkIdList** GetCellIdsForProcess(int proc, int* nlists);

  /**
   * Fills in the Source and Target arrays which contain a schedule to allow
   * each processor to talk to every other.
   */
  void SetUpPairWiseExchange();

  ///@{
  /**
   * ?
   */
  void FreeIntArrays(vtkIdTypeArray** ar);
  static void FreeIdLists(vtkIdList** lists, int nlists);
  static vtkIdType GetIdListSize(vtkIdList** lists, int nlists);
  ///@}

  ///@{
  /**
   * This transfers counts (array sizes) between processes.
   */
  vtkIdTypeArray* ExchangeCounts(vtkIdType myCount, int tag);
  vtkIdTypeArray* ExchangeCountsLean(vtkIdType myCount, int tag);
  vtkIdTypeArray* ExchangeCountsFast(vtkIdType myCount, int tag);
  ///@}

  ///@{
  /**
   * This transfers id valued data arrays between processes.
   */
  vtkIdTypeArray** ExchangeIdArrays(vtkIdTypeArray** arIn, int deleteSendArrays, int tag);
  vtkIdTypeArray** ExchangeIdArraysLean(vtkIdTypeArray** arIn, int deleteSendArrays, int tag);
  vtkIdTypeArray** ExchangeIdArraysFast(vtkIdTypeArray** arIn, int deleteSendArrays, int tag);
  ///@}

  ///@{
  /**
   * This transfers float valued data arrays between processes.
   */
  vtkFloatArray** ExchangeFloatArrays(vtkFloatArray** myArray, int deleteSendArrays, int tag);
  vtkFloatArray** ExchangeFloatArraysLean(vtkFloatArray** myArray, int deleteSendArrays, int tag);
  vtkFloatArray** ExchangeFloatArraysFast(vtkFloatArray** myArray, int deleteSendArrays, int tag);
  ///@}

  ///@{
  /**
   * ?
   */
  vtkUnstructuredGrid* ExchangeMergeSubGrids(vtkIdList** cellIds, int deleteCellIds,
    vtkDataSet* myGrid, int deleteMyGrid, int filterOutDuplicateCells, int ghostCellFlag, int tag);
  vtkUnstructuredGrid* ExchangeMergeSubGrids(vtkIdList*** cellIds, int* numLists, int deleteCellIds,
    vtkDataSet* myGrid, int deleteMyGrid, int filterOutDuplicateCells, int ghostCellFlag, int tag);
  vtkUnstructuredGrid* ExchangeMergeSubGridsLean(vtkIdList*** cellIds, int* numLists,
    int deleteCellIds, vtkDataSet* myGrid, int deleteMyGrid, int filterOutDuplicateCells,
    int ghostCellFlag, int tag);
  vtkUnstructuredGrid* ExchangeMergeSubGridsFast(vtkIdList*** cellIds, int* numLists,
    int deleteCellIds, vtkDataSet* myGrid, int deleteMyGrid, int filterOutDuplicateCells,
    int ghostCellFlag, int tag);
  ///@}

  ///@{
  /**
   * ?
   */
  char* MarshallDataSet(vtkUnstructuredGrid* extractedGrid, vtkIdType& size);
  vtkUnstructuredGrid* UnMarshallDataSet(char* buf, vtkIdType size);
  ///@}

  ///@{
  /**
   * ?
   */
  void ClipCellsToSpatialRegion(vtkUnstructuredGrid* grid);
#if 0
  void ClipWithVtkClipDataSet(vtkUnstructuredGrid *grid, double *bounds,
           vtkUnstructuredGrid **outside, vtkUnstructuredGrid **inside);
#endif
  ///@}

  void ClipWithBoxClipDataSet(vtkUnstructuredGrid* grid, double* bounds,
    vtkUnstructuredGrid** outside, vtkUnstructuredGrid** inside);

  ///@{
  /**
   * Accessors to the "GLOBALID" point and cell arrays of the dataset.
   * Global ids are used by D3 to uniquely name all points and cells
   * so that after shuffling data between processors, redundant information
   * can be quickly eliminated.
   */
  vtkIdTypeArray* GetGlobalNodeIdArray(vtkDataSet* set);
  vtkIdType* GetGlobalNodeIds(vtkDataSet* set);
  vtkIdTypeArray* GetGlobalElementIdArray(vtkDataSet* set);
  vtkIdType* GetGlobalElementIds(vtkDataSet* set);
  int AssignGlobalNodeIds(vtkUnstructuredGrid* grid);
  int AssignGlobalElementIds(vtkDataSet* in);
  vtkIdTypeArray** FindGlobalPointIds(vtkFloatArray** ptarray, vtkIdTypeArray* ids,
    vtkUnstructuredGrid* grid, vtkIdType& numUniqueMissingPoints);
  ///@}

  /**
   * ?
   */
  vtkIdTypeArray** MakeProcessLists(
    vtkIdTypeArray** pointIds, vtkPDistributedDataFilterSTLCloak* procs);

  /**
   * ?
   */
  vtkIdList** BuildRequestedGrids(vtkIdTypeArray** globalPtIds, vtkUnstructuredGrid* grid,
    vtkPDistributedDataFilterSTLCloak* ptIdMap);

  ///@{
  /**
   * ?
   */
  int InMySpatialRegion(float x, float y, float z);
  int InMySpatialRegion(double x, double y, double z);
  int StrictlyInsideMyBounds(float x, float y, float z);
  int StrictlyInsideMyBounds(double x, double y, double z);
  ///@}

  ///@{
  /**
   * ?
   */
  vtkIdTypeArray** GetGhostPointIds(
    int ghostLevel, vtkUnstructuredGrid* grid, int AddCellsIAlreadyHave);
  vtkUnstructuredGrid* AddGhostCellsUniqueCellAssignment(
    vtkUnstructuredGrid* myGrid, vtkPDistributedDataFilterSTLCloak* globalToLocalMap);
  vtkUnstructuredGrid* AddGhostCellsDuplicateCellAssignment(
    vtkUnstructuredGrid* myGrid, vtkPDistributedDataFilterSTLCloak* globalToLocalMap);
  vtkUnstructuredGrid* SetMergeGhostGrid(vtkUnstructuredGrid* ghostCellGrid,
    vtkUnstructuredGrid* incomingGhostCells, int ghostLevel,
    vtkPDistributedDataFilterSTLCloak* idMap);
  ///@}

  ///@{
  /**
   * ?
   */
  vtkUnstructuredGrid* ExtractCells(vtkIdList* list, int deleteCellLists, vtkDataSet* in);
  vtkUnstructuredGrid* ExtractCells(
    vtkIdList** lists, int nlists, int deleteCellLists, vtkDataSet* in);
  vtkUnstructuredGrid* ExtractZeroCellGrid(vtkDataSet* in);
  ///@}

  ///@{
  /**
   * ?
   */
  static int GlobalPointIdIsUsed(
    vtkUnstructuredGrid* grid, int ptId, vtkPDistributedDataFilterSTLCloak* globalToLocal);
  static int LocalPointIdIsUsed(vtkUnstructuredGrid* grid, int ptId);
  static vtkIdType FindId(vtkIdTypeArray* ids, vtkIdType gid, vtkIdType startLoc);
  ///@}

  /**
   * ?
   */
  static vtkIdTypeArray* AddPointAndCells(vtkIdType gid, vtkIdType localId,
    vtkUnstructuredGrid* grid, vtkIdType* gidCells, vtkIdTypeArray* ids);

  ///@{
  /**
   * ?
   */
  static void AddConstantUnsignedCharPointArray(
    vtkUnstructuredGrid* grid, const char* arrayName, unsigned char val);
  static void AddConstantUnsignedCharCellArray(
    vtkUnstructuredGrid* grid, const char* arrayName, unsigned char val);
  ///@}

  /**
   * ?
   */
  static void RemoveRemoteCellsFromList(
    vtkIdList* cellList, vtkIdType* gidCells, vtkIdType* remoteCells, vtkIdType nRemoteCells);

  /**
   * ?
   */
  static vtkUnstructuredGrid* MergeGrids(vtkDataSet** sets, int nsets, int deleteDataSets,
    int useGlobalNodeIds, float pointMergeTolerance, int useGlobalCellIds);

  vtkPDistributedDataFilter(const vtkPDistributedDataFilter&) = delete;
  void operator=(const vtkPDistributedDataFilter&) = delete;
};
VTK_ABI_NAMESPACE_END
#endif
