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

 Program:   Visualization Toolkit
 Module:    vtkStructuredAMRGridConnectivity.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   vtkStructuredAMRGridConnectivity
 *  grid connectivity.
 *
 *
 *  A concrete instance of vtkAbstractGridConnectivity that implements
 *  functionality for computing the neighboring topology within a structured
 *  AMR grid, as well as, generating ghost-layers. Support is provided for
 *  1-D, 2-D (XY,XZ,YZ) and 3-D cell-centered datasets. This implementation
 *  does not have any support for distributed data. For the parallel
 *  implementation see vtkPStructuredAMRGridConnectivity.
 *
 * @sa
 *  vtkPStructuredAMRGridConnectivity vtkAbstractGridConnectivity
 */

#ifndef vtkStructuredAMRGridConnectivity_h
#define vtkStructuredAMRGridConnectivity_h

#include "vtkAbstractGridConnectivity.h"
#include "vtkFiltersGeometryModule.h" // For export macro

#include "vtkStructuredAMRNeighbor.h" // For vtkStructuredAMRNeighbor def.

// C++ includes
#include <map>     // For STL map
#include <ostream> // For STL stream
#include <set>     // For STL set
#include <vector>  // For STL vector

class VTKFILTERSGEOMETRY_EXPORT vtkStructuredAMRGridConnectivity
  : public vtkAbstractGridConnectivity
{
public:
  static vtkStructuredAMRGridConnectivity* New();
  vtkTypeMacro(vtkStructuredAMRGridConnectivity, vtkAbstractGridConnectivity);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  /**
   * Initializes this instance of vtkStructuredAMRGridConnectivity where N
   * is the total number of grids in the AMR hierarchy. Optionally, if the
   * AMR dataset has a constant refinement, it should be specified during
   * initialization as the code optimizes for it. If a -1 or no refinement
   * ratio is specified a varying refinement ratio is assumed.
   */
  void Initialize(
    const unsigned int NumberOfLevels, const unsigned int N, const int RefinementRatio = -1);

  /**
   * Computes neighboring information.
   */
  void ComputeNeighbors() override;

  /**
   * Creates ghost layers.
   */
  void CreateGhostLayers(const int N = 1) override;

  /**
   * Registers the AMR grid with the given global linear grid ID (starting
   * numbering from 0) and level and refinement ratio. This method is to be
   * used when the refinement ratio is not constant.
   */
  virtual void RegisterGrid(const int gridIdx, const int level, const int refinementRatio,
    int extents[6], vtkUnsignedCharArray* nodesGhostArray, vtkUnsignedCharArray* cellGhostArray,
    vtkPointData* pointData, vtkCellData* cellData, vtkPoints* gridNodes);

  /**
   * Registers the AMR grid with the given global linear grid ID (starting
   * numbering from 0) and level. The extents of the grid are expected to be
   * global node extents.
   */
  virtual void RegisterGrid(const int gridIdx, const int level, int extents[6],
    vtkUnsignedCharArray* nodesGhostArray, vtkUnsignedCharArray* cellGhostArray,
    vtkPointData* pointData, vtkCellData* cellData, vtkPoints* gridNodes);

  ///@{
  /**
   * Get/Set macro for BalancedRefinement property, default is true. If the
   * refinement is balanced, then, adjacent grids in the AMR hierarchy can
   * only differ by one level. By default, a balanced refinement is assumed.
   */
  vtkSetMacro(BalancedRefinement, bool);
  vtkGetMacro(BalancedRefinement, bool);
  ///@}

  ///@{
  /**
   * Get/Set macro NodeCentered property which indicates if the data is
   * node-centered or cell-centered. By default, node-centered is set to false
   * since AMR datasets are primarily cell-centered.
   */
  vtkSetMacro(NodeCentered, bool);
  vtkGetMacro(NodeCentered, bool);
  ///@}

  ///@{
  /**
   * Get/Set CellCentered property which indicates if the data is cell-centered
   * By default, cell-centered is set to true.
   */
  vtkSetMacro(CellCentered, bool);
  vtkGetMacro(CellCentered, bool);
  ///@}

  /**
   * Returns the number of neighbors for the grid corresponding to the given
   * grid ID.
   */
  int GetNumberOfNeighbors(const int gridID);

  /**
   * Returns the ghost extend for the grid corresponding to the given grid ID.
   */
  void GetGhostedExtent(const int gridID, int ext[6]);

  /**
   * Returns the AMR neighbor for the patch with the corresponding grid ID.
   */
  vtkStructuredAMRNeighbor GetNeighbor(const int gridID, const int nei);

protected:
  vtkStructuredAMRGridConnectivity();
  ~vtkStructuredAMRGridConnectivity() override;

  /**
   * Sets the total number of grids(blocks) in the AMR hierarchy
   */
  void SetNumberOfGrids(const unsigned int N) override;

  /**
   * Creates the ghosted mask arrays
   */
  void CreateGhostedMaskArrays(const int gridID);

  /**
   * Creates the ghosted extent of the given grid
   */
  void CreateGhostedExtent(const int gridID, const int N);

  /**
   * Sets the ghost extent for the grid corresponding to the given grid ID.
   */
  void SetGhostedExtent(const int gridID, int ext[6]);

  /**
   * Gets the coarsened extent for the grid with the given grid index.
   */
  void GetCoarsenedExtent(const int gridIdx, int fromLevel, int toLevel, int ext[6]);

  /**
   * Gets the refined extent for the grid with the given grid index.
   */
  void GetRefinedExtent(const int gridIdx, int fromLevel, int toLevel, int ext[6]);

  /**
   * Refines the given extent.
   */
  void RefineExtent(int orient[3], int ndim, int fromLevel, int toLevel, int ext[6]);

  /**
   * Given the global i,j,k index of a cell at a coarse level, fromLevel, this
   * method computes the range of cells on the refined grid.
   */
  void GetCellRefinedExtent(int orient[3], int ndim, const int i, const int j, const int k,
    const int fromLevel, const int toLevel, int ext[6]);

  /**
   * Coarsens the given extent.
   */
  void CoarsenExtent(int orient[3], int ndim, int fromLevel, int toLevel, int ext[6]);

  /**
   * Gets the grid extent for the grid with the given grid ID.
   */
  void GetGridExtent(const int gridIdx, int ext[6]);

  /**
   * Returns the level of the grid with the corresponding grid ID.
   */
  int GetGridLevel(const int gridIdx);

  /**
   * Checks if the given level has been registered
   */
  bool LevelExists(const int level);

  /**
   * Checks if the node is an interior node in the given extent.
   */
  bool IsNodeInterior(const int i, const int j, const int k, int GridExtent[6]);

  /**
   * Checks if the node is within the extent.
   */
  bool IsNodeWithinExtent(const int i, const int j, const int k, int GridExtent[6]);

  /**
   * Checks if the node is on a shared boundary.
   */
  bool IsNodeOnSharedBoundary(
    const int i, const int j, const int k, const int gridId, int gridExt[6]);

  /**
   * Checks if the node is on the boundary of the given extent.
   */
  bool IsNodeOnBoundaryOfExtent(const int i, const int j, const int k, int ext[6]);

  /**
   * Inserts the grid corresponding to the given ID at the prescribed level.
   */
  void InsertGridAtLevel(const int level, const int gridID);

  /**
   * Loops through the neighbors of this grid and computes the send and rcv
   * extents for the N requested ghost layers.
   */
  void ComputeNeighborSendAndRcvExtent(const int gridID, const int N);

  /**
   * Computes the whole extent w.r.t. level 0 as well as the AMR dataset
   * description and dimension.
   */
  void ComputeWholeExtent();

  /**
   * Gets the whole extent with respect to the given level.
   * NOTE: This method assument that the whole extent has been computed.
   */
  void GetWholeExtentAtLevel(const int level, int ext[6]);

  /**
   * Establishes neighboring relationship between grids i,j wheren i,j are
   * global indices.
   */
  void EstablishNeighbors(const int i, const int j);

  /**
   * Computes the node orientation tuple for the given i,j,k node.
   */
  void GetNodeOrientation(
    const int i, const int j, const int k, int gridExt[6], int nodeOrientation[3]);

  /**
   * Establishes the orientation vector and dimension based on the computed
   * data description. The orientation vector is a 3-tuple, which encodes the
   * dimensions that are used. For example, let's say that we want to define
   * the orientation to be in the XZ plane, then, the orient array would be
   * constructed as follows: {0,2 -1}, where -1 indicates a NIL value.
   */
  void GetOrientationVector(const int dataDescription, int orient[3], int& ndim);

  /**
   * Checks if a constant refinement ratio has been specified.
   */
  bool HasConstantRefinementRatio();

  /**
   * Sets the refinement ratio at the given level.
   */
  void SetRefinementRatioAtLevel(const int level, const int r);

  /**
   * Returns the refinement ratio at the given level.
   */
  int GetRefinementRatioAtLevel(const int level);

  /**
   * Checks if the extent ext1 and ext2 are equal.
   */
  bool AreExtentsEqual(int ext1[6], int ext2[6]);

  /**
   * Constructs the block topology for the given grid.
   */
  void SetBlockTopology(const int gridID);

  /**
   * Returns the number of faces of the block corresponding to the given grid
   * ID that are adjacent to at least one other block. Note, this is not the
   * total number of neighbors for the block. This method simply checks how
   * many out of the 6 block faces have connections. Thus, the return value
   * has an upper-bound of 6.
   */
  int GetNumberOfConnectingBlockFaces(const int gridID);

  ///@{
  /**
   * Checks if the block corresponding to the given grid ID has a block
   * adjacent to it in the given block direction.
   * NOTE: The block direction is essentially one of the 6 faces of the
   * block defined as follows:
   * <ul>
   * <li> FRONT  = 0 (+k diretion)  </li>
   * <li> BACK   = 1 (-k direction) </li>
   * <li> RIGHT  = 2 (+i direction) </li>
   * <li> LEFT   = 3 (-i direction) </li>
   * <li> TOP    = 4 (+j direction) </li>
   * <li> BOTTOM = 5 (-j direction) </li>
   * </ul>
   */
  bool HasBlockConnection(const int gridID, const int blockDirection)
  {
    // Sanity check
    assert("pre: gridID is out-of-bounds" && (gridID >= 0) &&
      (gridID < static_cast<int>(this->NumberOfGrids)));
    assert("pre: BlockTopology has not been properly allocated" &&
      (this->NumberOfGrids == this->BlockTopology.size()));
    assert("pre: blockDirection is out-of-bounds" && (blockDirection >= 0) && (blockDirection < 6));
    bool status = false;
    if (this->BlockTopology[gridID] & (1 << blockDirection))
    {
      status = true;
    }
    return (status);
  }
  ///@}

  /**
   * Removes a block connection along the given direction for the block
   * corresponding to the given gridID.
   * NOTE: The block direction is essentially one of the 6 faces of the
   * block defined as follows:
   * <ul>
   * <li> FRONT  = 0 (+k diretion)  </li>
   * <li> BACK   = 1 (-k direction) </li>
   * <li> RIGHT  = 2 (+i direction) </li>
   * <li> LEFT   = 3 (-i direction) </li>
   * <li> TOP    = 4 (+j direction) </li>
   * <li> BOTTOM = 5 (-j direction) </li>
   * </ul>
   */
  void RemoveBlockConnection(const int gridID, const int blockDirection);

  /**
   * Adds a block connection along the given direction for the block
   * corresponding to the given gridID.
   * NOTE: The block direction is essentially one of the 6 faces of the
   * block defined as follows:
   * <ul>
   * <li> FRONT  = 0 (+k diretion)  </li>
   * <li> BACK   = 1 (-k direction) </li>
   * <li> RIGHT  = 2 (+i direction) </li>
   * <li> LEFT   = 3 (-i direction) </li>
   * <li> TOP    = 4 (+j direction) </li>
   * <li> BOTTOM = 5 (-j direction) </li>
   * </ul>
   */
  void AddBlockConnection(const int gridID, const int blockDirection);

  /**
   * Clears all block connections for the block corresponding to the given
   * grid ID.
   */
  void ClearBlockConnections(const int gridID);

  /**
   * Marks the ghost property for the given node.
   */
  virtual void MarkNodeProperty(const int gridId, const int i, const int j, const int k,
    int gridExt[6], int wholeExt[6], unsigned char& p);

  /**
   * Fills the node ghost arrays for the given grid
   */
  virtual void FillNodesGhostArray(const int gridId, vtkUnsignedCharArray* nodesArray);

  /**
   * Fills the cell ghost arrays for the given grid
   */
  virtual void FillCellsGhostArray(const int gridId, vtkUnsignedCharArray* cellsArray);

  /**
   * Fills ghost arrays.
   */
  void FillGhostArrays(
    const int gridId, vtkUnsignedCharArray* nodesArray, vtkUnsignedCharArray* cellsArray) override;

  /**
   * Compute the AMR neighbor of grid "i" and its neighbor grid "j".

   * Given the structured neighbors computed in normalized space (i.e., at
   * the same level) between the two grids, this method computes the
   * corresponding AMR neighbor which essentially adds other bits of
   * information, such as level, relationship type, etc.

   * NOTE:
   * The extents next1 and next2 for each grid are the normalized extents
   */
  vtkStructuredAMRNeighbor GetAMRNeighbor(const int i, const int iLevel, int next1[6], const int j,
    const int jLevel, int next2[6], const int normalizedLevel, const int levelDiff,
    vtkStructuredNeighbor& nei);

  /**
   * A Helper method to compute the AMR neighbor overlap extents. The method
   * coarsens/refines the gridOverlap and neiOverlap extents accordingly s.t.
   * they are w.r.t. to the level of the grid they refer to.
   */
  void ComputeAMRNeighborOverlapExtents(const int iLevel, const int jLevel,
    const int normalizedLevel, const vtkStructuredNeighbor& nei, int orient[3], int ndim,
    int gridOverlapExtent[6], int neiOverlapExtent[6]);

  /**
   * Get 1-D orientation.
   */
  int Get1DOrientation(const int idx, const int ExtentLo, const int ExtentHi, const int OnLo,
    const int OnHi, const int NotOnBoundary);

  /**
   * Prints the extent
   */
  void PrintExtent(std::ostream& os, int ext[6]);

  /**
   * Initializes the ghost data-structures
   */
  void InitializeGhostData(const int gridID);

  /**
   * Transfers the data of the registered grid, to the ghosted data-structures.
   */
  void TransferRegisteredDataToGhostedData(const int gridID);

  /**
   * Transfers local node-centered neighbor data
   */
  void TransferLocalNodeCenteredNeighborData(const int gridID, vtkStructuredAMRNeighbor& nei);

  /**
   * Copy cell center value from a coarser level by direct-injection, i.e., the
   * values within the coarse cell is assumed to be constant.
   */
  void GetLocalCellCentersFromCoarserLevel(const int gridID, vtkStructuredAMRNeighbor& nei);

  /**
   * Copy cell center values from a finer level by cell averaging.
   */
  void GetLocalCellCentersFromFinerLevel(const int gridID, vtkStructuredAMRNeighbor& nei);

  /**
   * Copy cell center values to fill in the ghost levels from a neighbor at
   * the same level as the grid corresponding to the given grid ID.
   */
  void GetLocalCellCentersAtSameLevel(const int gridID, vtkStructuredAMRNeighbor& nei);

  /**
   * Transfers local cell-centered neighbor data
   */
  void TransferLocalCellCenteredNeighborData(const int gridID, vtkStructuredAMRNeighbor& nei);

  /**
   * Transfers local neighbor data
   */
  void TransferLocalNeighborData(const int gridID, vtkStructuredAMRNeighbor& nei);

  /**
   * Fills in the ghost data from the neighbors
   */
  virtual void TransferGhostDataFromNeighbors(const int gridID);

  /**
   * Loops through all arrays and computes the average of the supplied source
   * indices and stores the corresponding average
   */
  void AverageFieldData(vtkFieldData* source, vtkIdType* sourceIds, const int N,
    vtkFieldData* target, vtkIdType targetIdx);

  /**
   * Loops through all arrays in the source and for each array, it copies the
   * tuples from sourceIdx to the target at targetIdx. This method assumes
   * that the source and target have a one-to-one array correspondence, that
   * is array i in the source corresponds to array i in the target.
   */
  void CopyFieldData(
    vtkFieldData* source, vtkIdType sourceIdx, vtkFieldData* target, vtkIdType targetIdx);

  unsigned int NumberOfLevels; // The total number of levels;
  int DataDimension;           // The dimension of the data, i.e. 2 or 3
  int DataDescription;         // The data description, i.e., VTK_XY_PLANE, etc.
  int WholeExtent[6];          // The whole extent w.r.t. to the root level, level 0.
  int MaxLevel;                // The max level of the AMR hierarchy
  int RefinementRatio;         // The refinement ratio, set in the initialization,iff,
                               // a constant refinement ratio is used. A value of -1
                               // indicates that the refinement ratio is not constant
                               // and the RefinementRatios vector is used instead.

  bool NodeCentered; // Indicates if the data is node-centered
  bool CellCentered; // Indicates if the data is cell-centered

  bool BalancedRefinement; // If Balanced refinement is true, then adjacent
                           // grids in the hierarchy can only differ by one
                           // level.

  // AMRHierarchy stores the set of grid Ids in [0,N] for each level
  std::map<int, std::set<int>> AMRHierarchy;

  // For each grid, [0,N] store the grid extents,level, and list of neighbors
  std::vector<int> GridExtents;             // size of this vector is 6*N
  std::vector<int> GhostedExtents;          // size of this vector is 6*N
  std::vector<unsigned char> BlockTopology; // size of this vector is N
  std::vector<int> GridLevels;              // size of this vector is N
  std::vector<std::vector<vtkStructuredAMRNeighbor>> Neighbors;

  // For each grid, [0,N], store the donor level,grid and cell information, a
  // DonorLevel of -1 indicates that the cell is not receiving any information
  // from a donor.
  std::vector<std::vector<int>> CellCenteredDonorLevel;

  // RefinementRatios stores the refinement ratio at each level, this vector
  // is used only when the refinement ratio varies across levels
  std::vector<int> RefinementRatios;

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

//=============================================================================
//  INLINE METHODS
//=============================================================================

//------------------------------------------------------------------------------
inline int vtkStructuredAMRGridConnectivity::GetNumberOfNeighbors(const int gridID)
{
  assert("pre: grid ID is out-of-bounds" && (gridID >= 0) &&
    (gridID < static_cast<int>(this->NumberOfGrids)));
  assert("pre: neighbors vector has not been properly allocated" &&
    (this->Neighbors.size() == this->NumberOfGrids));
  return (static_cast<int>(this->Neighbors[gridID].size()));
}

//------------------------------------------------------------------------------
inline vtkStructuredAMRNeighbor vtkStructuredAMRGridConnectivity::GetNeighbor(
  const int gridID, const int nei)
{
  assert("pre: grid ID is out-of-bounds" && (gridID >= 0) &&
    (gridID < static_cast<int>(this->NumberOfGrids)));
  assert("pre: neighbors vector has not been properly allocated" &&
    (this->Neighbors.size() == this->NumberOfGrids));
  assert("pre: nei index is out-of-bounds" && (nei >= 0) &&
    (nei < static_cast<int>(this->Neighbors[gridID].size())));
  return (this->Neighbors[gridID][nei]);
}

//------------------------------------------------------------------------------
inline int vtkStructuredAMRGridConnectivity::Get1DOrientation(const int idx, const int ExtentLo,
  const int ExtentHi, const int OnLo, const int OnHi, const int NotOnBoundary)
{
  if (idx == ExtentLo)
  {
    return OnLo;
  }
  else if (idx == ExtentHi)
  {
    return OnHi;
  }
  return NotOnBoundary;
}

//------------------------------------------------------------------------------
inline int vtkStructuredAMRGridConnectivity::GetNumberOfConnectingBlockFaces(const int gridID)
{
  // Sanity check
  assert("pre: gridID is out-of-bounds" && (gridID >= 0) &&
    (gridID < static_cast<int>(this->NumberOfGrids)));
  assert("pre: BlockTopology has not been properly allocated" &&
    (this->NumberOfGrids == this->BlockTopology.size()));

  int count = 0;
  for (int i = 0; i < 6; ++i)
  {
    if (this->HasBlockConnection(gridID, i))
    {
      ++count;
    }
  }
  assert("post: count must be in [0,5]" && (count >= 0 && count <= 6));
  return (count);
}

//------------------------------------------------------------------------------
inline void vtkStructuredAMRGridConnectivity::RemoveBlockConnection(
  const int gridID, const int blockDirection)
{
  // Sanity check
  assert("pre: gridID is out-of-bounds" && (gridID >= 0) &&
    (gridID < static_cast<int>(this->NumberOfGrids)));
  assert("pre: BlockTopology has not been properly allocated" &&
    (this->NumberOfGrids == this->BlockTopology.size()));
  assert("pre: blockDirection is out-of-bounds" && (blockDirection >= 0) && (blockDirection < 6));

  this->BlockTopology[gridID] &= ~(1 << blockDirection);
}

//------------------------------------------------------------------------------
inline void vtkStructuredAMRGridConnectivity::AddBlockConnection(
  const int gridID, const int blockDirection)
{
  // Sanity check
  assert("pre: gridID is out-of-bounds" && (gridID >= 0) &&
    (gridID < static_cast<int>(this->NumberOfGrids)));
  assert("pre: BlockTopology has not been properly allocated" &&
    (this->NumberOfGrids == this->BlockTopology.size()));
  assert("pre: blockDirection is out-of-bounds" && (blockDirection >= 0) && (blockDirection < 6));
  this->BlockTopology[gridID] |= (1 << blockDirection);
}

//------------------------------------------------------------------------------
inline void vtkStructuredAMRGridConnectivity::ClearBlockConnections(const int gridID)
{
  // Sanity check
  assert("pre: gridID is out-of-bounds" && (gridID >= 0) &&
    (gridID < static_cast<int>(this->NumberOfGrids)));
  assert("pre: BlockTopology has not been properly allocated" &&
    (this->NumberOfGrids == this->BlockTopology.size()));
  for (int i = 0; i < 6; ++i)
  {
    this->RemoveBlockConnection(gridID, i);
  } // END for all block directions
}

//------------------------------------------------------------------------------
inline bool vtkStructuredAMRGridConnectivity::AreExtentsEqual(int ext1[6], int ext2[6])
{
  for (int i = 0; i < 6; ++i)
  {
    if (ext1[i] != ext2[i])
    {
      return false;
    }
  } // END for
  return true;
}

//------------------------------------------------------------------------------
inline void vtkStructuredAMRGridConnectivity::PrintExtent(std::ostream& os, int ext[6])
{
  for (int i = 0; i < 6; i += 2)
  {
    os << "[";
    os << ext[i] << " ";
    os << ext[i + 1] << "] ";
  } // END for
}

//------------------------------------------------------------------------------
inline int vtkStructuredAMRGridConnectivity::GetGridLevel(const int gridIdx)
{
  assert("pre: grid Index is out-of-bounds!" && (gridIdx < static_cast<int>(this->NumberOfGrids)));
  assert("pre: grid levels vector has not been allocated" &&
    (this->GridLevels.size() == this->NumberOfGrids));
  return (this->GridLevels[gridIdx]);
}

//------------------------------------------------------------------------------
inline void vtkStructuredAMRGridConnectivity::SetRefinementRatioAtLevel(
  const int level, const int r)
{
  assert("pre: RefinementRatios vector is not properly allocated" &&
    this->RefinementRatios.size() == this->NumberOfLevels);
  assert("pre: leve is out-of-bounds!" && (level >= 0) &&
    (level < static_cast<int>(this->RefinementRatios.size())));
  assert("pre: invalid refinement ratio" && (r >= 2));

  this->RefinementRatios[level] = r;
}

//------------------------------------------------------------------------------
inline int vtkStructuredAMRGridConnectivity::GetRefinementRatioAtLevel(const int level)
{
  assert("pre: RefinementRatios vector is not properly allocated" &&
    this->RefinementRatios.size() == this->NumberOfLevels);
  assert("pre: leve is out-of-bounds!" && (level >= 0) &&
    (level < static_cast<int>(this->RefinementRatios.size())));
  assert(
    "pre: refinement ratio for level has not been set" && (this->RefinementRatios[level] >= 2));

  return (this->RefinementRatios[level]);
}

//------------------------------------------------------------------------------
inline bool vtkStructuredAMRGridConnectivity::HasConstantRefinementRatio()
{
  if (this->RefinementRatio < 2)
  {
    return false;
  }
  return true;
}

//------------------------------------------------------------------------------
inline void vtkStructuredAMRGridConnectivity::GetGridExtent(const int gridIdx, int ext[6])
{
  assert("pre: grid index is out-of-bounds" &&
    ((gridIdx >= 0) && (gridIdx < static_cast<int>(this->GridExtents.size()))));

  for (int i = 0; i < 6; ++i)
  {
    ext[i] = this->GridExtents[gridIdx * 6 + i];
  }
}

//------------------------------------------------------------------------------
inline bool vtkStructuredAMRGridConnectivity::LevelExists(const int level)
{
  if (this->AMRHierarchy.find(level) != this->AMRHierarchy.end())
  {
    return true;
  }
  return false;
}

//------------------------------------------------------------------------------
inline void vtkStructuredAMRGridConnectivity::InsertGridAtLevel(const int level, const int gridID)
{
  if (this->LevelExists(level))
  {
    this->AMRHierarchy[level].insert(gridID);
  }
  else
  {
    std::set<int> grids;
    grids.insert(gridID);
    this->AMRHierarchy[level] = grids;
  }
}

#endif /* VTKSTRUCTUREDAMRGRIDCONNECTIVITY_H_ */
