
//=========================================================================
//  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 "smtk/io/ImportMesh.h"
#include "smtk/mesh/Collection.h"
#include "smtk/mesh/Manager.h"

#include "smtk/mesh/testing/cxx/helpers.h"

namespace
{

//SMTK_DATA_DIR is a define setup by cmake
std::string data_root = SMTK_DATA_DIR;

smtk::mesh::CollectionPtr load_mesh(smtk::mesh::ManagerPtr mngr)
{
  std::string file_path(data_root);
  file_path += "/mesh/3d/twoassm_out.h5m";

  smtk::mesh::CollectionPtr c = smtk::io::importMesh(file_path, mngr);
  test(c->isValid(), "collection should be valid");

  return c;
}

void cleanup(const smtk::mesh::CollectionPtr& c, smtk::mesh::MeshSet meshset)
{
  smtk::mesh::MeshSet allMeshes = c->meshes();

  const bool is_part_of_collection = smtk::mesh::set_intersect(allMeshes, meshset).is_empty();
  if (is_part_of_collection)
  {
    test(c->removeMeshes(meshset) == true);
  }
}

void verify_create_empty_mesh(const smtk::mesh::CollectionPtr& c)
{
  const std::size_t numMeshesBeforeCreation = c->numberOfMeshes();

  smtk::mesh::CellSet emptyCellSet = smtk::mesh::CellSet(c, smtk::mesh::HandleRange());
  smtk::mesh::MeshSet result = c->createMesh(emptyCellSet);

  test(result.is_empty(), "empty cellset should create empty meshset");
  test(numMeshesBeforeCreation == c->numberOfMeshes(),
    "the number of meshes shouldn't change when adding an empty mesh");
}

void verify_create_mesh_with_cells_from_other_collection(
  smtk::mesh::ManagerPtr mngr, const smtk::mesh::CollectionPtr& c)
{
  //make another collection inside the manager
  smtk::mesh::CollectionPtr otherc = load_mesh(mngr);

  const std::size_t numMeshesBeforeCreation = c->numberOfMeshes();

  smtk::mesh::CellSet cellsFromOtherMesh = otherc->cells();
  smtk::mesh::MeshSet result = c->createMesh(cellsFromOtherMesh);

  test(result.is_empty(), "cellset from different collection should create empty meshset");
  test(numMeshesBeforeCreation == c->numberOfMeshes());

  //unload the second collection from memory
  mngr->removeCollection(otherc);
}

void verify_create_mesh_with_invalid_cell_ids(const smtk::mesh::CollectionPtr& c)
{
  const std::size_t numMeshesBeforeCreation = c->numberOfMeshes();

  smtk::mesh::HandleRange invalidRange;
  invalidRange.insert(0, 5); //insert values 0 to 5;
  smtk::mesh::CellSet invalidCellIds = smtk::mesh::CellSet(c, invalidRange);
  smtk::mesh::MeshSet result = c->createMesh(invalidCellIds);

  test(result.is_empty(), "invalid cellset should create empty meshset");
  test(numMeshesBeforeCreation == c->numberOfMeshes());
}

void verify_create_mesh(const smtk::mesh::CollectionPtr& c)
{
  const std::size_t numMeshesBeforeCreation = c->numberOfMeshes();

  smtk::mesh::CellSet allNonVolumeCells =
    smtk::mesh::set_difference(c->cells(), c->cells(smtk::mesh::Dims3));
  smtk::mesh::MeshSet result = c->createMesh(allNonVolumeCells);

  test(result.size() == 1, "valid cellset should create meshset with single mesh");
  test((numMeshesBeforeCreation + 1) == c->numberOfMeshes());

  test(result.cells().size() == allNonVolumeCells.size());
  test(result.cells() == allNonVolumeCells);

  cleanup(c, result);
}

void verify_create_mesh_num_meshes(const smtk::mesh::CollectionPtr& c)
{
  const std::size_t numMeshesBeforeCreation = c->numberOfMeshes();

  //validate that for each mesh we create the numberOfMeshes is correct
  std::vector<smtk::mesh::MeshSet> results;
  for (int i = 0; i < 3; ++i)
  {
    smtk::mesh::DimensionType dt = static_cast<smtk::mesh::DimensionType>(i);
    results.push_back(c->createMesh(c->cells(dt)));
  }
  test((numMeshesBeforeCreation + 3) == c->numberOfMeshes());

  for (std::size_t i = 0; i < results.size(); ++i)
  {
    cleanup(c, results[i]);
  }
}

void verify_create_mesh_updated_mesh_queries(const smtk::mesh::CollectionPtr& c)
{
  const std::size_t numMeshesBeforeCreation = c->numberOfMeshes();

  smtk::mesh::CellSet allNonVolumeCells =
    smtk::mesh::set_difference(c->cells(), c->cells(smtk::mesh::Dims3));
  smtk::mesh::MeshSet result = c->createMesh(allNonVolumeCells);

  test((numMeshesBeforeCreation + 1) == c->numberOfMeshes());

  //The meshset returned from create should entirely be contained within
  //all the meshes in the collection
  smtk::mesh::MeshSet intersect = smtk::mesh::set_intersect(c->meshes(), result);
  test(intersect == result);

  //The meshset returned from create should entirely be contained within
  //all the meshes that have 2d cells
  intersect = smtk::mesh::set_intersect(c->meshes(smtk::mesh::Dims2), result);
  test(intersect == result);

  cleanup(c, result);
}

void verify_create_mesh_num_cells(const smtk::mesh::CollectionPtr& c)
{
  const std::size_t numCellsBeforeCreation = c->cells().size();

  smtk::mesh::CellSet allNonVolumeCells =
    smtk::mesh::set_difference(c->cells(), c->cells(smtk::mesh::Dims3));
  smtk::mesh::MeshSet result = c->createMesh(allNonVolumeCells);

  test(result.size() == 1, "valid cellset should create meshset with single mesh");
  test(numCellsBeforeCreation == c->cells().size());

  cleanup(c, result);
}

void verify_create_mesh_marks_modified()
{
  //verify that a collection loaded from file is not marked as modified
  smtk::mesh::ManagerPtr mngr = smtk::mesh::Manager::create();
  smtk::mesh::CollectionPtr c = load_mesh(mngr);
  test(c->isModified() == false, "collection loaded from disk shouldn't be modified");

  //verify a failure to create meshes doesn't mark as modify
  verify_create_mesh_with_invalid_cell_ids(c);
  test(c->isModified() == false, "collection loaded from disk shouldn't be modified");

  //verify that creating a mesh does mark update modify flag
  verify_create_mesh(c);
  test(c->isModified() == true, "collection should be marked as modified now");
}
}

int UnitTestCreateMesh(int, char** const)
{
  smtk::mesh::ManagerPtr mngr = smtk::mesh::Manager::create();
  smtk::mesh::CollectionPtr c = load_mesh(mngr);

  verify_create_empty_mesh(c);
  verify_create_mesh_with_cells_from_other_collection(mngr, c);
  verify_create_mesh_with_invalid_cell_ids(c);

  verify_create_mesh(c);

  verify_create_mesh_num_meshes(c);
  verify_create_mesh_updated_mesh_queries(c);

  verify_create_mesh_num_cells(c);

  verify_create_mesh_marks_modified();

  return 0;
}
