//=========================================================================
//  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.
//=========================================================================
#ifndef __smtk_session_rgg_test_h
#define __smtk_session_rgg_test_h

#include "smtk/session/rgg/Assembly.h"
#include "smtk/session/rgg/Core.h"
#include "smtk/session/rgg/Pin.h"
#include "smtk/session/rgg/Registrar.h"
#include "smtk/session/rgg/Resource.h"
#include "smtk/session/rgg/Session.h"

#include "smtk/session/rgg/operators/AddMaterial.h"
#include "smtk/session/rgg/operators/CreateModel.h"
#include "smtk/session/rgg/operators/EditAssembly.h"
#include "smtk/session/rgg/operators/EditCore.h"
#include "smtk/session/rgg/operators/EditDuct.h"
#include "smtk/session/rgg/operators/EditPin.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/attribute/Attribute.h"
#include "smtk/attribute/ResourceItem.h"

#include "smtk/model/EntityRef.h"
#include "smtk/model/Group.h"
#include "smtk/model/Model.h"

using namespace smtk::session::rgg;
using json = nlohmann::json;

namespace smtk
{
namespace session
{
namespace rgg
{
namespace test
{
inline int createTargetPins(smtk::model::Model model)
{
  /// Create four pins
  auto editPinOp = smtk::session::rgg::EditPin::create();
  if (!editPinOp)
  {
    std::cerr << "No \"Edit Pin\" operator\n";
    return 1;
  }
  // Pin1-P1
  {
    Pin pin = Pin("Pin1", "P1");
    pin.setColor({0.258824, 0.572549, 0.776471, 1});
    pin.setCellMaterialIndex(0);
    std::vector<Pin::Piece> pinPieces;
    pinPieces.push_back(Pin::Piece(Pin::PieceType::CYLINDER, 100, 2., 2.));
    pin.setPieces(pinPieces);
    std::vector<Pin::LayerMaterial> pinMaterials;
    pinMaterials.push_back(Pin::LayerMaterial(6,1));
    pin.setLayerMaterials(pinMaterials);

    json PinJson = pin;
    std::string targetPinStr = PinJson.dump();

    editPinOp->parameters()->associate(model.component());

    editPinOp->parameters()
      ->findString("pin representation")
      ->setValue(targetPinStr);

    auto createPinResult = editPinOp->operate();
    if (createPinResult->findInt("outcome")->value() !=
      static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
    {
      std::cerr << "\"Edit Pin\" operator failed to create a pin\n";
      return 1;
    }
  }

  // ControlRod-CR
  {
    editPinOp->parameters()->removeAllAssociations();
    Pin pin = Pin("ControlRod", "CR");
    pin.setColor({0.254902, 0.670588, 0.364706, 1});
    pin.setCellMaterialIndex(0);
    std::vector<Pin::Piece> pinPieces;
    pinPieces.push_back(Pin::Piece(Pin::PieceType::CYLINDER, 100, 1, 1));
    pin.setPieces(pinPieces);
    std::vector<Pin::LayerMaterial> pinMaterials;
    pinMaterials.push_back(Pin::LayerMaterial(1,1));
    pin.setLayerMaterials(pinMaterials);

    json PinJson = pin;
    std::string targetPinStr = PinJson.dump();

    editPinOp->parameters()->associate(model.component());

    editPinOp->parameters()
      ->findString("pin representation")
      ->setValue(targetPinStr);

    auto createPinResult = editPinOp->operate();
    if (createPinResult->findInt("outcome")->value() !=
      static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
    {
      std::cerr << "\"Edit Pin\" operator failed to create a pin\n";
      return 1;
    }
  }

  // FuelCell_1-F2
  {
    editPinOp->parameters()->removeAllAssociations();
    Pin pin = Pin("FuelCell_1", "F2");
    pin.setColor({0.45098, 0.45098, 0.45098, 1});
    pin.setCellMaterialIndex(0);
    std::vector<Pin::Piece> pinPieces;
    pinPieces.push_back(Pin::Piece(Pin::PieceType::CYLINDER, 100, 0.5, 0.5));
    pin.setPieces(pinPieces);
    std::vector<Pin::LayerMaterial> pinMaterials;
    pinMaterials.push_back(Pin::LayerMaterial(2,1));
    pin.setLayerMaterials(pinMaterials);

    json PinJson = pin;
    std::string targetPinStr = PinJson.dump();

    editPinOp->parameters()->associate(model.component());

    editPinOp->parameters()
      ->findString("pin representation")
      ->setValue(targetPinStr);

    auto createPinResult = editPinOp->operate();
    if (createPinResult->findInt("outcome")->value() !=
      static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
    {
      std::cerr << "\"Edit Pin\" operator failed to create a pin\n";
      return 1;
    }
  }

  // FuelCell_2-F1
  {
    editPinOp->parameters()->removeAllAssociations();
    Pin pin = Pin("FuelCell_2", "F1");
    pin.setColor({0.45098, 0.45098, 0.45098, 1});
    pin.setCellMaterialIndex(0);
    std::vector<Pin::Piece> pinPieces;
    pinPieces.push_back(Pin::Piece(Pin::PieceType::CYLINDER, 100, 0.7, 0.7));
    pin.setPieces(pinPieces);
    std::vector<Pin::LayerMaterial> pinMaterials;
    pinMaterials.push_back(Pin::LayerMaterial(2,0.66666));
    pinMaterials.push_back(Pin::LayerMaterial(5,1));
    pin.setLayerMaterials(pinMaterials);

    json PinJson = pin;
    std::string targetPinStr = PinJson.dump();

    editPinOp->parameters()->associate(model.component());

    editPinOp->parameters()
      ->findString("pin representation")
      ->setValue(targetPinStr);

    auto createPinResult = editPinOp->operate();
    if (createPinResult->findInt("outcome")->value() !=
      static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
    {
      std::cerr << "\"Edit Pin\" operator failed to create a pin\n";
      return 1;
    }
  }

  return 0;
}


inline int createTargetDucts(smtk::model::Model model)
{
  auto editDuctOp = smtk::session::rgg::EditDuct::create();
  if(!editDuctOp)
  {
    std::cerr << "No \"Edit Duct\" operator\n";
    return 1;
  }

  // Create Duct C1_Duct
  {
    Duct targetDuct = Duct("C1_Duct", false);
    Duct::Segment initSeg;
    initSeg.baseZ = 0.0;
    initSeg.height = 100.0;
    initSeg.layers.push_back(std::make_tuple(7, 1.0, 1.0));
    targetDuct.setSegments({initSeg});
    json targetDuctJson = targetDuct;
    std::string targetDuctStr = targetDuctJson.dump();

    editDuctOp->parameters()->associate(model.component());
    editDuctOp->parameters()->findString("duct representation")->setValue(targetDuctStr);

    auto createDuctResult = editDuctOp->operate();

    if (createDuctResult->findInt("outcome")->value() !=
      static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
    {
      std::cerr << "\"Edit Duct\" operator failed to create a duct\n";
      return 1;
    }
  }

  // Create Duct FF_Duct
  {
    editDuctOp->parameters()->removeAllAssociations();
    Duct targetDuct = Duct("FF_Duct", false);
    Duct::Segment initSeg;
    initSeg.baseZ = 0.0;
    initSeg.height = 100.0;
    initSeg.layers.push_back(std::make_tuple(3, 0.9, 0.9));
    initSeg.layers.push_back(std::make_tuple(4, 1.0, 1.0));
    targetDuct.setSegments({initSeg});
    json targetDuctJson = targetDuct;
    std::string targetDuctStr = targetDuctJson.dump();

    editDuctOp->parameters()->associate(model.component());
    editDuctOp->parameters()->findString("duct representation")->setValue(targetDuctStr);

    auto createDuctResult = editDuctOp->operate();

    if (createDuctResult->findInt("outcome")->value() !=
      static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
    {
      std::cerr << "\"Edit Duct\" operator failed to create a duct\n";
      return 1;
    }
  }

  return 0;
}

inline int createTargetAssemblies(smtk::model::Model model)
{
  auto editAssyOp = smtk::session::rgg::EditAssembly::create();
  if (!editAssyOp)
  {
    std::cerr << "NO \"Edit Assembly\" operator\n";
    return 1;
  }

  smtk::model::EntityRef pinP1 = model.resource()->findEntitiesByProperty("label", "P1")[0];
  smtk::model::EntityRef pinCR = model.resource()->findEntitiesByProperty("label", "CR")[0];
  smtk::model::EntityRef pinF2 = model.resource()->findEntitiesByProperty("label", "F2")[0];
  smtk::model::EntityRef pinF1 = model.resource()->findEntitiesByProperty("label", "F1")[0];

  smtk::model::EntityRef ductC1 = model.resource()->findEntitiesByProperty("name", "C1_Duct")[0];
  smtk::model::EntityRef ductFF = model.resource()->findEntitiesByProperty("name", "FF_Duct")[0];

  // Create assembly control-C1
  {
    Assembly assy = Assembly("control", "C1");
    assy.setAssociatedDuct(ductC1.entity());

    // modify the export parameters
    AssyExportParameters aep;
    aep.MeshType = "Hex"; // hex or Tet
    aep.GeometryType = "Hexagonal"; // Hexagonal
    aep.Geometry = "Volume";
    aep.MergeTolerance = 1e-3;
    aep.RadialMeshSize = 0.7;
    aep.AxialMeshSize = 20.0;
    aep.MeshScheme = "hole";
    aep.EdgeInterval = 5;
    aep.CreateSideset = "NO";
    aep.Rotate = "Z 30.0";
    aep.MaterialSet_StartId = 100;
    aep.NeumannSet_StartId = 100;
    assy.setExportParams(aep);

    assy.setColor({0.258824, 0.572549, 0.776471, 1});
    assy.setCenterPin(false);
    assy.setPitch(10,10);
    Assembly::UuidToSchema layout;
    layout[pinP1.entity().toString()] = {std::make_pair(0,0)};
    assy.setLayout(layout);
    assy.setLatticeSize(1);

    json assyJson = assy;
    std::string assyStr = assyJson.dump();

    editAssyOp->parameters()->associate(model.component());

    editAssyOp->parameters()->findString("assembly representation")->setValue(assyStr);

    editAssyOp->parameters()->findComponent("associated duct")->setValue(ductC1.component());

    auto createAssyOpResult = editAssyOp->operate();
    if (createAssyOpResult->findInt("outcome")->value() !=
        static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
    {
      std::cerr << "\"Create Assembly\" operator failed\n";
      return 1;
    }
  }

  // Create assembly fuel-FF
  {
    editAssyOp->parameters()->removeAllAssociations();
    Assembly assy = Assembly("fuel", "FF");
    assy.setAssociatedDuct(ductFF.entity());

    // modify the export parameters
    AssyExportParameters aep;
    aep.MeshType = "Hex"; // hex or Tet
    aep.GeometryType = "Hexagonal"; // Hexagonal
    aep.Geometry = "Volume";
    aep.MergeTolerance = 1e-3;
    aep.RadialMeshSize = 0.7;
    aep.AxialMeshSize = 20.0;
    aep.EdgeInterval = 5;
    aep.CreateSideset = "NO";
    aep.Rotate = "Z -30.0";
    aep.MaterialSet_StartId = -1;
    aep.NeumannSet_StartId = -1;
    assy.setExportParams(aep);

    assy.setColor({0.945098, 0.411765, 0.0745098, 1});
    assy.setCenterPin(false);
    assy.setPitch(1.8796,1.8796);
    Assembly::UuidToSchema layout;
    layout[pinCR.entity().toString()] = {std::make_pair(0,0)};
    layout[pinF1.entity().toString()] = {std::make_pair(1,0), std::make_pair(1,1), std::make_pair(1, 2),
        std::make_pair(1,3), std::make_pair(1,4), std::make_pair(1, 5)};
    layout[pinF2.entity().toString()] = {std::make_pair(2,0), std::make_pair(2, 1), std::make_pair(2,2),
        std::make_pair(2,3), std::make_pair(2,4), std::make_pair(2, 5),
        std::make_pair(2,6), std::make_pair(2,7), std::make_pair(2, 8),
        std::make_pair(2,9), std::make_pair(2,10), std::make_pair(2, 11)};
    assy.setLayout(layout);
    assy.setLatticeSize(3);

    json assyJson = assy;
    std::string assyStr = assyJson.dump();

    editAssyOp->parameters()->associate(model.component());

    editAssyOp->parameters()->findString("assembly representation")->setValue(assyStr);

    editAssyOp->parameters()->findComponent("associated duct")->setValue(ductC1.component());

    auto createAssyOpResult = editAssyOp->operate();
    if (createAssyOpResult->findInt("outcome")->value() !=
        static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
    {
      std::cerr << "\"Create Assembly\" operator failed\n";
      return 1;
    }
  }
  return 0;
}

inline int addMaterial(smtk::model::Model model, const std::string& name,
                       std::array<double, 4> color, double temperature,
                       std::vector<std::pair<std::string, double>> composition)
{
  auto op = smtk::session::rgg::AddMaterial::create();
  auto op_parameters = op->parameters();
  op_parameters->associate(model.component());
  op_parameters->findString("name")->setValue(name);
  op_parameters->findString("label")->setValue(name);
  for (int i = 0; i < 4; i++)
  {
    op_parameters->findDouble("color")->setValue(i, color[i]);
  }
  op_parameters->findDouble("temperature")->setValue(temperature);
  double density = std::accumulate(composition.begin(), composition.end(), 0.,
                                   [](const double& d, const std::pair<std::string, double>& p)
                                   { return d + p.second; });
  op_parameters->findDouble("density")->setValue(1.);
  op_parameters->findString("densityType")->setValue("adensity");
  op_parameters->findString("compositionType")->setValue("adens");

  op_parameters->findString("component")->setNumberOfValues(composition.size());
  op_parameters->findDouble("content")->setNumberOfValues(composition.size());
  int i = 0;
  for (auto& c : composition)
  {
    op_parameters->findString("component")->setValue(i, c.first);
    op_parameters->findDouble("content")->setValue(i, c.second);
    ++i;
  }

  auto res = op->operate();

  if (res->findInt("outcome")->value(0) !=
      static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
  {
    std::cerr << "\"Add Material\" operator failed\n";
    return 1;
  }
  return 0;
}

inline int editTargetCore(smtk::model::Model model)
{
  auto editCoreOp = smtk::session::rgg::EditCore::create();
  if (!editCoreOp)
  {
    std::cerr << "NO \"Edit Assembly\" operator\n";
    return 1;
  }

  smtk::model::EntityRef coreG = model.resource()->findEntitiesByProperty("rggType", Core::typeDescription)[0];
  smtk::model::EntityRef assyGC1 = model.resource()->findEntitiesByProperty("label", "C1")[0];
  smtk::model::EntityRef assyGFF = model.resource()->findEntitiesByProperty("label", "FF")[0];

  Core core = json::parse(coreG.stringProperty(Core::propDescription)[0]);
  core.setLatticeSize(2);

  CoreExportParameters cep;
  cep.Geometry = "Volume";
  cep.Symmetry = "1";
  cep.GeometryType = "hexflat";
  // cep.NeumannSet.push_back("Top 97");
  // cep.NeumannSet.push_back("Bot 98");
  // cep.NeumannSet.push_back("SideAll 99");
  // cep.BackgroundInfo.first = "outer_cylinder.cub";
  // cep.BackgroundInfo.second = std::string(DATA_DIR) + "/outer_cylinder.cub";
  cep.OutputFileName = "shfc.exo";
  core.setExportParams(cep);

  Core::UuidToSchema layout;
  layout[assyGFF.entity().toString()] = {std::make_pair(1,0), std::make_pair(1,1), std::make_pair(1, 2),
      std::make_pair(1,3), std::make_pair(1,4), std::make_pair(1, 5)};
  layout[assyGC1.entity().toString()] = {std::make_pair(0,0), std::make_pair(2,0), std::make_pair(2, 1), std::make_pair(2,2),
      std::make_pair(2,3), std::make_pair(2,4), std::make_pair(2, 5),
      std::make_pair(2,6), std::make_pair(2,7), std::make_pair(2, 8),
      std::make_pair(2,9), std::make_pair(2,10), std::make_pair(2, 11)};
  core.setLayout(layout);

  editCoreOp->parameters()->associate(coreG.component());
  json coreJson = core;
  std::string coreJsonStr = coreJson.dump();
  editCoreOp->parameters()->findString("core representation")->setValue(coreJsonStr);

  auto editCoreOpResult = editCoreOp->operate();
  if (editCoreOpResult->findInt("outcome")->value() !=
      static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
  {
    std::cerr << "\"Edit core\" operator failed\n";
    return 1;
  }

  return 0;
}

inline smtk::model::ResourcePtr constructTestModel()
{
  auto createModelOp = smtk::session::rgg::CreateModel::create();
  if (!createModelOp)
  {
    std::cerr << "No create model operator\n";
    return nullptr;
  }

  // Create a hex core
  createModelOp->parameters()->findString("name")->setValue("simpleHexCore");
  createModelOp->parameters()->findString("geometry type")->setDiscreteIndex(0);
  createModelOp->parameters()->findDouble("z origin")->setValue(0);
  createModelOp->parameters()->findDouble("height")->setValue(100);
  createModelOp->parameters()->findDouble("duct thickness")->setValue(10);
  createModelOp->parameters()->findInt("hex lattice size")->setValue(3);

  auto createModelOpResult = createModelOp->operate();
  if (createModelOpResult->findInt("outcome")->value() !=
    static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
  {
    std::cerr << "create model operator failed\n";
    return nullptr;
  }

  smtk::model::ResourcePtr resource = std::dynamic_pointer_cast<smtk::model::Resource>(
    createModelOpResult->findResource("resource")->value());

  smtk::model::Model model =
    createModelOpResult->findComponent("created")->valueAs<smtk::model::Entity>();
  if (!model.isValid())
  {
    std::cerr << "create model operator constructed an invalid model\n";
    return nullptr;
  }
  assert(resource->findEntitiesByProperty("rggType", Core::typeDescription).size() == 1);

  // Set material and its color
  std::vector<std::string> materials{"NoCellMaterial", "CR", "Fuel", "Dt1", "Dt2", "MatH",
                                    "Mat_Control", "Mat_Coolant"};
  model.setStringProperty("materials", materials);

  int fail = 0;
  fail += addMaterial(model, "NoCellMaterial", { 1.0, 1.0, 1.0, 1.0 }, 743.,
                      { { "na23", 1.e-10 } });
  fail += addMaterial(model, "CR", { 0.3, 0.5, 1.0, .5 }, 743.,
                      { { "na23", 2.1924e-2 } });
  fail += addMaterial(model, "Fuel", { 0.3, 0.3, 1.0, .5 }, 1500.,
                      { { "o16", 4.0E-02 },
                        { "U238", 1.8E-02 },
                        { "Pu239", 1.9e-03 },
                        { "Pu240", 1.0e-03 } });
  fail += addMaterial(model, "Dt1", { 0.75, 0.2, 0.75, 1.0}, 743.,
                      { { "MO92", 7.4E-05 },
                        { "MO95", 7.7E-05 },
                        { "MO96", 7.9E-05 },
                        { "MO98", 1.1E-04 },
                        { "CR52", 6.3E-03 },
                        { "FE56", 6.7E-02 },
                        { "FE57", 1.5E-03 },
                        { "SI28", 4.5E-04 } });
  fail += addMaterial(model, "Dt2", { 1.0, 0.1, 0.1, 1.0 }, 743.,
                      { { "MO92", 7.4E-05 },
                        { "MO95", 7.7E-05 },
                        { "MO96", 7.9E-05 },
                        { "MO98", 1.1E-04 },
                        { "CR52", 6.3E-03 },
                        { "FE56", 6.7E-02 },
                        { "FE57", 1.5E-03 },
                        { "SI28", 4.5E-04 } });
  fail += addMaterial(model, "MatH", { 0.0, 0.0, 0.0, 0.0 }, 743.,
                      { { "CR52", 1.4E-02 },
                        { "FE56", 4.9E-02 },
                        { "O16", 3.9E-04 } });
  fail += addMaterial(model, "Mat_Control", { 0.3, 1.0, 0.5, 1.0 }, 743.,
                      { { "na23", 2.1924e-2 } });
  fail += addMaterial(model, "Mat_Coolant", { .4, .4, .4, 1.0 }, 743.,
                      { { "na23", 2.1924e-2 } });

  if (fail)
  {
    std::cerr << "failed to add materials\n";
    return nullptr;
  }

  smtk::model::Group coreGroup = createModelOpResult->findComponent("created")->
      valueAs<smtk::model::Entity>(1);
  if (!coreGroup.isValid())
  {
    std::cerr << "create model operator constructed an invliad core group\n";
    return nullptr;
  }
  if (!coreGroup.hasStringProperty(Core::propDescription))
  {
    std::cerr << "The created core does not have json representation string property\n";
    return nullptr;
  }
  Core core = json::parse(coreGroup.stringProperty(Core::propDescription)[0]);

  if (createTargetPins(model))
  {
    std::cerr << "Fail to create the target pins" <<std::endl;
    return nullptr;
  }

  assert(model.resource()->findEntitiesByProperty("label", "P1").size() > 0);
  smtk::model::EntityRef pinP1 = model.resource()->findEntitiesByProperty("label", "P1")[0];
  assert(model.resource()->findEntitiesByProperty("label", "CR").size() > 0);
  smtk::model::EntityRef pinCR = model.resource()->findEntitiesByProperty("label", "CR")[0];
  assert(model.resource()->findEntitiesByProperty("label", "F2").size() > 0);
  smtk::model::EntityRef pinF2 = model.resource()->findEntitiesByProperty("label", "F2")[0];
  assert(model.resource()->findEntitiesByProperty("label", "F1").size() > 0);
  smtk::model::EntityRef pinF1 = model.resource()->findEntitiesByProperty("label", "F1")[0];

  if(createTargetDucts(model))
  {
    std::cerr << "Fail to create the target ducts" <<std::endl;
    return nullptr;
  }
  assert(model.resource()->findEntitiesByProperty("name", "C1_Duct").size() > 0);
  smtk::model::EntityRef ductC1 = model.resource()->findEntitiesByProperty("name", "C1_Duct")[0];
  assert(model.resource()->findEntitiesByProperty("name", "FF_Duct").size() > 0);
  smtk::model::EntityRef ductFF = model.resource()->findEntitiesByProperty("name", "FF_Duct")[0];

  if (createTargetAssemblies(model))
  {
    std::cerr << "Fail to create the target assemblies" <<std::endl;
    return nullptr;
  }
  assert(model.resource()->findEntitiesByProperty("rggType", Assembly::typeDescription).size() == 2);

  if (editTargetCore(model))
  {
    std::cerr << "Fail to edit the target core" <<std::endl;
    return nullptr;
  }

  return resource;
}

// A smtk representation of arcbench_test11_twodant.son file in pyarc repo
inline smtk::model::ResourcePtr constructTest11TwoDantModel()
{
  auto createModelOp = smtk::session::rgg::CreateModel::create();
  if (!createModelOp)
  {
    std::cerr << "No create model operator\n";
    return nullptr;
  }

  // Create a hex core
  createModelOp->parameters()->findString("name")->setValue("test11_twodant");
  createModelOp->parameters()->findString("geometry type")->setDiscreteIndex(0);
  createModelOp->parameters()->findDouble("z origin")->setValue(0);
  createModelOp->parameters()->findDouble("height")->setValue(200);
  createModelOp->parameters()->findDouble("duct thickness")->setValue(0.2);
  createModelOp->parameters()->findInt("hex lattice size")->setValue(6);

  auto createModelOpResult = createModelOp->operate();
  if (createModelOpResult->findInt("outcome")->value() !=
    static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
  {
    std::cerr << "create model operator failed\n";
    return nullptr;
  }

  smtk::model::ResourcePtr resource = std::dynamic_pointer_cast<smtk::model::Resource>(
    createModelOpResult->findResource("resource")->value());

  smtk::model::Model model =
    createModelOpResult->findComponent("created")->valueAs<smtk::model::Entity>();
  if (!model.isValid())
  {
    std::cerr << "create model operator constructed an invalid model\n";
    return nullptr;
  }
  assert(resource->findEntitiesByProperty("rggType", Core::typeDescription).size() == 1);

  // Set material and its color
  std::vector<std::string> materials{"NoCellMaterial", "MOX1", "MOX2",
                                    "Na", "Str", "blend_mat_fuel1", "blend_mat_fuel2", "blend_mat_refl"};
  model.setStringProperty("materials", materials);

  int fail = 0;
  fail += addMaterial(model, "NoCellMaterial", { 1.0, 1.0, 1.0, 1.0 }, 743.,
                      { { "na23", 1.e-10 } });
  fail += addMaterial(model, "MOX1", { 0.75, 0.2, 0.75, 1.0}, 900,
                      { { "u235", 2.0E-04 },
                        { "u238", 2.0E-02 },
                        { "pu239", 5.0E-03 },
                        { "pu240", 1.0E-03 },
                        { "am242m", 1.0E-10 },
                        { "o16", 8.0E-02 } });
  fail += addMaterial(model, "MOX2", { 1.0, 0.1, 0.1, 1.0 }, 700,
                      { { "u235", 2.0E-04 },
                        { "u238", 2.0E-02 },
                        { "pu239", 8.0E-03 },
                        { "pu240", 1.0E-03 },
                        { "o16", 8.0E-02 } });
  fail += addMaterial(model, "Na", { 0.0, 0.0, 0.0, 0.0 }, 300,
                      { { "na23", 2.0E-02 } });
  fail += addMaterial(model, "Str", { 0.3, 1.0, 0.5, 1.0 }, 300,
                      { { "fe56", 6.0e-2 },
                        { "cr52", 8.6e-3}});
  // .4 * MOX1 + .3 * NA + .3 * Str
  fail += addMaterial(model, "blend_mat_fuel1", { 1., 0., 0.}, .4 * 900. + .3 * 300. + .3 * 300.,
                      { { "u235", .4 * 2.0E-04 },
                        { "u238", .4 * 2.0E-02 },
                        { "pu239", .4 * 5.0E-03 },
                        { "pu240", .4 * 1.0E-03 },
                        { "am242m", .4 * 1.0E-10 },
                        { "o16", .4 * 8.0E-02 },
                        { "na23", .3 * 2.0E-02 },
                        { "fe56", .3 * 6.0e-2 },
                        { "cr52", .3 * 8.6e-3 } });
  // .4 * MOX2 + .3 * NA + .3 * Str
  fail += addMaterial(model, "blend_mat_fuel2", { 0., 1., 0.}, .4 * 700. + .3 * 300. + .3 * 300.,
                      { { "u235", .4 * 2.0E-04 },
                        { "u238", .4 * 2.0E-02 },
                        { "pu239", .4 * 8.0E-03 },
                        { "pu240", .4 * 1.0E-03 },
                        { "o16", .4 * 8.0E-02 },
                        { "na23", .3 * 2.0E-02 },
                        { "fe56", .3 * 6.0e-2 },
                        { "cr52", .3 * 8.6e-3 } });
  // .5 * NA + .5 * Str
  fail += addMaterial(model, "blend_mat_refl", { 0., 0., 1.}, .5 * 300. + .5 * 300.,
                      { { "na23", .5 * 2.0E-02 },
                        { "fe56", .5 * 6.0e-2 },
                        { "cr52", .5 * 8.6e-3 } });

  if (fail)
  {
    std::cerr << "failed to add materials\n";
    return nullptr;
  }

  smtk::model::Group coreGroup = createModelOpResult->findComponent("created")->
      valueAs<smtk::model::Entity>(1);
  if (!coreGroup.isValid())
  {
    std::cerr << "create model operator constructed an invliad core group\n";
    return nullptr;
  }
  if (!coreGroup.hasStringProperty(Core::propDescription))
  {
    std::cerr << "The created core does not have json representation string property\n";
    return nullptr;
  }
  Core core = json::parse(coreGroup.stringProperty(Core::propDescription)[0]);

  // Create target Ducts
  {
    auto editDuctOp = smtk::session::rgg::EditDuct::create();
    if(!editDuctOp)
    {
      std::cerr << "No \"Edit Duct\" operator\n";
      return nullptr;
    }

    // Create Duct Refl_Duct
    {
      Duct targetDuct = Duct("Refl_Duct", false);
      Duct::Segment initSeg;
      initSeg.baseZ = 0.0;
      initSeg.height = 200.0;
      initSeg.layers.push_back(std::make_tuple(7, 1.0, 1.0)); // blend_mat_refl
      targetDuct.setSegments({initSeg});
      json targetDuctJson = targetDuct;
      std::string targetDuctStr = targetDuctJson.dump();

      editDuctOp->parameters()->associate(model.component());
      editDuctOp->parameters()->findString("duct representation")->setValue(targetDuctStr);

      auto createDuctResult = editDuctOp->operate();

      if (createDuctResult->findInt("outcome")->value() !=
        static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
      {
        std::cerr << "\"Edit Duct\" operator failed to create a Refl_Duct\n";
        return nullptr;
      }
    }

    // Create Duct Fuel_Duct1
    {
      editDuctOp->parameters()->removeAllAssociations();
      Duct targetDuct = Duct("Fuel_Duct1", false);
      Duct::Segment initSeg;
      initSeg.baseZ = 0.0;
      initSeg.height = 50.0;
      initSeg.layers.push_back(std::make_tuple(7, 1.0, 1.0));

      Duct::Segment secondSeg;
      secondSeg.baseZ = 50.0;
      secondSeg.height = 100.0;
      secondSeg.layers.push_back(std::make_tuple(5, 1.0, 1.0));
      Duct::Segment thirdSeg;
      thirdSeg.baseZ = 150.0;
      thirdSeg.height = 50.0;
      thirdSeg.layers.push_back(std::make_tuple(7, 1.0, 1.0));

      targetDuct.setSegments({initSeg, secondSeg, thirdSeg});
      json targetDuctJson = targetDuct;
      std::string targetDuctStr = targetDuctJson.dump();

      editDuctOp->parameters()->associate(model.component());
      editDuctOp->parameters()->findString("duct representation")->setValue(targetDuctStr);

      auto createDuctResult = editDuctOp->operate();

      if (createDuctResult->findInt("outcome")->value() !=
        static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
      {
        std::cerr << "\"Edit Duct\" operator failed to create a Fuel_Duct1\n";
        return nullptr;
      }
    }

    // Create Duct Fuel_Duct2
    {
      editDuctOp->parameters()->removeAllAssociations();
      Duct targetDuct = Duct("Fuel_Duct2", false);
      Duct::Segment initSeg;
      initSeg.baseZ = 0.0;
      initSeg.height = 50.0;
      initSeg.layers.push_back(std::make_tuple(7, 1.0, 1.0));

      Duct::Segment secondSeg;
      secondSeg.baseZ = 50.0;
      secondSeg.height = 100.0;
      secondSeg.layers.push_back(std::make_tuple(6, 1.0, 1.0));
      Duct::Segment thirdSeg;
      thirdSeg.baseZ = 150.0;
      thirdSeg.height = 50.0;
      thirdSeg.layers.push_back(std::make_tuple(7, 1.0, 1.0));

      targetDuct.setSegments({initSeg, secondSeg, thirdSeg});
      json targetDuctJson = targetDuct;
      std::string targetDuctStr = targetDuctJson.dump();

      editDuctOp->parameters()->associate(model.component());
      editDuctOp->parameters()->findString("duct representation")->setValue(targetDuctStr);

      auto createDuctResult = editDuctOp->operate();

      if (createDuctResult->findInt("outcome")->value() !=
        static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
      {
        std::cerr << "\"Edit Duct\" operator failed to create Fuel_Duct2\n";
        return nullptr;
      }
    }
  }

  assert(model.resource()->findEntitiesByProperty("name", "Refl_Duct").size() > 0);
  smtk::model::EntityRef ductRefl = model.resource()->findEntitiesByProperty("name", "Refl_Duct")[0];
  assert(model.resource()->findEntitiesByProperty("name", "Fuel_Duct1").size() > 0);
  smtk::model::EntityRef ductFuel1 = model.resource()->findEntitiesByProperty("name", "Fuel_Duct1")[0];
  assert(model.resource()->findEntitiesByProperty("name", "Fuel_Duct2").size() > 0);
  smtk::model::EntityRef ductFuel2 = model.resource()->findEntitiesByProperty("name", "Fuel_Duct2")[0];

  // Create target assemblies
  {
    auto editAssyOp = smtk::session::rgg::EditAssembly::create();
    if (!editAssyOp)
    {
      std::cerr << "NO \"Edit Assembly\" operator\n";
      return nullptr;
    }

    // Create assembly Refl
    {
      Assembly assy = Assembly("Refl_assembly", "Refl");
      assy.setAssociatedDuct(ductRefl.entity());

      assy.setColor({0.258824, 0.572549, 0.776471, 1});

      json assyJson = assy;
      std::string assyStr = assyJson.dump();

      editAssyOp->parameters()->associate(model.component());

      editAssyOp->parameters()->findString("assembly representation")->setValue(assyStr);

      editAssyOp->parameters()->findComponent("associated duct")->setValue(ductRefl.component());

      auto createAssyOpResult = editAssyOp->operate();
      if (createAssyOpResult->findInt("outcome")->value() !=
          static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
      {
        std::cerr << "\"Create Assembly\" operator failed to create assembly Refl\n";
        return nullptr;
      }
    }

    // Create assembly Fuel_assembly1
    {
      editAssyOp->parameters()->removeAllAssociations();
      Assembly assy = Assembly("Fuel_assembly1", "Fuel1");
      assy.setAssociatedDuct(ductFuel1.entity());

      assy.setColor({0.945098, 0.411765, 0.0745098, 1});

      json assyJson = assy;
      std::string assyStr = assyJson.dump();

      editAssyOp->parameters()->associate(model.component());

      editAssyOp->parameters()->findString("assembly representation")->setValue(assyStr);

      editAssyOp->parameters()->findComponent("associated duct")->setValue(ductFuel1.component());

      auto createAssyOpResult = editAssyOp->operate();
      if (createAssyOpResult->findInt("outcome")->value() !=
          static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
      {
        std::cerr << "\"Create Assembly\" operator failed to create assembly Fuel_assembly1\n";
        return nullptr;
      }
    }

    // Create assembly Fuel_assembly2
    {
      editAssyOp->parameters()->removeAllAssociations();
      Assembly assy = Assembly("Fuel_assembly2", "Fuel2");
      assy.setAssociatedDuct(ductFuel2.entity());

      assy.setColor({0.945098, 0.411765, 0.0745098, 1});

      json assyJson = assy;
      std::string assyStr = assyJson.dump();

      editAssyOp->parameters()->associate(model.component());

      editAssyOp->parameters()->findString("assembly representation")->setValue(assyStr);

      editAssyOp->parameters()->findComponent("associated duct")->setValue(ductFuel2.component());

      auto createAssyOpResult = editAssyOp->operate();
      if (createAssyOpResult->findInt("outcome")->value() !=
          static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
      {
        std::cerr << "\"Create Assembly\" operator failed to create assembly Fuel_assembly2\n";
        return nullptr;
      }
    }

  }
  assert(model.resource()->findEntitiesByProperty("rggType", Assembly::typeDescription).size() == 3);
  assert(model.resource()->findEntitiesByProperty("name", "Refl_assembly").size() > 0);
  smtk::model::EntityRef assyRefl = model.resource()->findEntitiesByProperty("name", "Refl_assembly")[0];
  assert(model.resource()->findEntitiesByProperty("name", "Fuel_assembly1").size() > 0);
  smtk::model::EntityRef assy1 = model.resource()->findEntitiesByProperty("name", "Fuel_assembly1")[0];
  assert(model.resource()->findEntitiesByProperty("name", "Fuel_assembly2").size() > 0);
  smtk::model::EntityRef assy2 = model.resource()->findEntitiesByProperty("name", "Fuel_assembly2")[0];

  {
    auto editCoreOp = smtk::session::rgg::EditCore::create();
    if (!editCoreOp)
    {
      std::cerr << "NO \"Edit Assembly\" operator\n";
      return nullptr;
    }

    smtk::model::EntityRef coreG = model.resource()->findEntitiesByProperty("rggType", Core::typeDescription)[0];

    Core core = json::parse(coreG.stringProperty(Core::propDescription)[0]);
    core.setLatticeSize(6);

    Core::UuidToSchema layout;

    // RGG uses 0 based index and pyarc use 1 based index
    std::vector<std::pair<int, int>> assy1Layout{{2,1}, {2,3}, {2,5}, {2,7}, {2,9},
                                                 {2,11}};
    assy1Layout.emplace_back(0, 0);
    for (int i = 0; i < 1*6; i++)
    {
      assy1Layout.emplace_back(1, i);
    }

    std::vector<std::pair<int, int>> assy2Layout{{2,2}, {2,6}, {2,10}};
    for (int i = 0; i < 3*6; i++)
    {
      assy2Layout.emplace_back(3, i);
    }
    for (int i = 0; i < 4*6; i++)
    {
      assy2Layout.emplace_back(4, i);
    }

    std::vector<std::pair<int, int>> reflLayout{{2,0}, {2,4}, {2,8}};
    for (int i = 0; i < 5*6; i++)
    {
      reflLayout.emplace_back(5, i);
    }

    layout[assyRefl.entity().toString()] = reflLayout;
    layout[assy1.entity().toString()] = assy1Layout;
    layout[assy2.entity().toString()] = assy2Layout;

    core.setLayout(layout);

    editCoreOp->parameters()->associate(coreG.component());
    json coreJson = core;
    std::string coreJsonStr = coreJson.dump();
    editCoreOp->parameters()->findString("core representation")->setValue(coreJsonStr);

    auto editCoreOpResult = editCoreOp->operate();
    if (editCoreOpResult->findInt("outcome")->value() !=
        static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
    {
      std::cerr << "\"Edit core\" operator failed\n";
      return nullptr;
    }
  }

  return resource;
}
}
}
}
}

#endif
