//=========================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt 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.
//=========================================================================


#include <vtkCMBMeshServer.h>
#include <vtkDiscreteModel.h>
#include <vtkCMBModelEdgeMesh.h>
#include <vtkCMBModelEntityMesh.h>
#include <vtkCMBModelFaceMesh.h>
#include <vtkCMBModelReadOperator.h>
#include <vtkDiscreteModelWrapper.h>
#include <vtkEdgeSplitOperator.h>
#include <vtkMergeOperator.h>
#include <vtkModelEdge.h>
#include <vtkModelFace.h>
#include <vtkModelItemIterator.h>
#include <vtkPolyData.h>
#include <vtkSmartPointer.h>
#include <vtkPointData.h>
#include <vtkCellData.h>
#include <vtkIntArray.h>
#include <vtkIdTypeArray.h>
#include "cmbFaceMeshHelper.h"

using namespace discreteFaceMesherClasses;

// This tests the events for the SimBuilder Mesh.

int Check2DModel(const char* fileName)
{
  int numberOfErrors = 0;
  vtkDiscreteModelWrapper* modelWrapper = vtkDiscreteModelWrapper::New();

  vtkDiscreteModel* model = modelWrapper->GetModel();

  vtkSmartPointer<vtkCMBModelReadOperator> reader =
    vtkSmartPointer<vtkCMBModelReadOperator>::New();
  reader->SetFileName(fileName);
  reader->Operate(modelWrapper);
  if(reader->GetOperateSucceeded() == false)
    {
    vtkGenericWarningMacro("Could not load file " << fileName);
    return 1;
    }

  vtkSmartPointer<vtkCMBMeshServer> mesh =
    vtkSmartPointer<vtkCMBMeshServer>::New();
  mesh->Initialize(model);

  vtkSmartPointer<vtkModelItemIterator> edges;
  edges.TakeReference(model->NewIterator(vtkModelEdgeType));

  vtkSmartPointer<vtkModelItemIterator> faces;
  faces.TakeReference(model->NewIterator(vtkModelFaceType));

  mesh->SetGlobalLength(1);
  mesh->SetGlobalMinimumAngle(20);
  for(edges->Begin();!edges->IsAtEnd();edges->Next())
    {
    vtkModelEdge* edge = vtkModelEdge::SafeDownCast(edges->GetCurrentItem());
    vtkCMBModelEdgeMesh* edgeMesh = vtkCMBModelEdgeMesh::SafeDownCast(
      mesh->GetModelEntityMesh(edge));
    edgeMesh->BuildModelEntityMesh(false);
    }
  for(faces->Begin();!faces->IsAtEnd();faces->Next())
    {
    vtkModelFace* face = vtkModelFace::SafeDownCast(faces->GetCurrentItem());
    vtkCMBModelFaceMesh* faceMesh = vtkCMBModelFaceMesh::SafeDownCast(
      mesh->GetModelEntityMesh(face));
    faceMesh->BuildModelEntityMesh(false);
    }

  //lets now confirm each faces model mapping on the mesh
  //We are going to confirm the mapping by verifying the number of
  //each model types per face on the points.

  //face ids are 26,27,28,29. so we are going to get the correct index
  //by subtracting 26 from the id. so 26 == 0, ... 29 == 3
  //index 1 == verts, 3 == edge, 6 == face

  //face 26 == 2 verts, 12 edges, 10 face points
  //face 27 == 5 verts, 9 edges, 7 face points
  //face 28 == 3 verts, 7 edges, 3 face points
  //face 29 == 4 verts, 14 edges, 8 face points
  const vtkIdType correctPointIdSums[4][7]={
    {0,2,0,12,0,0,10},
    {0,5,0,9,0,0,7},
    {0,3,0,7,0,0,3},
    {0,4,0,14,0,0,8}
    };


  vtkIdType foundPointIdSums[4][7] = {
    {0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0}
    };

  //verify points
  for(faces->Begin();!faces->IsAtEnd();faces->Next())
    {
    vtkModelFace* face = vtkModelFace::SafeDownCast(faces->GetCurrentItem());
    vtkCMBModelFaceMesh* faceMesh = vtkCMBModelFaceMesh::SafeDownCast(mesh->GetModelEntityMesh(face));
    vtkPolyData *meshTmp = faceMesh->GetModelEntityMesh();

    vtkIdType faceId = face->GetUniquePersistentId() - 26;
    vtkIntArray *types = vtkIntArray::SafeDownCast(
      meshTmp->GetPointData()->GetArray(
      ModelFaceRep::Get2DAnalysisPointModelTypesString()));
    for (vtkIdType i=0; i < types->GetNumberOfTuples(); ++i)
      {
      foundPointIdSums[faceId][types->GetValue(i)]++;
      }

    //verify this face is correct
    bool valid = false;
    for (vtkIdType i=0; i < 7; ++i)
      {
      valid = foundPointIdSums[faceId][i] == correctPointIdSums[faceId][i];
      if ( valid == false )
        {
        cerr << "ERROR: Point Mapping failed on FaceId: " << 26 + faceId << endl;
        cerr << "i is :" << i << endl;
        cerr << "foundCellIdSums: " << foundPointIdSums[faceId][i] << endl;
        cerr << "correctCellIdSums: " << correctPointIdSums[faceId][i] << endl;
        cerr << endl;
        numberOfErrors++;
        break;
        }
      }
    }

  //now that the point mapping has been checked we are going to verify the cell
  //mapping. This is going to be done by looking at the edges. Each edge of the triangle
  //is assigned to an edge or face. so we can do a sum check on these too. The summed
  //lookup table is:
  // 0 edge model Id's on the triangle = index 18
  // 1 edge model Id's on the triangle = index 15
  // 2 edge model Id's on the triangle = inde 12
  // 3 edge model Id's on the triangle = inde 9

  //face 26 == 1 2 edge cell, 12 1 edge cells, 19 face cells
  //face 27 == 2 2 edge cell, 10 1 edge cells, 14 face cells
  //face 28 == 2 2 edge cell, 6 1 edge cells, 6 face cells
  //face 29 == 1 2 edge cell, 16 1 edge cells, 17 face cells


  const int correctCellIdSums[4][19]={
    {0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,12,0,0,19},
    {0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,10,0,0,14},
    {0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,6,0,0,6},
    {0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,16,0,0,17}
    };

  int foundCellIdSums[4][19]={
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    };

  //verify cells
  for(faces->Begin();!faces->IsAtEnd();faces->Next())
    {
    vtkModelFace* face = vtkModelFace::SafeDownCast(faces->GetCurrentItem());
    vtkCMBModelFaceMesh* faceMesh = vtkCMBModelFaceMesh::SafeDownCast(mesh->GetModelEntityMesh(face));
    vtkPolyData *meshTmp = faceMesh->GetModelEntityMesh();

    vtkIdType faceId = face->GetUniquePersistentId() - 26;
    vtkIntArray *types = vtkIntArray::SafeDownCast(
      meshTmp->GetCellData()->GetArray(
      ModelFaceRep::Get2DAnalysisCellModelTypesString()));
    for (vtkIdType i=0; i < types->GetNumberOfTuples(); ++i)
      {
      int sum=0;
      for(vtkIdType j=0; j < types->GetNumberOfComponents(); ++j)
        {
        sum += types->GetComponent(i,j);
        }
      foundCellIdSums[faceId][sum]++;
      }

    //verify this face is correct
    bool valid = false;
    for (vtkIdType i=0; i < 19; ++i)
      {
      valid = foundCellIdSums[faceId][i] == correctCellIdSums[faceId][i];
      if ( valid == false )
        {
        cerr << "ERROR: Cell Mapping failed on FaceId: " << 26 + faceId << endl;
        cerr << "i is :" << i << endl;
        cerr << "foundCellIdSums: " << foundCellIdSums[faceId][i] << endl;
        cerr << "correctCellIdSums: " << correctCellIdSums[faceId][i] << endl;
        cerr << endl;
        numberOfErrors++;
        break;
        }
      }
    }

  model->Reset();
  modelWrapper->Delete();

  return numberOfErrors;
}

int main(int argc, char ** argv)
{
  if(argc != 2)
    {
    vtkGenericWarningMacro("Not enough arguments -- need to specify a 2D CMB model file.");
    return 1;
    }
  int errors = Check2DModel(argv[1]);
  //errors += Check3DModel(argv[2]);
  std::cout << "Finished with " << errors << " errors.\n";
  return errors;
}
