//=============================================================================
// 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/session/rgg/operators/ReadRXFFileHelper.h"

#include "smtk/session/rgg/operators/ReadRXFFile.h"

#define PUGIXML_HEADER_ONLY
#include "pugixml/src/pugixml.cpp"

#include "smtk/io/Logger.h"

#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/DoubleItem.h"
#include "smtk/attribute/IntItem.h"
#include "smtk/attribute/StringItem.h"

#include "smtk/session/rgg/Assembly.h"
#include "smtk/session/rgg/Core.h"
#include "smtk/session/rgg/Duct.h"
#include "smtk/session/rgg/Material.h"
#include "smtk/session/rgg/Pin.h"

#include "smtk/session/rgg/operators/CreateModel.h"
#include "smtk/session/rgg/operators/EditAssembly.h"
#include "smtk/session/rgg/operators/EditPin.h"
#include "smtk/session/rgg/operators/EditDuct.h"
#include "smtk/session/rgg/operators/OperationHelper.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/model/EntityRef.h"
#include "smtk/model/Group.h"
#include "smtk/model/Instance.h"
#include "smtk/model/Model.h"
#include "smtk/model/Resource.h"

#include <limits>
#include <string>

using rggAssembly = smtk::session::rgg::Assembly;
using rggCore = smtk::session::rgg::Core;
using rggDuct = smtk::session::rgg::Duct;
using rggPin = smtk::session::rgg::Pin;
using json = nlohmann::json;

// We use either STL regex or Boost regex, depending on support. These flags
// correspond to the equivalent logic used to determine the inclusion of Boost's
// regex library.
#if defined(SMTK_CLANG) ||                                                                         \
  (defined(SMTK_GCC) && __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) ||                 \
  defined(SMTK_MSVC)
#include <regex>
using std::regex;
using std::sregex_token_iterator;
using std::regex_replace;
using std::regex_search;
using std::regex_match;
#else
#include <boost/regex.hpp>
using boost::regex;
using boost::sregex_token_iterator;
using boost::regex_replace;
using boost::regex_search;
using boost::regex_match;
#endif
static const double cos30 = 0.86602540378443864676372317075294;
static const double cos60 = 0.5;
// static const int degreesHex[6] = { -120, -60, 0, 60, 120, 180 };
// static const int degreesRec[4] = { -90, 0, 90, 180 };
// 0', 60', 120, 180', 240', 300'
static const double cosSinAngles[6][2] = { { 1.0, 0.0 }, { cos60, -cos30 }, { -cos60, -cos30 },
  { -1.0, 0.0 }, { -cos60, cos30 }, { cos60, cos30 } };

namespace
{
struct ComparePairOfZOriginAndPinPiece
{
  bool operator()(const std::pair<int, rggPin::Piece>& lhs,
                  const std::pair<int, rggPin::Piece>& rhs) const
  {
    return lhs.first < rhs.first;
  }
};
}
using namespace smtk::model;

