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

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

 =========================================================================*/
// .NAME vtkAbstractGridConnectivity.h -- Superclass for GridConnectivity
//
// .SECTION Description
//  A superclass that defines the interface to be implemented by all
//  concrete grid connectivity classes. Grid connectivity classes provide the
//  mechanism to achieve the following:
//  <ul>
//    <li>
//      <b> Handling of partitioned/distributed data </b>
//      <p>
//       Construct the neighboring topology information for each partition,e.g.,
//       used for creating communication lists and in computing statistics,i.e.,
//       average, mean, etc.
//      </p>
//      <b> Creation of ghost layers </b>
//      <p>
//       Provides the mechanism for automatically generating ghost information
//       given a partitioned/distributed grid configuration.
//      </p>
//    </li>
//  </ul>
//
// .SECTION See Also
//  vtkStructuredGridConnectivity

#ifndef VTKABSTRACTGRIDCONNECTIVITY_H_
#define VTKABSTRACTGRIDCONNECTIVITY_H_

// VTK includes
#include "vtkFiltersGeometryModule.h" // For export macro
#include "vtkObject.h"
#include "vtkPoints.h"            // for vtkPoints definition in STL vector
#include "vtkPointData.h"         // for vtkPointData definition in STL vector
#include "vtkCellData.h"          // for vtkCellData definition int STL vector
#include "vtkUnsignedCharArray.h" // for vtkUnsignedCharArray definition

// Forward declarations
class vtkPointData;
class vtkCellData;
class vtkUnsignedCharArray;
class vtkPoints;

// C++ include directives
#include <vector>  // For STL vector
#include <cassert> // For assert

class VTKFILTERSGEOMETRY_EXPORT vtkAbstractGridConnectivity : public vtkObject
{
  public:
    vtkTypeMacro( vtkAbstractGridConnectivity, vtkObject );
    void PrintSelf(ostream &os,vtkIndent indent );

    // Description:
    // Set/Get the number of ghost layers
    vtkSetMacro( NumberOfGhostLayers, unsigned int );
    vtkGetMacro( NumberOfGhostLayers, unsigned int);

    // Description:
    // Sets the total number of grids in the domain.
    // Note: This method is implemented by concrete classes.
    // NOTE: Concrete classes implementing this pure virtual method must
    // set the number of grids and call AllocateUserRegisterDataStructures
    // in addition to defining any other additional functionality.
    virtual void SetNumberOfGrids( const unsigned int N ) = 0;

    // Description:
    // Returns the total number of grids.
    unsigned int GetNumberOfGrids() { return this->NumberOfGrids; };

    // Description:
    // Computes the grid neighboring topology for the domain
    virtual void ComputeNeighbors( ) = 0;

    // Description:
    // Fills the ghost arrays for the given grid.
    virtual void FillGhostArrays(
        const int gridId,
        vtkUnsignedCharArray* nodesArray,
        vtkUnsignedCharArray* cellsArray ) = 0;

    // Description:
    // Creates N layers of ghost layers where N is the number of cells that will
    // be added to each grid. If no parameter is supplied, N has a nominal value
    // of 1, in which case 1 layer of cells would be added.
    // NOTE: This method is implemented by concrete implementations
    virtual void CreateGhostLayers( const int N=1 ) = 0;

    // Description:
    // Registers the ghostarrays for the given grid.
    void RegisterGridGhostArrays(
         const int gridID,vtkUnsignedCharArray *nodesArray,
         vtkUnsignedCharArray *cellsArray );

    // Description:
    // Registers the grid's field data, i.e., the node and cell data.
    void RegisterFieldData(
         const int gridID, vtkPointData *PointData, vtkCellData *CellData );

    // Description:
    // Registers the grid nodes for the grid associated with the given gridID.
    void RegisterGridNodes( const int gridID, vtkPoints *nodes );

    // Description:
    // Returns the ghosted points ghost array for the grid associated with the
    // given grid ID. The return pointer is a shallow-copy of the internal
    // data-structure. The pointer may be NULL iff there is no ghosted points
    // ghost array for the requested grid.
    vtkUnsignedCharArray* GetGhostedPointGhostArray( const int gridID );

    // Description:
    // Returns the ghosted cells ghost array for the grid associated with the
    // given grid ID. The return pointer is a shallow-copy of the internal
    // data-structure. The pointer may be NULL iff there is no ghosted cells
    // ghost array for the requested grid.
    vtkUnsignedCharArray* GetGhostedCellGhostArray( const int gridID );

    // Description:
    // Returns the ghosted grid point data for the grid associated with the
    // given grid ID. The return pointer is a shallow-copy of the internal
    // data-structure. The pointer may be NULL iff there is no ghosted point
    // data for the requested grid.
    vtkPointData* GetGhostedGridPointData( const int gridID );

