//=========================================================================
//  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/model/operators/ExportSMTKModel.h"

#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/Definition.h"
#include "smtk/attribute/FileItem.h"
#include "smtk/attribute/IntItem.h"
#include "smtk/attribute/ModelEntityItem.h"
#include "smtk/common/CompilerInformation.h"
#include "smtk/io/ExportJSON.h"
#include "smtk/io/ExportJSON.txx"
#include "smtk/mesh/Collection.h"
#include "smtk/mesh/Manager.h"
#include "smtk/model/Session.h"
#include "smtk/model/CellEntity.h"
#include "smtk/model/Manager.h"
#include "smtk/model/Model.h"

SMTK_THIRDPARTY_PRE_INCLUDE
#include "boost/filesystem.hpp"
SMTK_THIRDPARTY_POST_INCLUDE

#include "cJSON.h"
#include <fstream>

using namespace smtk::model;
using namespace boost::filesystem;

namespace smtk {
  namespace model {

OperatorResult ExportSMTKModel::operateInternal()
{
  smtk::attribute::FileItemPtr filenameItem = this->findFile("filename");
  smtk::attribute::IntItemPtr flagsItem = this->findInt("flags");

  Models models = this->m_specification->associatedModelEntities<Models>();
  if (models.empty())
    {
    smtkErrorMacro(this->log(), "No valid models selected for export.");
    return this->createResult(OPERATION_FAILED);
    }

  std::string filename = filenameItem->value();
  if (filename.empty())
    {
    smtkErrorMacro(this->log(), "A filename must be provided.");
    return this->createResult(OPERATION_FAILED);
    }

  std::ofstream jsonFile(filename.c_str(), std::ios::trunc);
  if (!jsonFile.good())
    {
    smtkErrorMacro(this->log(), "Could not open file \"" << filename << "\".");
    return this->createResult(OPERATION_FAILED);
    }

  cJSON* top = cJSON_CreateObject();
  std::string smtkfilepath = path(filename).parent_path().string();
  std::string smtkfilename = path(filename).stem().string();

  // Add the output smtk model name to the model "smtk_url", so that the individual session can
  // use that name to construct a filename for saving native models of the session.
  Models::iterator modit;
  for(modit = models.begin(); modit != models.end(); ++modit)
    {
    modit->setStringProperty("smtk_url", filename);

    // we also want to write out the meshes to new "write_locations"
    std::vector<smtk::mesh::CollectionPtr> collections =
      this->manager()->meshes()->associatedCollections(*modit);
    std::vector<smtk::mesh::CollectionPtr>::const_iterator cit;
    for(cit = collections.begin(); cit != collections.end(); ++cit)
      {
      std::ostringstream outmeshname;
      outmeshname << smtkfilename << "_" << (*cit)->name() << ".h5m";
      std::string write_path = (path(smtkfilepath) / path(outmeshname.str())).string();
      smtk::common::FileLocation wfLocation(write_path, smtkfilepath);
      (*cit)->writeLocation(wfLocation);
      }
    }

  smtk::io::ExportJSON::forManagerSessionPartial(
    this->session()->sessionId(),
    this->m_specification->associatedModelEntityIds(),
    top, this->manager(), true, smtkfilepath);

  char* json = cJSON_Print(top);
  jsonFile << json;
  free(json);
  cJSON_Delete(top);
  jsonFile.close();

  // Now we need to do some work to reset the url property of models since
  // during export, the property may be changed to be relative path, and we want
  // to set it back to be absolute path to display
  if(!smtkfilepath.empty())
    {
    for(modit = models.begin(); modit != models.end(); ++modit)
      {
      if(modit->hasStringProperty("url"))
        {
        path url(modit->stringProperty("url")[0]);
        if (!url.string().empty() && !url.is_absolute())
          {
          url = smtkfilepath / url;
          url = canonical(url, smtkfilepath);
          // set the url property to be consistent with "modelFiles" record when written out
          modit->setStringProperty("url", url.string());
          }
        }

      // After a collection is written out successfully or not,
      // we want to clear the writeLocation so that it won't be re-written
      // unless explicitly set again.
      std::vector<smtk::mesh::CollectionPtr> collections =
        this->manager()->meshes()->associatedCollections(*modit);
      std::vector<smtk::mesh::CollectionPtr>::const_iterator cit;
      for(cit = collections.begin(); cit != collections.end(); ++cit)
        {
        (*cit)->writeLocation(std::string());
        }
      }
    }

  return this->createResult(OPERATION_SUCCEEDED);
}

void ExportSMTKModel::generateSummary(OperatorResult& res)
{
  std::ostringstream msg;
  int outcome = res->findInt("outcome")->value();
  smtk::attribute::FileItemPtr fitem = this->findFile("filename");
  msg << this->specification()->definition()->label();
  if (outcome == static_cast<int>(OPERATION_SUCCEEDED))
    {
    msg << ": wrote \"" << fitem->value(0) << "\"";
    smtkInfoMacro(this->log(), msg.str());
    }
  else
    {
    msg << ": failed to write \"" << fitem->value(0) << "\"";
    smtkErrorMacro(this->log(), msg.str());
    }
}

  } //namespace model
} // namespace smtk

#include "smtk/model/ExportSMTKModel_xml.h"

smtkImplementsModelOperator(
  SMTKCORE_EXPORT,
  smtk::model::ExportSMTKModel,
  export_smtk_model,
  "export smtk model",
  ExportSMTKModel_xml,
  smtk::model::Session);
