//=========================================================================
//  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/attribute/Attribute.h"
#include "smtk/attribute/ComponentItem.h"
#include "smtk/attribute/DirectoryItem.h"
#include "smtk/attribute/DoubleItem.h"
#include "smtk/attribute/FileItem.h"
#include "smtk/attribute/IntItem.h"
#include "smtk/attribute/ModelEntityItem.h"
#include "smtk/attribute/ResourceItem.h"
#include "smtk/attribute/StringItem.h"

#include "smtk/io/WriteMesh.h"

#include "smtk/session/rgg/Assembly.h"
#include "smtk/session/rgg/Core.h"
#include "smtk/session/rgg/Pin.h"
#include "smtk/session/rgg/Registrar.h"
#include "smtk/session/rgg/Resource.h"
#include "smtk/session/rgg/Session.h"

#include "smtk/session/rgg/operators/CreateModel.h"
#include "smtk/session/rgg/operators/EditAssembly.h"
#include "smtk/session/rgg/operators/EditCore.h"
#include "smtk/session/rgg/operators/EditDuct.h"
#include "smtk/session/rgg/operators/EditPin.h"
#include "smtk/session/rgg/operators/ExportInp.h"

#include "smtk/session/rgg/json/jsonAssembly.h"
#include "smtk/session/rgg/json/jsonCore.h"
#include "smtk/session/rgg/json/jsonDuct.h"
#include "smtk/session/rgg/json/jsonPin.h"

#include "smtk/mesh/core/Resource.h"
#include "smtk/mesh/resource/Registrar.h"

#include "smtk/model/EntityRef.h"
#include "smtk/model/Group.h"
#include "smtk/model/Model.h"

#include "TestModel.h"

using namespace smtk::session::rgg;
using json = nlohmann::json;

//force to use filesystem version 3
#define BOOST_FILESYSTEM_VERSION 3
#include <boost/filesystem.hpp>
using namespace boost::filesystem;

namespace
{
std::string dataRoot = DATA_DIR;
std::string writeRoot = SCRATCH_DIR;
std::string assygenExe = ASSYGEN_EXE;
std::string coregenExe = COREGEN_EXE;
std::string cubitExe = CUBIT_EXE;

bool findExecutables()
{
  //verify the executables we need to generate a mesh exist
  return (::boost::filesystem::exists(::boost::filesystem::path(assygenExe)) &&
          ::boost::filesystem::exists(::boost::filesystem::path(coregenExe)) &&
          ::boost::filesystem::exists(::boost::filesystem::path(cubitExe)));
}

void cleanup(const std::string& file_path)
{
  //first verify the file exists
  ::boost::filesystem::path path(file_path);
  if (::boost::filesystem::is_regular_file(path))
  {
    //remove the file_path if it exists.
    ::boost::filesystem::remove(path);
  }
}
}

int TestGenerateMesh(int, char* /*argv*/[])
{
  if (findExecutables() == false)
  {
    std::cerr << "Could not locate the necessary executables to run this test.\n";
    std::cerr << "Please set ASSYGEN_EXE, COREGEN_EXE and CUBIT_EXE during configuration.\n";
    std::cerr << "Current values:" << "\n";
    std::cerr << "  ASSYGEN_EXE = " << assygenExe << "\n";
    std::cerr << "  COREGEN_EXE = " << coregenExe << "\n";
    std::cerr << "  CUBIT_EXE = " << cubitExe << "\n";
    return 125;
  }

  smtk::model::ResourcePtr resource = smtk::session::rgg::test::constructTestModel();
  smtk::model::Model model =
    resource->entitiesMatchingFlagsAs<smtk::model::EntityRefArray>(smtk::model::MODEL_ENTITY)[0];

  // Create a resource manager
  smtk::resource::Manager::Ptr resourceManager = smtk::resource::Manager::create();

  // Register resources to the resource manager
  {
    smtk::mesh::Registrar::registerTo(resourceManager);
    smtk::session::rgg::Registrar::registerTo(resourceManager);
  }

  // Create an operation manager
  smtk::operation::Manager::Ptr operationManager = smtk::operation::Manager::create();

  // Register rgg operators to the operation manager
  {
    smtk::session::rgg::Registrar::registerTo(operationManager);
  }

  // Register the resource manager to the operation manager (newly created
  // resources will be automatically registered to the resource manager).
  operationManager->registerResourceManager(resourceManager);

  // Create a "Generate Mesh" operation
  smtk::operation::Operation::Ptr generateMeshOp =
    operationManager->create("rggsession.session.generate_mesh.GenerateMesh");

  if (!generateMeshOp)
  {
    std::cerr << "Could not create \"generate mesh\" operation\n";
    return 1;
  }

  generateMeshOp->parameters()->associate(model.component());

  generateMeshOp->parameters()->findFile("assygen")->setIsEnabled(true);
  generateMeshOp->parameters()->findFile("assygen")->setValue(assygenExe);
  generateMeshOp->parameters()->findFile("coregen")->setIsEnabled(true);
  generateMeshOp->parameters()->findFile("coregen")->setValue(coregenExe);
  generateMeshOp->parameters()->findFile("cubit")->setIsEnabled(true);
  generateMeshOp->parameters()->findFile("cubit")->setValue(cubitExe);

  smtk::operation::Operation::Result generateMeshOpResult = generateMeshOp->operate();

  if (generateMeshOpResult->findInt("outcome")->value() !=
      static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
  {
    std::cerr << "\"generate gesh\" operator failed to generate a mesh\n";
    std::cerr << generateMeshOp->log().convertToString(true) << "\n";
    return 1;
  }

  smtk::mesh::Resource::Ptr meshResource = std::dynamic_pointer_cast<smtk::mesh::Resource>(
    generateMeshOpResult->findResource("resource")->value());

  std::cout<<"meshes: "<<meshResource->meshes().size()<<std::endl;
  std::cout<<"cells: "<<meshResource->cells().size()<<std::endl;
  std::cout<<"points: "<<meshResource->points().size()<<std::endl;
  std::cout<<std::endl;

  // Cubit produces different meshes every time the test is run. The only
  // consistent metric is that there are 9 meshsets that comprise the model's
  // mesh.
  if (meshResource->meshes().size() != 9)
  {
    std::cerr << "\"generate gesh\" operator produced unexpected results\n";
    std::cerr << generateMeshOp->log().convertToString(true) << "\n";
    return 1;
  }

  std::cout<<"3D cells: "<<meshResource->cells(smtk::mesh::Dims3).size()<<std::endl;
  std::cout<<"2D cells: "<<meshResource->cells(smtk::mesh::Dims2).size()<<std::endl;
  std::cout<<"1D cells: "<<meshResource->cells(smtk::mesh::Dims1).size()<<std::endl;
  std::cout<<"0D cells: "<<meshResource->cells(smtk::mesh::Dims0).size()<<std::endl;

  return 0;
}