    // Description:
    // Returns the ghosted grid cell data for the grid associated with the
    // given grid ID. The return pointer is a shallow-copy of the internal
    // data-structure. The pointer may be NULL iff there is no ghosted cell
    // data for the requested grid.
    vtkCellData* GetGhostedGridCellData( const int gridID );

    // Description:
    // Returns the ghosted grid points for the grid associated with the given
    // grid ID. The return pointer is a shallow-copy of the internal data
    // structure. The pointer may be NULL iff there are no ghosted points
    // created for the requested grid.
    vtkPoints* GetGhostedPoints( const int gridID );

  protected:
    vtkAbstractGridConnectivity();
    virtual ~vtkAbstractGridConnectivity();

    // Description:
    // Allocate/De-allocate the data-structures where the user-supplied grids
    // will be registered.
    void AllocateUserRegisterDataStructures();
    void DeAllocateUserRegisterDataStructures();

    // Description:
    // Allocated/De-allocate the data-structures where the ghosted grid
    // data will be stored.
    void AllocateInternalDataStructures();
    void DeAllocateInternalDataStructures();

    // The total number of grids, set initially by the user.
    unsigned int NumberOfGrids;
    unsigned int NumberOfGhostLayers;

    // BTX
    // Arrays registered by the user for each grid
    std::vector< vtkUnsignedCharArray* > GridPointGhostArrays;
    std::vector< vtkUnsignedCharArray* > GridCellGhostArrays;
    std::vector< vtkPointData* > GridPointData;
    std::vector< vtkCellData* > GridCellData;
    std::vector< vtkPoints* > GridPoints;

    // Arrays computed internally for each grid
    bool AllocatedGhostDataStructures;
    std::vector< vtkPointData* > GhostedGridPointData;
    std::vector< vtkCellData* > GhostedGridCellData;
    std::vector< vtkUnsignedCharArray* > GhostedPointGhostArray;
    std::vector< vtkUnsignedCharArray* > GhostedCellGhostArray;
    std::vector< vtkPoints* > GhostedGridPoints;
    // ETX

  private:
    vtkAbstractGridConnectivity(const vtkAbstractGridConnectivity&);// Not implemented
    void operator=(const vtkAbstractGridConnectivity&); // Not implemented
};

//------------------------------------------------------------------------------
inline vtkUnsignedCharArray*
vtkAbstractGridConnectivity::GetGhostedPointGhostArray( const int gridID )
{
  if( !this->AllocatedGhostDataStructures )
    {
    return NULL;
    }


  assert( "pre: GridID is out-of-bound GridPointData" &&
          (gridID >= 0) && (gridID < static_cast<int>(this->NumberOfGrids) ) );
  assert( "pre: Ghosted point ghost array" &&
          (this->NumberOfGrids == this->GhostedPointGhostArray.size() ) );

  return( this->GhostedPointGhostArray[ gridID ]  );
}

//------------------------------------------------------------------------------
inline vtkUnsignedCharArray*
vtkAbstractGridConnectivity::GetGhostedCellGhostArray( const int gridID )
{
  if( !this->AllocatedGhostDataStructures )
    {
    return NULL;
    }

  assert( "pre: GridID is out-of-bound GridPointData" &&
            (gridID >= 0) && (gridID < static_cast<int>(this->NumberOfGrids)));
  assert( "pre: Ghosted point ghost array" &&
          (this->NumberOfGrids == this->GhostedCellGhostArray.size() ) );

  return( this->GhostedCellGhostArray[ gridID ] );
}

//------------------------------------------------------------------------------
inline vtkPointData*
vtkAbstractGridConnectivity::GetGhostedGridPointData( const int gridID )
{
  if( !this->AllocatedGhostDataStructures )
    {
    return NULL;
    }

  assert( "pre: GridID is out-of-bound GridPointData" &&
            (gridID >= 0) && (gridID < static_cast<int>(this->NumberOfGrids)));
  assert( "pre: Ghosted point ghost array" &&
          (this->NumberOfGrids == this->GhostedGridPointData.size() ) );

  return( this->GhostedGridPointData[ gridID ] );
}

//------------------------------------------------------------------------------
inline vtkCellData*
vtkAbstractGridConnectivity::GetGhostedGridCellData( const int gridID )
{
  if( !this->AllocatedGhostDataStructures )
    {
    return NULL;
    }

  assert( "pre: GridID is out-of-bound GridPointData" &&
            (gridID >= 0) && (gridID < static_cast<int>(this->NumberOfGrids)));
  assert( "pre: Ghosted point ghost array" &&
          (this->NumberOfGrids == this->GhostedGridCellData.size() ) );

  return( this->GhostedGridCellData[ gridID ] );
}

