//=========================================================================
//  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.
//=========================================================================

// Local incluides
#include "smtk/simulation/ace3p/Metadata.h"
#include "smtk/simulation/ace3p/Project.h"
#include "smtk/simulation/ace3p/Registrar.h"
#include "smtk/simulation/ace3p/operations/Export.h"
#include "smtk/simulation/ace3p/operations/Read.h"

#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/DirectoryItem.h"
#include "smtk/attribute/FileItem.h"
#include "smtk/attribute/IntItem.h"
#include "smtk/attribute/ResourceItem.h"
#include "smtk/attribute/StringItem.h"
#include "smtk/common/testing/cxx/helpers.h"
#include "smtk/model/Registrar.h"
#include "smtk/model/Resource.h"
#include "smtk/operation/Manager.h"
#include "smtk/operation/Operation.h"
#include "smtk/project/Manager.h"
#include "smtk/project/Project.h"
#include "smtk/project/Registrar.h"
#include "smtk/resource/Manager.h"
#include "smtk/session/vtk/Registrar.h"

#include <boost/filesystem.hpp>

#include <iostream>
#include <set>
#include <string>
#include <vector>

// Reads project generated by TestCreateProject

const int OP_SUCCEEDED = static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED);

int TestExportProject(int /*argc*/, char* /*argv*/[])
{
  // Remove output folder
  std::stringstream ss;
  ss << SCRATCH_DIR << "/cxx"
     << "/export_project";
  std::string outputFolder = ss.str();
  if (boost::filesystem::is_directory(outputFolder))
  {
    boost::filesystem::remove_all(outputFolder);
  }
  boost::filesystem::create_directories(outputFolder);

  // Create sundry managers
  smtk::resource::ManagerPtr resourceManager = smtk::resource::Manager::create();
  smtk::attribute::Registrar::registerTo(resourceManager);
  smtk::model::Registrar::registerTo(resourceManager);
  smtk::session::vtk::Registrar::registerTo(resourceManager);

  smtk::operation::ManagerPtr operationManager = smtk::operation::Manager::create();
  smtk::operation::Registrar::registerTo(operationManager);
  smtk::attribute::Registrar::registerTo(operationManager);
  smtk::session::vtk::Registrar::registerTo(operationManager);

  smtk::project::ManagerPtr projectManager =
    smtk::project::Manager::create(resourceManager, operationManager);
  smtk::project::Registrar::registerTo(projectManager);
  smtk::simulation::ace3p::Registrar::registerTo(projectManager);

  // Todo Next line is currently a no-op. Is it needed?
  smtk::project::Registrar::registerTo(operationManager);
  operationManager->registerResourceManager(resourceManager);

  // Application must set workflows directory
  smtk::simulation::ace3p::Metadata::WORKFLOWS_DIRECTORY = WORKFLOWS_SOURCE_DIR;

  // Read project from file system
  std::shared_ptr<smtk::simulation::ace3p::Project> project;
  std::shared_ptr<smtk::project::Project> projectIntermediate;
  {
    boost::filesystem::path scratchFolder(SCRATCH_DIR);
    boost::filesystem::path projectFolder = scratchFolder / "cxx" / "create_project";
    boost::filesystem::path projectFilename = projectFolder / "create_project.project.smtk";
    std::cout << "Read project file " << projectFilename.string() << std::endl;

    auto readOp = operationManager->create<smtk::simulation::ace3p::Read>();
    smtkTest(readOp != nullptr, "failed to create ace3p project Read operation.");

    bool setFileOK = readOp->parameters()->findFile("filename")->setValue(projectFilename.string());
    smtkTest(setFileOK, "failed to set \"filename\" item.");

    smtkTest(readOp->ableToOperate(), "Read operaion not ready to operate.");
    auto result = readOp->operate();
    int outcome = result->findInt("outcome")->value();
    std::cout << "Read Outcome: "
              << (outcome == OP_SUCCEEDED ? "OP_SUCCEEDED" : std::to_string(outcome)) << "\n";
    smtkTest(outcome == OP_SUCCEEDED, "Read operation failed: " << readOp->log().convertToString());

    smtk::attribute::ResourceItemPtr projectItem = result->findResource("resource");
    smtk::resource::ResourcePtr resource = projectItem->value();

    smtkTest(resource != nullptr, "Read operation failed to return smtk resource instance.");
    projectIntermediate = std::dynamic_pointer_cast<smtk::project::Project>(resource);
    smtkTest(
      projectIntermediate != nullptr, "Read operation failed to return smtk project instance.");
    project = std::dynamic_pointer_cast<smtk::simulation::ace3p::Project>(projectIntermediate);
    smtkTest(project != nullptr, "Read operation failed to return ace3p project instance.");
    smtkTest(project->clean(), "Read operation returned a modified project instance.");
  }

  // Spot check some project features
  {
    smtkTest(
      project->numberOfStages() == 1,
      "Input project should have 1 stage not " << project->numberOfStages());

    auto attResource = project->stage(0)->attributeResource();
    smtkTest(attResource != nullptr, "Stage attribute resource is missing");

    auto modelResource = project->stage(0)->modelResource();
    smtkTest(modelResource != nullptr, "Stage model resource is missing");

    std::string key = smtk::simulation::ace3p::Metadata::METADATA_PROPERTY_KEY;
    smtkTest(
      modelResource->properties().contains<std::string>(key),
      "Model resource missing " << key << " property");
  }

  // Run export operator
  {
    std::cout << "Running export\n";
    auto exportOp = operationManager->create<smtk::simulation::ace3p::Export>();
    bool associateOK = exportOp->parameters()->associate(project);
    smtkTest(associateOK, "failed to associate project to Export operation.");

    bool ok = exportOp->parameters()->findInt("stage-index")->setValue(0);
    smtkTest(ok, "Failed to set stage-index parameter");

    std::cout << "OutputFolder: " << outputFolder << "\n";
    exportOp->parameters()->findDirectory("OutputFolder")->setIsEnabled(true);
    bool setFolder = exportOp->parameters()->findDirectory("OutputFolder")->setValue(outputFolder);
    smtkTest(setFolder, "Export operation failed to set OutputFolder item.")

      std::string outFilePrefix = "foo";
    std::cout << "OutputFilePrefix: " << outFilePrefix << "\n";
    exportOp->parameters()->findString("OutputFilePrefix")->setIsEnabled(true);
    bool setPrefix =
      exportOp->parameters()->findString("OutputFilePrefix")->setValue(outFilePrefix);
    smtkTest(setPrefix, "Export operation failed to set OutputFilePrefix.");

    smtkTest(exportOp->ableToOperate(), "Export operation not ready to operate.");
    auto result = exportOp->operate();
    int outcome = result->findInt("outcome")->value();
    std::cout << "Export Outcome: " << outcome << std::endl;
    smtkTest(
      outcome == OP_SUCCEEDED,
      "Export operation did not succeed: " << exportOp->log().convertToString());
    smtkTest(project->clean(), "Export operation modified the project instance.");
  }

  return 0;
}