namespace smtk
{
namespace session
{
namespace rgg
{
using namespace XMLAttribute;
double ReadRXFFileHelper::s_axialMeshSize = 0;
double ReadRXFFileHelper::s_edgeInterval = 0;

namespace
{
bool read(const pugi::xml_node& node, std::string attName, std::string& v)
{
  pugi::xml_attribute att = node.attribute(attName.c_str());
  if (!att)
  {
    return false;
  }
  v = std::string(att.value());
  return true;
}

bool read(const pugi::xml_node& node, std::string attName, double& v)
{
  std::string str;
  if (!read(node, attName, str))
    return false;
  v = std::atof(str.c_str());
  return true;
}

bool read(const pugi::xml_node& node, std::string attName, int& v)
{
  std::string str;
  if (!read(node, attName, str))
    return false;
  v = std::atoi(str.c_str());
  return true;
}

bool read(const pugi::xml_node& node, std::string attName, unsigned int& v)
{
  std::string str;
  if (!read(node, attName, str))
    return false;
  v = std::stoul(str.c_str());
  return true;
}

/*
bool read(pugi::xml_node& node, std::string attName, bool& v)
{
  std::string str;
  if (!read(node, attName, str))
    return false;
  v = static_cast<bool>(std::atoi(str.c_str()));
  return true;
}
*/

bool read(const pugi::xml_node& node, std::string attName, double* v, int size)
{
  std::string str;
  if (!read(node, attName, str))
    return false;
  regex re(",");
  sregex_token_iterator it(str.begin(), str.end(), re, -1), last;
  for (int i = 0; (it != last && i < size); ++it, ++i)
  {
    v[i] = std::atof(it->str().c_str());
  }
  return true;
}

bool read(const pugi::xml_node& node, std::string attName, std::vector<std::string>& values)
{
  std::string str;
  if (!read(node, attName, str))
    return false;
  regex re(";");
  sregex_token_iterator it(str.begin(), str.end(), re, -1), last;
  for (; it != last; ++it)
  {
    values.push_back(it->str());
  }
  return true;
}

bool readColor(const pugi::xml_node& node, std::string attName, std::vector<double>& color)
{
  pugi::xml_attribute att = node.attribute(attName.c_str());
  if (!att)
  {
    return false;
  }
  std::string ts(att.value());
  std::vector<double> values;
  regex re(",");
  sregex_token_iterator it(ts.begin(), ts.end(), re, -1), last;
  for (int i = 0; (it != last && i < 4); ++it, ++i)
  {
    values.push_back(std::atof(it->str().c_str()));
  }

  color.clear();
  if (values.size() == 4)
  {
    color = values;
    return true;
  }
  else
  {
    color = { 1, 1, 1, 1 };
    return false;
  }
}

size_t materialNameToIndex(std::string materialN, const std::vector<std::string>& materialList)
{
  auto iter = std::find(materialList.begin(), materialList.end(), materialN);
  if (iter != materialList.end())
  {
    size_t pos = std::distance(materialList.begin(), iter);
    return pos;
  }
  if (!materialN.empty())
  {
    smtkErrorMacro(smtk::io::Logger::instance().instance(), "Cannot find index for"
                                                            " material '"
        << materialN << "'. Set it to be the first defined material");
  }
  return 0;
}
}

bool ReadRXFFileHelper::parseMaterial(const pugi::xml_node& node, smtk::model::EntityRef& model)
{
  smtk::model::StringList materialsList, materialsDList;
  // The first one will always be no cell material
  model.setFloatProperty("NoCellMaterial",{1,1,1,1});
  bool r = true;
  for (auto materialN = node.child((MATERIAL_TAG.c_str())); materialN;
       materialN = materialN.next_sibling(MATERIAL_TAG.c_str()))
  {
    std::string name, label, description;
    std::vector<double> color;
    r &= read(materialN, NAME_TAG.c_str(), name);
    r &= read(materialN, LABEL_TAG.c_str(), label);
    read(materialN, DESCRIPTION_TAG.c_str(), description); // optional
    r &= readColor(materialN, COLOR_TAG.c_str(), color);
    materialsList.push_back(name);
    // TODO: add a parsable material description
    if (!description.empty())
    {
      materialsDList.push_back(description);
    }
    model.setStringProperty(name, label);
    model.setFloatProperty(name, color);
  }
  model.setStringProperty("materials", materialsList);
  if (materialsDList.size())
  {
    model.setStringProperty(Material::label, materialsDList);
  }
  return r;
}

bool ReadRXFFileHelper::parsePins(
  const pugi::xml_node& rootElement, smtk::model::EntityRef& model, smtk::model::EntityRefArray& newPins)
{
  bool r(true);
  const pugi::xml_node pinsNode = rootElement.child(PINS_TAG.c_str());
  for (auto pinN = pinsNode.child(PINCELL_TAG.c_str()); pinN;
       pinN = pinN.next_sibling(PINCELL_TAG.c_str()))
  {
    r &= ReadRXFFileHelper::parsePin(pinN, model, newPins);
  }
  return r;
}

bool ReadRXFFileHelper::parseAssemblies(const pugi::xml_node& rootNode, EntityRef& model,
  EntityRefArray& newAssys, StringToEnt& labelToPin,
                              StringToEnt& nameToDuct,
                                           smtk::attribute::ComponentItemPtr& cI,
                                           smtk::attribute::ComponentItemPtr& mI,
                                           smtk::attribute::ComponentItemPtr& tI)
{
  bool r(true);
  for (pugi::xml_node aNode = rootNode.child(ASSEMBLY_TAG.c_str()); aNode;
       aNode = aNode.next_sibling(ASSEMBLY_TAG.c_str()))
  {
    r &= ReadRXFFileHelper::parseAssembly(
      aNode, model, newAssys, labelToPin, nameToDuct, cI, mI, tI);
  }
  return r;
}

bool ReadRXFFileHelper::populateCoreInfo(const pugi::xml_node& rootNode, smtk::model::EntityRef& model,
                               StringToEnt& labelToAssy,
                               smtk::attribute::ComponentItemPtr& cI,
                               smtk::attribute::ComponentItemPtr& mI,
                               smtk::attribute::ComponentItemPtr& tI)
{
  bool r(true);
  smtk::model::ResourcePtr resource = model.resource();
  smtk::model::Group coreG = model.resource()->findEntitiesByProperty(
        "rggType", smtk::session::rgg::Core::typeDescription)[0];
  bool isHex = model.integerProperty(
        Core::geomDescription)[0] == static_cast<int>(Core::GeomType::Hex);
  rggCore core = json::parse(coreG.stringProperty(rggCore::propDescription)[0]);

  pugi::xml_node latticeNode = rootNode.child(LATTICE_TAG.c_str());

  Core::UuidToSchema assyUuidToSchema;
  std::pair<int, int> latticeSize;
  r &= ReadRXFFileHelper::parseLattice(latticeNode, labelToAssy, isHex,
                                       assyUuidToSchema, latticeSize);
  core.setLatticeSize(latticeSize);
  core.setLayout(assyUuidToSchema);

  bool calEntCoordFlag = OperationHelper::calculateEntityCoordsInCore(core, resource);
  if (!calEntCoordFlag)
  {
    smtkErrorMacro(smtk::io::Logger::instance(),
                   "Fail to calculate entity coordinates for the core");
  }
  r &= calEntCoordFlag;

  json coreJson = core;
  coreG.setStringProperty(rggCore::propDescription, coreJson.dump());

  // Glyph the used pins and ducts, then add them into the group
  const Core::UuidToCoordinates& pDToCoords = core.pinsAndDuctsToCoordinates();
  OperationHelper::glyphDucts(resource,
                              cI,
                              mI, tI,
                              coreG, pDToCoords, "Core");
  OperationHelper::glyphPins(resource,
                              cI,
                              mI, tI,
                              coreG, pDToCoords, "Core");
  // TODO: Add support for unknown attribute
  return r;
}

bool ReadRXFFileHelper::parseCore(const pugi::xml_node& rootNode, smtk::model::EntityRef& model)
{
  bool r = true;
  smtk::model::ResourcePtr res = model.resource();
  smtk::model::EntityRef coreG = model.resource()->findEntitiesByProperty("rggType", Core::typeDescription)[0];
  smtk::session::rgg::Core core;

  // Use the assembly to populate the geom type info first
  auto assemblyNode = rootNode.child(ASSEMBLY_TAG.c_str());
  std::string geomType;
  read(assemblyNode, GEOMETRY_TAG.c_str(), geomType);
  std::transform(geomType.begin(), geomType.end(), geomType.begin(), ::tolower);
  if (geomType == "hexagonal")
  {
    core.setGeomType(Core::GeomType::Hex);
    // Convenience property
    model.setIntegerProperty(Core::geomDescription, static_cast<int>(Core::GeomType::Hex));
  }
  else
  {
    core.setGeomType(Core::GeomType::Rect);
    // Convenience property
    model.setIntegerProperty(Core::geomDescription, static_cast<int>(Core::GeomType::Rect));
  }


  // Read from default node
  pugi::xml_node defaultsN = rootNode.child(DEFAULTS_TAG.c_str());
  double height(std::numeric_limits<double>::max()), zOrigin(0), thicknesses[2];
  r &= read(defaultsN, LENGTH_TAG.c_str(), height);
  core.setHeight(height);

  r &= read(defaultsN, Z0_TAG.c_str(), zOrigin);
  core.setZOrigin(zOrigin);

  r &= read(defaultsN, THICKNESS_TAG.c_str(), thicknesses, 2);
  core.setDuctThickness(thicknesses[0], thicknesses[1]);

  r &= read(defaultsN, AXIAL_MESH_SIZE_TAG.c_str(), s_axialMeshSize);
  r &= read(defaultsN, EDGE_INTERVAL_TAG.c_str(), s_edgeInterval);

  auto parametersNode = rootNode.child(PARAMETERS_TAG.c_str());
  std::string outputFileName;
  r &= read(parametersNode, MESH_FILENAME_TAG.c_str(), outputFileName);
  core.exportParams().OutputFileName = outputFileName;

  json coreJson = core;
  std::string coreString = coreJson.dump();
  coreG.setStringProperty(Core::propDescription, coreString);
  return r;
}


bool ReadRXFFileHelper::parseDucts(
  const pugi::xml_node& ductsN, smtk::model::EntityRef& model,
    smtk::model::EntityRefArray& newDucts)
{
  bool r = true;
  for(auto ductN = ductsN.child(DUCT_CELL_TAG.c_str()); ductN;
      ductN = ductN.next_sibling(DUCT_CELL_TAG.c_str()))
  {
    r &= ReadRXFFileHelper::parseDuct(ductN, model, newDucts);
  }
  return r;
}

bool ReadRXFFileHelper::parseDuct(
    const pugi::xml_node& ductN, smtk::model::EntityRef model, smtk::model::EntityRefArray& newDucts)
{
  bool r{true};

  smtk::model::ResourcePtr mgr = model.resource();
  smtk::model::StringList materialList = model.stringProperty("materials");
  smtk::model::AuxiliaryGeometry ductAux =
      mgr->addAuxiliaryGeometry(model.as<smtk::model::Model>(), 3);
  newDucts.push_back(ductAux);

  smtk::session::rgg::Duct duct;

  ductAux.setStringProperty("rggType", rggDuct::typeDescription);
  ductAux.setStringProperty("selectable", rggDuct::typeDescription);
  ductAux.setVisible(false);

  std::string name;
  r &= read(ductN, NAME_TAG.c_str(), name);
  ductAux.setName(name);
  duct.setName(name);

  // Read all layers AKA segments
  for (pugi::xml_node dLN = ductN.child(DUCT_LAYER_TAG.c_str()); dLN;
       dLN = dLN.next_sibling(DUCT_LAYER_TAG.c_str()))
  {
    rggDuct::Segment seg;
    // Cache current x, y, z0 and z1
    double xyZ0Z1[4];
    if (!read(dLN, LOC_TAG.c_str(), xyZ0Z1, 4))
    {
      smtkErrorMacro(smtk::io::Logger::instance().instance(), "Duct "
          << name << " has an invalid location, skip it.");
      continue;
    }
    seg.baseZ = xyZ0Z1[2];
    seg.height = xyZ0Z1[3] - xyZ0Z1[2];

    // read materials layers of current duct layer
    for (auto mN = dLN.child(MATERIAL_LAYER_TAG.c_str()); mN;
         mN = mN.next_sibling(MATERIAL_LAYER_TAG.c_str()))
    {
      std::string mName;
      r &= read(mN, MATERIAL_TAG.c_str(), mName);
      size_t pos = materialNameToIndex(mName, materialList);

      double radius[2];
      r &= read(mN, THICKNESS_TAG.c_str(), radius, 2);
      seg.layers.push_back(std::make_tuple(static_cast<int>(pos), radius[0], radius[1]));
    }
    duct.segments().push_back(seg);
  }

  // Create auxgeom placeholders for layers and parts
  OperationHelper::populateChildrenInDuctAux(ductAux, newDucts, duct);
  if (!r)
  {
    smtkErrorMacro(smtk::io::Logger::instance(), "Encounter errors when paring duct " << name);
  }

  json ductJson = duct;
  std::string ductStr = ductJson.dump();
  ductAux.setStringProperty(rggDuct::propDescription, ductStr);
  return r;
}

bool ReadRXFFileHelper::parsePin(const pugi::xml_node& pinNode, EntityRef model, EntityRefArray& newPins)
{
  bool r = true;
  smtk::model::ResourcePtr mgr = model.resource();
  smtk::model::StringList materialList = model.stringProperty("materials");
  smtk::model::AuxiliaryGeometry pinAux = mgr->addAuxiliaryGeometry(model.as<smtk::model::Model>(), 3);
  pinAux.setStringProperty("rggType", rggPin::typeDescription);
  pinAux.setStringProperty("selectable", rggPin::typeDescription);
  newPins.push_back(pinAux);
  pinAux.setVisible(false);
  rggPin pin;

  double zOrigin = std::numeric_limits<double>::max();

  std::string name, label, cellMaterial;
  std::vector<double> color;
  std::vector<rggPin::LayerMaterial> layerMs;
  std::vector<std::pair<int, rggPin::Piece>> baseZAndPieces;
  std::vector<rggPin::Piece> pieces;

  r &= read(pinNode, NAME_TAG.c_str(), name);
  pin.setName(name);
  pinAux.setName(name);

  r &= read(pinNode, LABEL_TAG.c_str(), label);
  // Make sure that the label is unique
  if (!rggPin::isAnUniqueLabel(label))
  {
    std::string oldLabel = label;
    label = rggPin::generateUniqueLabel();
    smtkErrorMacro(smtk::io::Logger::instance(), "The provided label " << oldLabel <<
                   " for the pin is not unique. Set it to be "<< label);
  }
  pinAux.setStringProperty("label", label);
  pin.setLabel(label);

  r &= readColor(pinNode, LEGEND_COLOR_TAG.c_str(), color);
  pinAux.setColor(color);
  pin.setColor(color);

  // Cell material
  if (read(pinNode, MATERIAL_TAG.c_str(), cellMaterial))
  {
    size_t cMPos = materialNameToIndex(cellMaterial, materialList);
    pin.setCellMaterialIndex(static_cast<int>(cMPos));
  }

  // Materials
  // TODO: In XRF file, materials are defined for each cylinder and frustum.
  // However, For each pin, it has only one material definition just along radius.
  // All subparts share the same material. If NEAMS workflow has a different
  // settings, we should change it here. For now I just calculate it once.
  bool layerIsCalculated(false);
  auto calculatePinMaterial = [&materialList, &layerMs](const pugi::xml_node& node) {
    for (auto mN = node.child(MATERIAL_LAYER_TAG.c_str()); mN;
         mN = mN.next_sibling(MATERIAL_LAYER_TAG.c_str()))
    {
      std::string mName;
      read(mN, MATERIAL_TAG.c_str(), mName);
      int mI = static_cast<int>(materialNameToIndex(mName, materialList));

      double radius[2];
      // Though RXF define two values, SMTK would only consume one value
      // since the cross section is a circle
      read(mN, THICKNESS_TAG.c_str(), radius, 2);
      layerMs.emplace_back(rggPin::LayerMaterial(mI, radius[0]));
    }
  };

  // Find all cylinders and frustums, then sort them based on base z value
  for (pugi::xml_node cylinderN = pinNode.child(CYLINDER_TAG.c_str()); cylinderN;
       cylinderN = cylinderN.next_sibling(CYLINDER_TAG.c_str()))
  {
    double radius(0);
    read(cylinderN, RADIUS_TAG.c_str(), radius);
    // pin sub parts
    double xyZ0Z1[4];
    if (!read(cylinderN, LOC_TAG.c_str(), xyZ0Z1, 4))
    {
      smtkErrorMacro(smtk::io::Logger::instance().instance(), "Pin "
          << name << " has an invalid location, skip it.");
      continue;
    }

    if (zOrigin > xyZ0Z1[2])
    {
      zOrigin = xyZ0Z1[2];
    }
    double height = xyZ0Z1[3] - xyZ0Z1[2];
    baseZAndPieces.emplace_back(
    std::make_pair(static_cast<int>(xyZ0Z1[2]),
              rggPin::Piece(rggPin::PieceType::CYLINDER, height, radius, radius)));
    if (!layerIsCalculated)
    {
      calculatePinMaterial(cylinderN);
      layerIsCalculated = true;
    }
  }

  for (pugi::xml_node frustumN = pinNode.child(FRUSTRUM_TAG.c_str()); frustumN;
       frustumN = frustumN.next_sibling(FRUSTRUM_TAG.c_str()))
  {
    double radius[2];
    read(frustumN, RADIUS_TAG.c_str(), radius, 2);
    // pin sub parts
    double xyZ0Z1[4];
    if (!read(frustumN, LOC_TAG.c_str(), xyZ0Z1, 4))
    {
      smtkErrorMacro(smtk::io::Logger::instance().instance(), "Pin "
          << name << " has an invalid location, skip it.");
      continue;
    }

    if (zOrigin > xyZ0Z1[2])
    {
      zOrigin = xyZ0Z1[2];
    }
    double height = xyZ0Z1[3] - xyZ0Z1[2];
    baseZAndPieces.emplace_back(std::make_pair(static_cast<int>(xyZ0Z1[2]),
                                rggPin::Piece(rggPin::PieceType::FRUSTUM, height, radius[0], radius[1])));
    if (!layerIsCalculated)
    {
      calculatePinMaterial(frustumN);
      layerIsCalculated = true;
    }
  }
  // sort the cylinders and frustums by baseZ value
  std::sort(baseZAndPieces.begin(), baseZAndPieces.end(),
            ComparePairOfZOriginAndPinPiece());
  for(const auto& iter: baseZAndPieces)
  {
    pieces.push_back(iter.second);
  }

  pin.setLayerMaterials(layerMs);
  pin.setPieces(pieces);

  pin.setZOrigin(zOrigin);

  OperationHelper::populateChildrenInPinAux(pinAux, newPins, pin);
  if (!r)
  {
    smtkErrorMacro(smtk::io::Logger::instance(), "Encounter errors when paring pin " << name);
  }
  json pinJson = pin;
  std::string pinStr = pinJson.dump();
  pinAux.setStringProperty(rggPin::propDescription, pinStr);
  return r;
}

bool ReadRXFFileHelper::parseAssembly(const pugi::xml_node& assyNode, EntityRef model,
                                      EntityRefArray& newAssys,
                                      const StringToEnt& labelToPin,
                                      const StringToEnt& nameToDuct,
                                           smtk::attribute::ComponentItemPtr& cI,
                                           smtk::attribute::ComponentItemPtr& mI,
                                           smtk::attribute::ComponentItemPtr& tI)
{
  bool r(true);
  bool isHex = model.integerProperty(Core::geomDescription)[0] == static_cast<int>(Core::GeomType::Hex);
  // Entities that should be glyphed and their coordinates during create
  // assembly process(sub parts of duct and pins)
  smtk::model::ResourcePtr resource = model.resource();
  smtk::model::Group assemblyG = resource->addGroup(0, "group"); // Assign the name later
  rggAssembly assembly;

  smtk::model::Model modelM = model.as<smtk::model::Model>();
  modelM.addGroup(assemblyG);
  BitFlags mask(0);
  mask |= smtk::model::AUX_GEOM_ENTITY;
  mask |= smtk::model::INSTANCE_ENTITY;
  assemblyG.setMembershipMask(mask);
  assemblyG.setStringProperty("rggType", Assembly::typeDescription);
  assemblyG.setStringProperty("selectable", Assembly::typeDescription);
  assemblyG.setVisible(false);

  std::string name, label;
  std::vector<double> color;
  r &= read(assyNode, LABEL_TAG.c_str(), label);
  r &= readColor(assyNode, LEGEND_COLOR_TAG.c_str(), color);
  // Make sure that the label is unique
  if (!rggAssembly::isAnUniqueLabel(label))
  {
    std::string oldLabel = label;
    label = rggAssembly::generateUniqueLabel();
    smtkErrorMacro(smtk::io::Logger::instance(), "The provided label " << oldLabel <<
                   " for the assembly is not unique. Set it to be "<< label);
  }

  name = "assembly_" + label;
  assemblyG.setName(name);
  assembly.setName(name);

  assemblyG.setStringProperty("label", label);
  assembly.setLabel(label);

  assemblyG.setColor(color);
  assembly.setColor(color);

  int centerPins(1);
  r &= read(assyNode, CENTER_PINS_TAG.c_str(), centerPins);
  assembly.setCenterPin(static_cast<bool>(centerPins));

  double pitches[2];
  r &= read(assyNode, PITCH_TAG.c_str(), pitches, 2);
  assembly.setPitch(pitches[0], pitches[1]);

  double degree(0);
  r &= read(assyNode, ROTATE_TAG.c_str(), degree);
  assembly.setRotate(degree);

  std::string ductName;
  smtk::model::EntityRef duct;
  r &= read(assyNode, DUCT_TAG.c_str(), ductName);
  if (nameToDuct.find(ductName) == nameToDuct.end())
  {
    smtkErrorMacro(smtk::io::Logger::instance(), "Assembly " << name
                                                             << " does not have "
                                                                "a valid duct, skipping it.");
    return false;
  }
  else
  {
    duct = nameToDuct.at(ductName);
  }

  assembly.setAssociatedDuct(duct.entity());

  // Parameters
  const pugi::xml_node paraNode = assyNode.child(PARAMETERS_TAG.c_str());
  assembly.exportParams().GeometryType =  isHex ? "Hexagonal" : "Rectangular";
  std::string geometry;
  r &= read(paraNode, GEOMETRY_TAG.c_str(), geometry);
  assembly.exportParams().Geometry = geometry;

  std::string center;
  r &= read(paraNode, CENTER_TAG.c_str(), center);
  if (center != "NotSet")
  {
    // TODO: Center is ill defined in old rgg appliation
  }

  double axialMeshSize;
  r &= read(paraNode, AXIAL_MESH_SIZE_TAG.c_str(), axialMeshSize);
  assembly.exportParams().AxialMeshSize = axialMeshSize;

  int ns;
  r &= read(paraNode, NEUMANNSET_STARTID_TAG.c_str(), ns);
  assembly.exportParams().NeumannSet_StartId = ns;

  int ms;
  r &= read(paraNode, MATERIALSET_STARTID_TAG.c_str(), ms);
  assembly.exportParams().MaterialSet_StartId = ms;

  int edgeInterval;
  // Old rgg application format has a bug that it uses EdgeInterval for core parameters
  // and edgeinterval for assembly parameters.
  std::string assyEdgeIntervalTag("edgeinterval");
  r &= read(paraNode, assyEdgeIntervalTag.c_str(), edgeInterval);
  assembly.exportParams().EdgeInterval = edgeInterval;

  // Layout and lattice size
  const pugi::xml_node latticeNode = assyNode.child(LATTICE_TAG.c_str());
  Assembly::UuidToSchema pinUuidToSchema;
  std::pair<int, int> latticeSize;
  r &= ReadRXFFileHelper::parseLattice(latticeNode, labelToPin, isHex,
                                       pinUuidToSchema, latticeSize);
  assembly.setLatticeSize(latticeSize);
  assembly.setLayout(pinUuidToSchema);

  OperationHelper::calculateEntityCoordsInAssembly(assembly, isHex, resource);
  const Assembly::UuidToCoordinates& entityToCoordinates = assembly.entityToCoordinates();

  // Glyph the duct and pins
  Assembly::UuidToCoordinates ductToCoords;
  ductToCoords[duct.entity()] = {{0.0, 0.0, 0.0}};
  OperationHelper::glyphDucts(resource,
                              cI, mI, tI,
                              assemblyG, ductToCoords, assembly.label());
  OperationHelper::glyphPins(resource,
                              cI,
                              mI, tI,
                              assemblyG, entityToCoordinates, assembly.label());

  json assemblyJson = assembly;
  assemblyG.setStringProperty(Assembly::propDescription, assemblyJson.dump());

  newAssys.push_back(assemblyG);

  // TODO: Add support for unknown attribute
  if (!r)
  {
    smtkErrorMacro(smtk::io::Logger::instance(), "Encounter errors when paring assembly " << name);
  }
  return r;
}

bool ReadRXFFileHelper::parseLattice(
    const pugi::xml_node& latticeNode, const StringToEnt& labelToEntity, const bool isHex,
      UuidToSchema& uTS, std::pair<int, int>& ls)
{
  bool r(true);
  // TODO: Support type and subtype

  std::vector<std::string> labelsPerRing;
  r &= read(latticeNode, GRID_TAG.c_str(), labelsPerRing);
  int j(0);
  for (int i = 0; i < static_cast<int>(labelsPerRing.size()); i++)
  { // Per ring
    std::string labels = labelsPerRing[i];
    regex re(",");
    sregex_token_iterator it(labels.begin(), labels.end(), re, -1), last;
    for (j = 0; it != last; ++it, j++)
    { // per location
      std::string currentLabel = it->str();
      if (labelToEntity.find(currentLabel) != labelToEntity.end())
      {
        auto uuid = labelToEntity.at(currentLabel).entity();
        if (uTS.find(uuid) != uTS.end())
        {
          uTS[uuid].push_back({i, j});
        }
        else
        {
          uTS[uuid] = {{i, j}};
        }
      }
      else if (currentLabel != "XX")
      {
        smtkInfoMacro(smtk::io::Logger::instance(), "An invalid label "<< currentLabel << " is provided. It"
                                                       " can be an unknown label");
      }
    }
  }
  if (isHex)
  {
    ls = {static_cast<int>(labelsPerRing.size()),
         static_cast<int>(labelsPerRing.size())};
  }
  else
  { //TODO: Check if the order is right here
    ls = {j,
         static_cast<int>(labelsPerRing.size())};
  }
  return r;
}

} // namespace rgg
} //namespace session
} // namespace smtk
