//=========================================================================
//  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 "Read.h"

#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/FileItem.h"
#include "smtk/attribute/IntItem.h"
#include "smtk/attribute/ResourceItem.h"
#include "smtk/attribute/StringItem.h"

#include "smtk/session/aeva/Read_xml.h"
#include "smtk/session/aeva/Resource.h"
#include "smtk/session/aeva/Session.h"
#include "smtk/session/aeva/json/jsonResource.h"
#include "smtk/session/aeva/operators/Import.h"

#include "smtk/common/Paths.h"

#include "smtk/model/SessionIOJSON.h"

#include "vtkPolyData.h"
#include "vtkUnstructuredGrid.h"
#include "vtkXMLPolyDataReader.h"
#include "vtkXMLUnstructuredGridReader.h"

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

#ifdef ERROR
#undef ERROR
#endif

namespace smtk
{
namespace session
{
namespace aeva
{

vtkSmartPointer<vtkDataSet> readVTKData(const std::string& filename)
{
  vtkSmartPointer<vtkDataSet> data;
  // distinguish polydata and unstructured grid by file extension
  std::string ext = smtk::common::Paths::extension(filename);
  if (ext == ".vtp")
  {
    vtkNew<vtkXMLPolyDataReader> reader;
    reader->SetFileName(filename.c_str());
    reader->Update();
    data = reader->GetOutput();
  }
  else if (ext == ".vtu")
  {
    vtkNew<vtkXMLUnstructuredGridReader> reader;
    reader->SetFileName(filename.c_str());
    reader->Update();
    data = reader->GetOutput();
  }
  return data;
}

Read::Result Read::operateInternal()
{
  std::string filename = this->parameters()->findFile("filename")->value();

  // Load file and parse it:
  smtk::model::SessionIOJSON::json j = smtk::model::SessionIOJSON::loadJSON(filename);
  if (j.is_null())
  {
    smtkErrorMacro(log(), "Cannot parse file \"" << filename << "\".");
    return this->createResult(smtk::operation::Operation::Outcome::FAILED);
  }

  // map from model UUID string to json blob as a string.
  json preservedUUIDs = j.at("preservedUUIDs");

  // Create a new resource for the import
  auto rsrc = smtk::session::aeva::Resource::create();
  auto session = smtk::session::aeva::Session::create();
  rsrc->setLocation(filename);
  rsrc->setSession(session);

  smtk::session::aeva::from_json(j, rsrc);

  // The file names in the smtk file are relative to the smtk file itself. We
  // need to append the smtk file's prefix to these values.
  std::string fileDirectory = smtk::common::Paths::directory(filename) + "/";

  for (auto& info : preservedUUIDs)
  {
    std::string aevaDatatype = info["type"];
    std::string subFilename = info["filename"];
    smtk::common::UUID uuid = info["uuid"];
    if (subFilename.empty())
    {
      smtkErrorMacro(this->log(), "Empty filename for preserved data.");
      continue;
    }

    // Turn relative paths into absolute ones (because they are
    // relative to the .smtk file not the current working directory):
    boost::filesystem::path p(subFilename);
    if (!p.is_absolute())
    {
      subFilename.insert(0, fileDirectory);
    }

    if (aevaDatatype == "image")
    {
      vtkSmartPointer<vtkImageData> image = Import::readVTKImage(subFilename);
      if (!image)
      {
        smtkErrorMacro(log(), "Cannot import file \"" << subFilename << "\".");
        return this->createResult(smtk::operation::Operation::Outcome::FAILED);
      }
      session->addStorage(uuid, image);
    }
    if (aevaDatatype == "poly")
    {
      vtkSmartPointer<vtkDataSet> data = readVTKData(subFilename);
      if (!data)
      {
        smtkErrorMacro(log(), "Cannot import file \"" << subFilename << "\".");
        return this->createResult(smtk::operation::Operation::Outcome::FAILED);
      }
      session->addStorage(uuid, data);
    }
  }

  Result result = this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED);

  {
    smtk::attribute::ResourceItem::Ptr created = result->findResource("resource");
    created->setValue(rsrc);
  }

  return result;
}

const char* Read::xmlDescription() const
{
  return Read_xml;
}

void Read::markModifiedResources(Read::Result& res)
{
  auto resourceItem = res->findResource("resource");
  for (auto rit = resourceItem->begin(); rit != resourceItem->end(); ++rit)
  {
    auto resource = std::dynamic_pointer_cast<smtk::resource::Resource>(*rit);

    // Set the resource as unmodified from its persistent (i.e. on-disk) state
    resource->setClean(true);
  }
}

smtk::resource::ResourcePtr read(const std::string& filename)
{
  Read::Ptr read = Read::create();
  read->parameters()->findFile("filename")->setValue(filename);
  Read::Result result = read->operate();
  if (result->findInt("outcome")->value() != static_cast<int>(Read::Outcome::SUCCEEDED))
  {
    return smtk::resource::ResourcePtr();
  }
  return result->findResource("resource")->value();
}

}
}
}