//------------------------------------------------------------------------------
inline vtkPoints*
vtkAbstractGridConnectivity::GetGhostedPoints( const int gridID )
{
  if( !this->AllocatedGhostDataStructures )
    {
    return NULL;
    }

  assert( "pre: GridID is out-of-bound GridPointData" &&
            (gridID >= 0) && (gridID < static_cast<int>(this->NumberOfGrids)));
  assert( "pre: Ghosted point ghost array" &&
          (this->NumberOfGrids == this->GhostedGridPoints.size() ) );

  return( this->GhostedGridPoints[ gridID ] );
}

//------------------------------------------------------------------------------
inline void vtkAbstractGridConnectivity::AllocateUserRegisterDataStructures()
{
  // Sanity Check
  assert( "pre: Allocating UserRegister for N > 0 grids" &&
          (this->NumberOfGrids > 0) );

  this->GridPointGhostArrays.resize( this->NumberOfGrids, NULL );
  this->GridCellGhostArrays.resize( this->NumberOfGrids, NULL );
  this->GridPointData.resize( this->NumberOfGrids, NULL );
  this->GridCellData.resize( this->NumberOfGrids, NULL );
  this->GridPoints.resize( this->NumberOfGrids, NULL );
}

//------------------------------------------------------------------------------
inline void vtkAbstractGridConnectivity::DeAllocateUserRegisterDataStructures()
{
  assert( "pre: Data-structure has not been properly allocated" &&
             (this->GridPointGhostArrays.size() == this->NumberOfGrids  ) );
  assert( "pre: Data-structure has not been properly allocated" &&
             (this->GridCellGhostArrays.size() == this->NumberOfGrids  ) );
  assert( "pre: Data-structure has not been properly allocated" &&
             (this->GridPointData.size() == this->NumberOfGrids  ) );
  assert( "pre: Data-structure has not been properly allocated" &&
             (this->GridCellData.size() == this->NumberOfGrids  ) );
  assert( "pre: Data-structure has not been properly allocated" &&
             (this->GridPoints.size() == this->NumberOfGrids  ) );

  for( unsigned int i=0; i < this->NumberOfGrids; ++i )
    {
// NOTE: Ghost arrays are not deleted here b/c when they are registered, they
// are not shallow-copied.
//    if( this->GridPointGhostArrays[i] != NULL )
//      {
//      this->GridPointGhostArrays[i]->Delete();
//      }
//    if( this->GridCellGhostArrays[i] != NULL )
//      {
//      this->GridCellGhostArrays[i]->Delete();
//      }
    if( this->GridPointData[i] != NULL )
      {
      this->GridPointData[i]->Delete();
      }
    if( this->GridCellData[i] != NULL )
      {
      this->GridCellData[i]->Delete();
      }
    if( this->GridPoints[i] != NULL )
      {
      this->GridPoints[i]->Delete();
      }
    } // END for all grids

  this->GridPointGhostArrays.clear();
  this->GridCellGhostArrays.clear();
  this->GridPointData.clear();
  this->GridCellData.clear();
  this->GridPoints.clear();
}

//------------------------------------------------------------------------------
inline void vtkAbstractGridConnectivity::AllocateInternalDataStructures()
{
  assert( "pre: Allocating Internal data-structured for N > 0 grids" &&
          (this->NumberOfGrids > 0) );

  this->GhostedGridPointData.resize( this->NumberOfGrids, NULL );
  this->GhostedGridCellData.resize( this->NumberOfGrids, NULL );
  this->GhostedPointGhostArray.resize( this->NumberOfGrids, NULL );
  this->GhostedCellGhostArray.resize( this->NumberOfGrids, NULL );
  this->GhostedGridPoints.resize( this->NumberOfGrids, NULL );
  this->AllocatedGhostDataStructures = true;
}

