/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         https://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/

#include "itkQuadEdgeMesh.h"

int
itkQuadEdgeMeshTest3(int, char *[])
{
  using PixelType = double;
  using MeshType = itk::QuadEdgeMesh<PixelType, 3>;
  using CellType = MeshType::CellType;
  using LineType = itk::QuadEdgeMeshLineCell<CellType>;
  using QEPolygonCellType = itk::QuadEdgeMeshPolygonCell<CellType>;

  auto mesh = MeshType::New();

  MeshType::PointType point0;
  MeshType::PointType point1;
  MeshType::PointType point2;
  MeshType::PointType point3;

  point0[0] = -1;
  point0[1] = -1;
  point0[2] = -1;
  point1[0] = 1;
  point1[1] = 1;
  point1[2] = -1;
  point2[0] = 1;
  point2[1] = -1;
  point2[2] = 1;
  point3[0] = -1;
  point3[1] = 1;
  point3[2] = 1;

  mesh->SetPoint(0, point0);
  mesh->SetPoint(1, point1);
  mesh->SetPoint(2, point2);
  mesh->SetPoint(3, point3);

  CellType::CellAutoPointer cellpointer;
  QEPolygonCellType *       poly;
  LineType *                edge;

  poly = new QEPolygonCellType(3);
  cellpointer.TakeOwnership(poly);
  cellpointer->SetPointId(0, 0);
  cellpointer->SetPointId(1, 1);
  cellpointer->SetPointId(2, 2);
  mesh->SetCell(0, cellpointer);

  poly = new QEPolygonCellType(3);
  cellpointer.TakeOwnership(poly);
  cellpointer->SetPointId(0, 0);
  cellpointer->SetPointId(1, 2);
  cellpointer->SetPointId(2, 3);
  mesh->SetCell(1, cellpointer);

  poly = new QEPolygonCellType(3);
  cellpointer.TakeOwnership(poly);
  cellpointer->SetPointId(0, 0);
  cellpointer->SetPointId(1, 3);
  cellpointer->SetPointId(2, 1);
  mesh->SetCell(2, cellpointer);

  poly = new QEPolygonCellType(3);
  // FIXME ALEX: write REAL test code here for coverage.
  poly->GetNameOfClass();
  poly->GetEdgeRingEntry()->GetRight();
  poly->GetEdgeRingEntry()->GetLeft();
  poly->GetEdgeRingEntry()->GetIdent();
  poly->GetType();
  poly->GetDimension();
  // up to here.
  cellpointer.TakeOwnership(poly);
  cellpointer->SetPointId(0, 3);
  cellpointer->SetPointId(1, 2);
  cellpointer->SetPointId(2, 1);
  mesh->SetCell(3, cellpointer);

  edge = new LineType;
  cellpointer.TakeOwnership(edge);
  cellpointer->SetPointId(0, 0);
  cellpointer->SetPointId(1, 1);
  mesh->SetCell(4, cellpointer);

  edge = new LineType;
  cellpointer.TakeOwnership(edge);
  cellpointer->SetPointId(0, 1);
  cellpointer->SetPointId(1, 2);
  mesh->SetCell(5, cellpointer);

  edge = new LineType;
  cellpointer.TakeOwnership(edge);
  cellpointer->SetPointId(0, 2);
  cellpointer->SetPointId(1, 0);
  mesh->SetCell(6, cellpointer);

  edge = new LineType;
  cellpointer.TakeOwnership(edge);
  cellpointer->SetPointId(0, 1);
  cellpointer->SetPointId(1, 3);
  mesh->SetCell(7, cellpointer);

  edge = new LineType;
  cellpointer.TakeOwnership(edge);
  cellpointer->SetPointId(0, 3);
  cellpointer->SetPointId(1, 2);
  mesh->SetCell(8, cellpointer);

  edge = new LineType;
  edge->GetNameOfClass();
  edge->GetType();
  edge->GetDimension();
  cellpointer.TakeOwnership(edge);
  cellpointer->SetPointId(0, 3);
  cellpointer->SetPointId(1, 0);
  mesh->SetCell(9, cellpointer);

  std::cout << "numPoints = " << mesh->GetNumberOfPoints() << std::endl;
  std::cout << "numCells  = " << mesh->GetNumberOfCells() << std::endl;

  using PointIterator = MeshType::PointsContainer::ConstIterator;
  PointIterator pointIterator = mesh->GetPoints()->Begin();
  PointIterator pointEnd = mesh->GetPoints()->End();

  while (pointIterator != pointEnd)
  {
    std::cout << pointIterator.Value() << std::endl;
    pointIterator++;
  }

  using CellIterator = MeshType::CellsContainer::ConstIterator;

  CellIterator cellIterator = mesh->GetCells()->Begin();
  CellIterator cellEnd = mesh->GetCells()->End();

  while (cellIterator != cellEnd)
  {
    CellType * cell = cellIterator.Value();
    std::cout << cell->GetNumberOfPoints() << std::endl;
    ++cellIterator;
  }

  cellIterator = mesh->GetCells()->Begin();
  cellEnd = mesh->GetCells()->End();

  while (cellIterator != cellEnd)
  {
    CellType * cell = cellIterator.Value();

    std::cout << "cell with " << cell->GetNumberOfPoints();
    std::cout << " points   : ";

    using PointIdIterator = CellType::PointIdIterator;

    PointIdIterator pointIditer = cell->PointIdsBegin();
    PointIdIterator pointIdend = cell->PointIdsEnd();

    if (pointIditer != pointIdend)
    {
      std::cout << *pointIditer;
      ++pointIditer;
    }

    while (pointIditer != pointIdend)
    {
      std::cout << " -> " << *pointIditer;
      ++pointIditer;
    }

    std::cout << std::endl;
    ++cellIterator;
  }

  // try to clear the populated mesh
  mesh->Clear();

  std::cout << "Mesh3Test passed" << std::endl;
  return EXIT_SUCCESS;
}
