//=========================================================================
//  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/simulation/ace3p/operations/Create.h"

// Project includes
#include "smtk/simulation/ace3p/Metadata.h"
#include "smtk/simulation/ace3p/Project.h"
#include "smtk/simulation/ace3p/operations/NewStage.h"
#include "smtk/simulation/ace3p/utility/AttributeUtils.h"

#include "smtk/simulation/ace3p/Create_xml.h"

// SMTK includes
#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/DirectoryItem.h"
#include "smtk/attribute/FileItem.h"
#include "smtk/attribute/IntItem.h"
#include "smtk/attribute/Resource.h"
#include "smtk/attribute/ResourceItem.h"
#include "smtk/attribute/StringItem.h"
#include "smtk/io/AttributeReader.h"
#include "smtk/io/Logger.h"
#include "smtk/project/Manager.h"
#include "smtk/resource/Manager.h"

#include <boost/filesystem.hpp>

#include <cassert>
#include <iostream>
#include <string>

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

namespace smtk
{
namespace simulation
{
namespace ace3p
{

smtk::operation::Operation::Result Create::operateInternal()
{
  auto& logger = this->log();

  // Make sure the attribute template (ace3p.sbt) exists.
  if (Metadata::WORKFLOWS_DIRECTORY.empty())
  {
    smtkErrorMacro(logger, "Internal Error: Metadata::WORKFLOWS_DIRECTORY not defined.");
    return this->createResult(smtk::operation::Operation::Outcome::FAILED);
  }

  boost::filesystem::path workflowDir(Metadata::WORKFLOWS_DIRECTORY);
  boost::filesystem::path sbtPath = workflowDir / "ACE3P.sbt";
  if (!boost::filesystem::exists(sbtPath))
  {
    smtkErrorMacro(logger, "Error: expecing sbt file at " << sbtPath.string());
    return this->createResult(smtk::operation::Operation::Outcome::FAILED);
  }

  // Initialize the project directory
  std::string projectFolder = this->parameters()->findDirectory("location")->value();
  boost::filesystem::path projectPath(projectFolder);

  // Clear any existing directory contents
  if (boost::filesystem::exists(projectPath) && !boost::filesystem::is_empty(projectPath))
  {
    bool overwrite = this->parameters()->find("overwrite")->isEnabled();
    if (!overwrite)
    {
      smtkErrorMacro(
        logger,
        "Error: Cannot create project in existing directory ("
          << projectPath.string() << ") unless overwrite flag is set.");
      return this->createResult(smtk::operation::Operation::Outcome::FAILED);
    }
    boost::filesystem::remove_all(projectPath);
  }

  if (!boost::filesystem::exists(projectPath))
  {
    boost::filesystem::create_directories(projectPath);
  }

  // Create ace3p project
  auto project = smtk::simulation::ace3p::Project::create();
  if (!project)
  {
    smtkErrorMacro(
      this->log(), "Cannot create project type \"" << Metadata::PROJECT_TYPENAME << "\"");
    return this->createResult(smtk::operation::Operation::Outcome::FAILED);
  }
  // Set the project name and location
  std::string projectName = projectPath.filename().string();
  project->setName(projectName);
  std::string filename = projectFolder + "/" + projectName + ".project.smtk";
  project->setLocation(filename);

  // Must set project operations manager to avoid seg fault writing project.
  // Todo is this a workaround for something missing elsewhere?
  project->operations().setManager(m_manager);

  // Create assets folder
  boost::filesystem::path assetsFolderPath = projectPath / "assets";
  if (!boost::filesystem::create_directories(assetsFolderPath))
  {
    smtkErrorMacro(
      this->log(),
      "Failed to create project assets directory: " << assetsFolderPath.string() << ".");
    return this->createResult(smtk::operation::Operation::Outcome::FAILED);
  }

  // Create first stage
  auto stageOp = this->manager()->create<NewStage>();
  auto stageParams = stageOp->parameters();
  stageParams->associate(project);

  std::string stageName = this->parameters()->findString("stage-name")->value();
  stageParams->findString("stage-name")->setValue(stageName);

  std::string meshFilename = this->parameters()->findFile("analysis-mesh")->value();
  stageParams->findString("assign-mesh")->setValue("open-new-mesh");
  stageParams->findFile("analysis-mesh-file")->setValue(meshFilename);

  auto stageResult = stageOp->operate();
  int stageOutcome = stageResult->findInt("outcome")->value(0);
  if (stageOutcome != OP_SUCCEEDED)
  {
    smtkErrorMacro(logger, "Error creating stage");
    return this->createResult(smtk::operation::Operation::Outcome::FAILED);
  }

  auto result = this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED);
  {
    smtk::attribute::ResourceItem::Ptr created = result->findResource("resource");
    bool ok = created->setValue(project);
    assert(ok);
  }

  return result;
}

smtk::operation::Operation::Specification Create::createSpecification()
{
  Specification spec = this->smtk::operation::XMLOperation::createSpecification();
  //std::cout << "Create Op:\n" << this->xmlDescription() << std::endl;
  return spec;
}

const char* Create::xmlDescription() const
{
  return Create_xml;
}
} // namespace ace3p
} // namespace simulation
} // namespace smtk