//------------------------------------------------------------------------------
inline void vtkAbstractGridConnectivity::DeAllocateInternalDataStructures()
{
  if( !this->AllocatedGhostDataStructures )
    {
    return;
    }

  assert( "pre: Data-structure has not been properly allocated" &&
          (this->GhostedGridPointData.size() == this->NumberOfGrids) );
  assert( "pre: Data-structure has not been properly allocated" &&
          (this->GhostedGridCellData.size() == this->NumberOfGrids) );
  assert( "pre: Data-structure has not been properly allocated" &&
          (this->GhostedPointGhostArray.size() == this->NumberOfGrids) );
  assert( "pre: Data-structure has not been properly allocated" &&
          (this->GhostedCellGhostArray.size() == this->NumberOfGrids ) );
  assert( "pre: Data-structure has not been properly allocated" &&
          (this->GhostedGridPoints.size() == this->NumberOfGrids ) );

  for( unsigned int i=0; i < this->NumberOfGrids; ++i )
    {
    if( this->GhostedGridPointData[i] != NULL )
      {
      this->GhostedGridPointData[i]->Delete();
      }
    if( this->GhostedGridCellData[i] != NULL )
      {
      this->GhostedGridCellData[i]->Delete();
      }
    if( this->GhostedPointGhostArray[i] != NULL )
      {
      this->GhostedPointGhostArray[i]->Delete();
      }
    if( this->GhostedCellGhostArray[i] != NULL )
      {
      this->GhostedCellGhostArray[i]->Delete();
      }
    if( this->GhostedGridPoints[i] != NULL )
      {
      this->GhostedGridPoints[i]->Delete();
      }
    } // END for all grids

  this->GhostedGridPointData.clear();
  this->GhostedGridCellData.clear();
  this->GhostedPointGhostArray.clear();
  this->GhostedCellGhostArray.clear();
  this->GhostedGridPoints.clear();

  this->AllocatedGhostDataStructures = false;
}

//------------------------------------------------------------------------------
inline void vtkAbstractGridConnectivity::RegisterGridGhostArrays(
        const int gridID,
        vtkUnsignedCharArray *nodesArray,
        vtkUnsignedCharArray *cellsArray )
{
  // Sanity check
  assert( "pre: GridID is out-of-bound GridPointData" &&
           (gridID >= 0) && (gridID < static_cast<int>(this->NumberOfGrids)));
  assert( "pre: GridPointGhostArrays has not been allocated" &&
           (this->GridPointGhostArrays.size() == this->NumberOfGrids) );
  assert( "pre: GridCellGhostArrays has not been allocated" &&
           (this->GridCellGhostArrays.size() == this->NumberOfGrids ) );

  // NOTE: We should really shallow copy the objects here
  this->GridPointGhostArrays[ gridID ] = nodesArray;
  this->GridCellGhostArrays[ gridID ]  = cellsArray;
}

//------------------------------------------------------------------------------
inline void vtkAbstractGridConnectivity::RegisterFieldData(
        const int gridID, vtkPointData *PointData, vtkCellData *CellData )
{
  // Sanity check
  assert( "pre: GridID is out-of-bound GridPointData" &&
           (gridID >= 0) && (gridID < static_cast<int>(this->NumberOfGrids)));
  assert( "pre: GridPointData has not been allocated!" &&
           (this->GridPointData.size() == this->NumberOfGrids ) );
  assert( "pre: GridCellData has not been allocated!" &&
           (this->GridCellData.size() == this->NumberOfGrids ) );

  // Note: The size of these vectors is allocated in SetNumberOfGrids
  if( PointData != NULL )
    {
    assert( "pre: GridPointData[gridID] must be NULL" &&
             this->GridPointData[ gridID ]==NULL );
    this->GridPointData[ gridID ] = vtkPointData::New();
    this->GridPointData[ gridID ]->ShallowCopy( PointData );
    }
  else
    {
    this->GridPointData[ gridID ] = NULL;
    }

  if( CellData != NULL )
    {
    assert( "pre: GridCellData[gridID] must be NULL" &&
            this->GridCellData[gridID]==NULL );
    this->GridCellData[ gridID ]  = vtkCellData::New();
    this->GridCellData[ gridID ]->ShallowCopy( CellData );
    }
  else
    {
    this->GridCellData[ gridID ] = NULL;
    }
}

//------------------------------------------------------------------------------
inline void vtkAbstractGridConnectivity::RegisterGridNodes(
        const int gridID, vtkPoints *nodes )
{
  // Sanity check
  assert( "pre: GridID is out-of-bound GridPointData" &&
          (gridID >= 0) && (gridID < static_cast<int>(this->NumberOfGrids)));
  assert( "pre: GridPoints has not been allocated!" &&
          (this->GridPoints.size() == this->NumberOfGrids) );

  if( nodes != NULL )
    {
    assert( "pre:GridPoints[gridID] must be NULL" &&
            this->GridPoints[gridID]==NULL );
    this->GridPoints[ gridID ] = vtkPoints::New();
    this->GridPoints[ gridID ]->SetDataTypeToDouble();
    this->GridPoints[ gridID ]->ShallowCopy( nodes );
    }
  else
    {
    this->GridPoints[ gridID ] = NULL;
    }
}

#endif /* VTKABSTRACTGRIDCONNECTIVITY_H_ */
