//=========================================================================
//  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/plugin/smtkRGGEditAssemblyView.h"
#include "smtk/session/rgg/plugin/ui_smtkRGGEditAssemblyParameters.h"

#include "smtkRGGViewHelper.h"

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

#include "smtk/io/Logger.h"

#include "smtk/model/AuxiliaryGeometry.h"
#include "smtk/model/Group.h"
#include "smtk/model/Model.h"
#include "smtk/model/Resource.h"

#include "smtk/operation/Operation.h"

#include "smtk/extension/qt/qtAttribute.h"
#include "smtk/extension/qt/qtBaseAttributeView.h"
#include "smtk/extension/qt/qtItem.h"
#include "smtk/extension/qt/qtReferenceItemEditor.h"
#include "smtk/extension/qt/qtUIManager.h"

#include "smtk/session/rgg/Assembly.h"
#include "smtk/session/rgg/Core.h"
#include "smtk/session/rgg/Duct.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/session/rgg/operators/EditAssembly.h"
#include "smtk/session/rgg/operators/OperationHelper.h"

#include "smtk/session/rgg/qt/qtColorButton.h"
#include "smtk/session/rgg/qt/qtDraw2DLattice.h"
#include "smtk/session/rgg/qt/rggNucAssembly.h"

#include "nlohmann/json.hpp"

#include "smtk/view/Configuration.h"

#include "pqActiveObjects.h"
#include "pqApplicationCore.h"
#include "pqPresetDialog.h"

#include <QComboBox>
#include <QDockWidget>
#include <QMap>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QVBoxLayout>

#include <tuple>
#include <memory>

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

static constexpr double cos30 = 0.86602540378443864676372317075294;
static constexpr double cos60 = 0.5;
static constexpr int degreesHex[6] = { -120, -60, 0, 60, 120, 180 };
static constexpr 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
{

std::pair<double, double> calculateDuctMinimimThickness(const smtk::model::EntityRef& ductAux)
{
  EntityRefArray coreArray =
      ductAux.owningModel().resource()->findEntitiesByProperty("rggType", Core::typeDescription);
  EntityRef coreGroup = coreArray[0];
  Core core = json::parse(coreGroup.stringProperty(Core::propDescription)[0]);
  Duct duct = json::parse(ductAux.stringProperty(Duct::propDescription)[0]);
  const auto& ductThickness = core.ductThickness();
  const auto& segments = duct.segments();


  double thickness0 = std::numeric_limits<double>::max();
  double thickness1 = std::numeric_limits<double>::max();
  for (size_t i = 0; i < segments.size(); i++)
  {
    // Since we want to calculate the minimum, just check the inner most layer.
    double currentT0 = ductThickness.first * std::get<1>(segments[i].layers[0]);
    double currentT1 = ductThickness.second * std::get<2>(segments[i].layers[0]);
    thickness0 = std::min(thickness0, currentT0);
    thickness1 = std::min(thickness1, currentT1);
  }
  return std::make_pair(thickness0, thickness1);
}

// Calculate the x,y coordinates of the current pin in the hex grid
std::tuple<double, double, double> calculateHexPinCoordinate(
  const double& spacing, const std::pair<int, int>& ringAndLayer)
{
  // The index order of layer is clockwise, starting from upper left corner of the hex.
  // It's RGG's order and not ideal...
  double x{0}, y{0}, z{0};
  int ring = ringAndLayer.first;
  int layer = ringAndLayer.second;
  if (ring == 0)
  {
    x = y = 0;
  }
  else
  {
    int remainder = layer % ring;
    int modulus = layer / ring;

    double eL = ring * spacing;                       // edge length
    double xBT = -eL * cos60 + eL / (ring)*remainder; // x before transform
    double yBT = eL * cos30;                          // y befor transform
    // Apply rotation if needed. In order to avoid sin/cos calculation, we use
    // predefined values;
    double cosValue = cosSinAngles[modulus][0];
    double sinValue = cosSinAngles[modulus][1];

    x = xBT * cosValue - yBT * sinValue;
    y = yBT * cosValue + xBT * sinValue;
  }
  return std::make_tuple(x, y, z);
}
}

class smtkRGGEditAssemblyViewInternals : public Ui::RGGEditAssemblyParameters
{
public:
  smtkRGGEditAssemblyViewInternals()
  {
  }

  ~smtkRGGEditAssemblyViewInternals()
  { // TODO: convert them into smart pointer
    if (CurrentAtt)
    {
      delete CurrentAtt;
      CurrentAtt = nullptr;
    }
    auto mapIter = this->SMTKAssyToCMBAssy.begin();
    while (mapIter != this->SMTKAssyToCMBAssy.end())
    {
      if (mapIter.value())
      {
        delete mapIter.value();
      }
      mapIter++;
    }
    if (this->SchemaPlanner)
    {
      this->SchemaPlanner->setVisible(false);
    }
  }

  qtAttribute* createAttUI(smtk::attribute::AttributePtr att, QWidget* pw, qtBaseView* view)
  {
    if (att && att->numberOfItems() > 0)
    {
      smtk::view::Configuration::Component comp;
      qtAttribute* attInstance = new qtAttribute(att, comp, pw, view);
      if (attInstance && attInstance->widget())
      {
        // Without any additional info lets use a basic layout with model associations
        // if any exists
        attInstance->createBasicLayout(true);
        attInstance->widget()->setObjectName("RGGAssemblyEditor");
        QVBoxLayout* parentlayout = static_cast<QVBoxLayout*>(pw->layout());
        parentlayout->insertWidget(0, attInstance->widget());
      }
      return attInstance;
    }
    return nullptr;
  }

  QPointer<qtAttribute> CurrentAtt{nullptr};
  QPointer<qtDraw2DLattice> Current2DLattice{nullptr};
  QPointer<QDockWidget> SchemaPlanner;
  // Map smtk assembly(smtk::model::AuxiliaryGeometry) to CMB assembly rggNucAssembly which is used for schema planning
  QMap<smtk::model::EntityRef, rggNucAssembly*> SMTKAssyToCMBAssy;
  // CMB representation of the assembly which is used in a schema planner
  rggNucAssembly* CurrentCMBAssy{nullptr};
  // Used for deciding when to enable the edit panel
  smtk::model::EntityRef CurrentSMTKAssy;
  smtk::model::EntityRef CurrentSMTKDuct;

  smtk::weak_ptr<smtk::operation::Operation> CreateInstanceOp;
  smtk::weak_ptr<smtk::operation::Operation> DeleteOp;
};

smtkRGGEditAssemblyView::smtkRGGEditAssemblyView(const smtk::view::Information& info)
  : qtBaseAttributeView(info)
{
  this->Internals = new smtkRGGEditAssemblyViewInternals();
}

smtkRGGEditAssemblyView::~smtkRGGEditAssemblyView()
{
  delete this->Internals;
}

const smtk::operation::OperationPtr& smtkRGGEditAssemblyView::operation() const
{
  return m_viewInfo.get<smtk::operation::OperationPtr>();
}

qtBaseView* smtkRGGEditAssemblyView::createViewWidget(const smtk::view::Information& info)
{
  smtkRGGEditAssemblyView* view = new smtkRGGEditAssemblyView(info);
  view->buildUI();
  return view;
}

void smtkRGGEditAssemblyView::requestModelEntityAssociation()
{
  this->updateUI();
}

void smtkRGGEditAssemblyView::valueChanged(smtk::attribute::ItemPtr /*optype*/)
{
  this->requestOperation(this->operation());
}

void smtkRGGEditAssemblyView::requestOperation(const smtk::operation::OperationPtr& op)
{
  if (!op || !op->parameters())
  {
    return;
  }
  op->operate();
}

void smtkRGGEditAssemblyView::attributeModified()
{
  // Always enable apply button here
}

void smtkRGGEditAssemblyView::onAttItemModified(smtk::extension::qtItem* item)
{
  smtk::attribute::ItemPtr itemPtr = item->item();
  // only changing assembly and duct would update edit assembly panel
  if (itemPtr->name() == "model/assembly" || itemPtr->name() == "associated duct")
  {
    // If the assembly has a duct assocated with it, update the qtReferenceItemEditor
    if (itemPtr->name() == "model/assembly")
    {
      smtk::attribute::AttributePtr att = this->operation()->parameters();
      smtk::model::EntityRefArray ents =
        att->associatedModelEntities<smtk::model::EntityRefArray>();
      if ((ents.size() > 0) && ents[0].hasStringProperty(Assembly::propDescription))
      {
        Assembly assembly = json::parse(ents[0].stringProperty(Assembly::propDescription)[0]);

        smtk::model::EntityRef duct =
          smtk::model::EntityRef(ents[0].resource(), assembly.associatedDuct());
        smtk::attribute::ComponentItemPtr ductI =
          this->Internals->CurrentAtt->attribute()->findComponent("associated duct");
        ductI->setValue(duct.component());
        QList<qtItem*> items = this->Internals->CurrentAtt->items();
        for(const auto& item : items)
        {
          if (item->item()->name() == "associated duct")
          {
            qtReferenceItemEditor* refItemComboBox = dynamic_cast<qtReferenceItemEditor*>(item);
            if (refItemComboBox)
            {
              refItemComboBox->updateItemData();
              break;
            }
          }
        }
      }
    }
    this->updateEditAssemblyPanel();
  }
}

void smtkRGGEditAssemblyView::apply()
{
  // Fill the attribute - read all data from UI
  smtk::attribute::AttributePtr eaAtt = this->operation()->parameters();

  smtk::model::EntityRefArray assocatedEntity =
    eaAtt->associatedModelEntities<smtk::model::EntityRefArray>();

  Assembly assy;

  smtk::model::Model model = assocatedEntity[0].isModel() ?
          assocatedEntity[0].as<smtk::model::Model>() : assocatedEntity[0].owningModel();

  bool isHex = model.integerProperty(Core::geomDescription)[0] == static_cast<int>(Core::GeomType::Hex);

  Assembly::UuidToSchema entityToLayout;
  // Call apply function on qtDraw2DLattice to update CurrentCMBAssy
  if (this->Internals->Current2DLattice)
  {
    this->Internals->Current2DLattice->apply();
  }
  // Pins and layouts
  auto cmbAssembly = this->Internals->CurrentCMBAssy;
  std::shared_ptr<qtLattice> assyLattice = cmbAssembly->lattice();
  std::pair<size_t, size_t> dimension = assyLattice->GetDimensions();
  for (size_t i = 0; i < dimension.first; i++)
  {
    size_t jMax = isHex ? ((i == 0) ? 1 : dimension.second * i) : dimension.second;
    for (size_t j = 0; j < jMax; j++)
    {
      smtk::model::EntityRef pinEntity = assyLattice->GetCell(i, j).getPart();
      if (!pinEntity.hasStringProperty(Pin::propDescription))
      {
        continue;
      }
      Pin pin = json::parse(pinEntity.stringProperty(Pin::propDescription)[0]);
      // Skip invalid and empty pin cell(defined in qtLattice class)
      if (!pinEntity.isValid() ||
        (pin.name() == "Empty Cell" && pin.label() == "XX"))
      {
        continue;
      }

      auto ij = std::make_pair(static_cast<int>(i), static_cast<int>(j));
      if (entityToLayout.find(pinEntity.entity()) != entityToLayout.end())
      {
        entityToLayout[pinEntity.entity()].push_back(ij);
      }
      else
      {
        std::vector<std::pair<int,int>> layout;
        layout.push_back(ij);
        entityToLayout.emplace(pinEntity.entity(), layout);
      }
    }
  }

  // Pins
  smtk::model::EntityRef duct = eaAtt->findComponent("associated duct")->valueAs<smtk::model::Entity>();
  this->calculatePitches(); // In case the user has changed duct properties.

  // Populate the op attribute and invoke the operation
  // Associating assembly and duct are taken care of by the qtAttribute
  assy.setName(this->Internals->nameLineEdit->text().toStdString());

  assy.setLabel(this->Internals->labelLineEdit->text().toStdString());

  std::vector<double> color;
  this->Internals->chooseColorButton->getColor(color);
  assy.setColor(color);

  assy.setZAxis(this->Internals->zAxisRotationComboBox->currentText().toDouble());

  assy.setLayout(entityToLayout);

  assy.setAssociatedDuct(duct.entity());


  assy.setCenterPin(this->Internals->centerPinsCheckbox->isChecked());

  if (this->Internals->pitchYSpinBox->isHidden())
  { // Geometry type is hex.
    assy.setPitch(this->Internals->pitchXSpinBox->value());
  }
  else
  {
    assy.setPitch(this->Internals->pitchXSpinBox->value(),
                  this->Internals->pitchYSpinBox->value());
  }

  if (this->Internals->pitchYSpinBox->isHidden())
  { // Geometry type is hex.
    assy.setLatticeSize(this->Internals->latticeXSpinBox->value(),
                        this->Internals->latticeXSpinBox->value());
  }
  else
  { // Geometry type is rect
    assy.setLatticeSize(this->Internals->latticeXSpinBox->value(),
                        this->Internals->latticeYSpinBox->value());
  }

  // Calculate EntityToCoordinates
  OperationHelper::calculateEntityCoordsInAssembly(assy, isHex, duct.resource());

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

  eaAtt->findString("assembly representation")->setValue(assStr);

  this->requestOperation(this->operation());
  this->Internals->chooseColorButton->setText(QString());
}

void smtkRGGEditAssemblyView::calculatePitches()
{
  bool isHex = this->Internals->latticeYLabel->isHidden();
  double pitchX, pitchY;
  int latticeX = this->Internals->latticeXSpinBox->value(),
      latticeY = this->Internals->latticeYSpinBox->value();
  // Get the inner duct size
  smtk::attribute::ComponentItemPtr ductItem =
    this->Internals->CurrentAtt->attribute()->findComponent("associated duct");
  smtk::model::EntityRef duct;
  if (ductItem)
  {
    duct = ductItem->valueAs<smtk::model::Entity>();
    if (!duct.hasStringProperty("rggType") ||
      duct.stringProperty("rggType")[0] != smtk::session::rgg::Duct::typeDescription)
    {
      return;
    }
  }
  auto thicknesses = calculateDuctMinimimThickness(duct);

  // Following the logic in RGG code cmbNucAssembly::calculatePitch function L318
  // According to Juda, the formula is provided by their vendor
  // TODO: Improve or add custom function
  if (isHex)
  {
    // Comment it out for now. Otherwise duct will have visible gaps
    // @juda Make it slightly smaller to make exporting happy
    //const double d = thicknesses.first - thicknesses.first * 0.035;
    const double d = thicknesses.first;
    pitchX = pitchY = (cos30 * d) / (latticeX + 0.5 * (latticeX - 1));
  }
  else
  {
    pitchX = (thicknesses.first) / (latticeX + 0.5);
    pitchY = (thicknesses.second) / (latticeY + 0.5);
  }
  this->Internals->pitchXSpinBox->setValue(pitchX);
  this->Internals->pitchYSpinBox->setValue(pitchY);
}

void smtkRGGEditAssemblyView::launchSchemaPlanner()
{
  if (!this->Internals->SchemaPlanner)
  {
    QWidget* dockP = nullptr;
    foreach (QWidget* widget, QApplication::topLevelWidgets())
    {
      if (widget->inherits("QMainWindow"))
      {
        dockP = widget;
        break;
      }
    }
    this->Internals->SchemaPlanner = new QDockWidget(dockP);
    this->Internals->SchemaPlanner->setObjectName("rggAssemblySchemaPlanner");
    this->Internals->SchemaPlanner->setWindowTitle("Assembly Schema Planner");
    auto sp = this->Internals->SchemaPlanner;
    sp->setFloating(true);
    sp->setWidget(this->Internals->Current2DLattice);
    sp->raise();
    sp->show();
    this->Internals->Current2DLattice->rebuild();
  }
  else
  {
    this->Internals->SchemaPlanner->raise();
    this->Internals->SchemaPlanner->show();
  }
}


void smtkRGGEditAssemblyView::updateUI()
{
  smtk::view::ConfigurationPtr view = this->configuration();
  if (!view || !this->Widget)
  {
    return;
  }

  if (this->Internals->CurrentAtt)
  {
    delete this->Internals->CurrentAtt;
  }

  int i = view->details().findChild("AttributeTypes");
  if (i < 0)
  {
    return;
  }
  smtk::view::Configuration::Component& comp = view->details().child(i);
  std::string eaName;
  for (std::size_t ci = 0; ci < comp.numberOfChildren(); ++ci)
  {
    smtk::view::Configuration::Component& attComp = comp.child(ci);
    if (attComp.name() != "Att")
    {
      continue;
    }
    std::string optype;
    if (attComp.attribute("Type", optype) && !optype.empty())
    {
      if (optype == "edit assembly")
      {
        eaName = optype;
        break;
      }
    }
  }
  if (eaName.empty())
  {
    return;
  }

  smtk::attribute::AttributePtr att = this->operation()->parameters();
  this->Internals->CurrentAtt = this->Internals->createAttUI(att, this->Widget, this);
  if (this->Internals->CurrentAtt)
  {
    QObject::connect(this->Internals->CurrentAtt, &qtAttribute::modified, this,
      &smtkRGGEditAssemblyView::attributeModified);
    QObject::connect(this->Internals->CurrentAtt, &qtAttribute::itemModified, this,
      &smtkRGGEditAssemblyView::onAttItemModified);

    this->updateEditAssemblyPanel();
  }
}

void smtkRGGEditAssemblyView::createWidget()
{
  smtk::view::ConfigurationPtr view = this->configuration();
  if (!view)
  {
    return;
  }

  QVBoxLayout* parentLayout = dynamic_cast<QVBoxLayout*>(this->parentWidget()->layout());

  // Delete any pre-existing widget
  if (this->Widget)
  {
    if (parentLayout)
    {
      parentLayout->removeWidget(this->Widget);
    }
    delete this->Widget;
  }

  // Create a new frame and lay it out
  this->Widget = new QFrame(this->parentWidget());
  QVBoxLayout* layout = new QVBoxLayout(this->Widget);
  layout->setMargin(0);
  this->Widget->setLayout(layout);
  this->Widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);

  QWidget* tempWidget = new QWidget(this->parentWidget());
  this->Internals->setupUi(tempWidget);
  tempWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
  layout->addWidget(tempWidget, 1);

  QObject::connect(
    this->Internals->centerPinsCheckbox, &QCheckBox::stateChanged, this, [=](int isChecked) {
      this->Internals->pitchXSpinBox->setEnabled(!isChecked);
      this->Internals->pitchYSpinBox->setEnabled(!isChecked);
      this->Internals->calculatePitchButton->setEnabled(!isChecked);
      if (isChecked)
      { // Modifying lattice X and lattice Y should automatically trigger pitch calculation
        QObject::connect(this->Internals->latticeXSpinBox,
          QOverload<int>::of(&QSpinBox::valueChanged), this,
          &smtkRGGEditAssemblyView::calculatePitches);
        QObject::connect(this->Internals->latticeYSpinBox,
          QOverload<int>::of(&QSpinBox::valueChanged), this,
          &smtkRGGEditAssemblyView::calculatePitches);
      }
      else
      {
        QObject::disconnect(this->Internals->latticeXSpinBox,
          QOverload<int>::of(&QSpinBox::valueChanged), this,
          &smtkRGGEditAssemblyView::calculatePitches);
        QObject::disconnect(this->Internals->latticeYSpinBox,
          QOverload<int>::of(&QSpinBox::valueChanged), this,
          &smtkRGGEditAssemblyView::calculatePitches);
      }
    });

  // By default, pins are centered so that modifying lattice X and lattice Y
  // should automatically trigger pitch calculation
  QObject::connect(this->Internals->latticeXSpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
    this, &smtkRGGEditAssemblyView::calculatePitches);
  QObject::connect(this->Internals->latticeYSpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
    this, &smtkRGGEditAssemblyView::calculatePitches);

  QObject::connect(this->Internals->calculatePitchButton, &QPushButton::clicked, this,
    &smtkRGGEditAssemblyView::calculatePitches);

  QObject::connect(this->Internals->launchSchemaButton, &QPushButton::clicked, this,
    &smtkRGGEditAssemblyView::launchSchemaPlanner);

  // Show help when the info button is clicked.
  QObject::connect(
    this->Internals->infoButton, &QPushButton::released, this, &smtkRGGEditAssemblyView::onInfo);

  QObject::connect(
    this->Internals->applyButton, &QPushButton::released, this, &smtkRGGEditAssemblyView::apply);

  this->updateUI();
}

void smtkRGGEditAssemblyView::updateEditAssemblyPanel()
{
  smtk::attribute::AttributePtr att = this->Internals->CurrentAtt->attribute();
  smtk::model::EntityRefArray ents = att->associatedModelEntities<smtk::model::EntityRefArray>();
  bool isEnabled(true);

  // Need a valid duct
  smtk::attribute::ComponentItemPtr ductItem = att->findComponent("associated duct");
  smtk::model::EntityRef duct;
  if (ductItem)
  {
    duct = ductItem->valueAs<smtk::model::Entity>();
    if (!duct.hasStringProperty("rggType") ||
      duct.stringProperty("rggType")[0] != smtk::session::rgg::Duct::typeDescription)
    {
      isEnabled = false;
    }
  }

  // Need a valid assembly or model
  if ((ents.size() == 0) || ((!ents[0].isModel()) &&
                             !(ents[0].isGroup() && ents[0].hasStringProperty("rggType") &&
    (ents[0].stringProperty("rggType")[0] == smtk::session::rgg::Assembly::typeDescription))))
  { // Its type is not rgg assembly or model
    isEnabled = false;
    // Invalid the current smtk assy
    this->Internals->CurrentSMTKAssy = smtk::model::EntityRef();
  }
  else
  {
    if (this->Internals->CurrentSMTKAssy == ents[0] && this->Internals->CurrentSMTKDuct == duct)
    { // If it's the same, do not reset the schema planner
      // since it might surprise the user
      return;
    }
    if (!ents[0].isModel())
    {
      this->Internals->CurrentSMTKAssy = ents[0]; // Update current smtk assy
    }
    else
    {
      this->Internals->CurrentSMTKAssy = smtk::model::EntityRef();
    }
  }
  this->Internals->CurrentSMTKDuct = duct; // Update current smtk duct

  if (this->Internals)
  {
    this->Internals->scrollArea->setEnabled(isEnabled);
  }

  if (isEnabled)
  {
    // qtLattice needs the mode resource to create/find blank EntityRef. To workaround the case
    // when multiple rgg model resources are present, we delay the construction of qt2DLattice at usage state
    if (!this->Internals->Current2DLattice)
    {
      this->Internals->Current2DLattice = new qtDraw2DLattice(ents[0]);
      this->Internals->Current2DLattice->setFrameShape(QFrame::NoFrame);
      // Modify layers/ x and y value would update the schema planner
      QObject::connect(this->Internals->latticeXSpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
        this->Internals->Current2DLattice, &qtDraw2DLattice::setLatticeXorLayers);
      QObject::connect(this->Internals->latticeYSpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
        this->Internals->Current2DLattice, &qtDraw2DLattice::setLatticeY);
      // Reset schema
      QObject::connect(this->Internals->resetSchemaPlannerButton, &QPushButton::clicked,
        this->Internals->Current2DLattice, &qtDraw2DLattice::reset);
      this->Internals->Current2DLattice->setVisible(false);
    }

    // Create/update current rggNucAssembly
    smtk::model::Model model = ents[0].isModel() ? ents[0].as<smtk::model::Model>() :
                               ents[0].owningModel();
    if (this->Internals->SMTKAssyToCMBAssy.contains(ents[0]))
    {
      this->Internals->CurrentCMBAssy = this->Internals->SMTKAssyToCMBAssy.value(ents[0]);
      /********************************/
      std::cout << "Enabled. Use a cached CMBAssy" <<std::endl;
      /********************************/
    }
    else
    {
      rggNucAssembly* assy = new rggNucAssembly(ents[0]);
      // Only cache it when it's in editing mode
      if (!ents[0].isModel())
      {
        this->Internals->SMTKAssyToCMBAssy[ents[0]] = assy;
      }
      this->Internals->CurrentCMBAssy = assy;
    }
    this->Internals->Current2DLattice->setLattice(this->Internals->CurrentCMBAssy);
    // Fulfill/ Update info of current assembly
    // TODO: Handle switching duct condition
    this->Internals->CurrentCMBAssy->setAssyDuct(duct);

    // Populate the panel
    this->Internals->nameLineEdit->setText(QString::fromStdString(this->Internals->CurrentCMBAssy->name()));

    this->Internals->labelLineEdit->setText(QString::fromStdString(this->Internals->CurrentCMBAssy->label()));

    const smtk::model::FloatList& assemblyColor = this->Internals->CurrentCMBAssy->color();
    if (assemblyColor.size() == 4 && assemblyColor[3] >= 0) // Valid color
    {
      QColor color =
        QColor::fromRgbF(assemblyColor[0], assemblyColor[1], assemblyColor[2], assemblyColor[3]);
      this->Internals->chooseColorButton->setColor(color);
    }
    else
    {
      this->Internals->chooseColorButton->setColor(qtColorButton::generateColor());
      this->Internals->chooseColorButton->setText(QString::fromStdString("(Suggesting color)"));
    }

    bool isHex = model.integerProperty(Core::geomDescription)[0] ==
        static_cast<int>(Core::GeomType::Hex);

    // Lattice
    this->Internals->latticeYLabel->setHidden(isHex);
    this->Internals->latticeYSpinBox->setHidden(isHex);
    this->Internals->latticeXLabel->setText(
      QString::fromStdString(isHex ? "Number of Layers" : "X"));
    // By default rect assembly is 4x4 and hex is 1x1.
    this->Internals->latticeXSpinBox->setValue(this->Internals->CurrentCMBAssy->latticeSize().first);
    this->Internals->latticeYSpinBox->setValue(this->Internals->CurrentCMBAssy->latticeSize().second);

    // Make sure that the label is expanded properly
    this->Internals->latticeXLabel->setMinimumWidth(isHex ? 120 : 20);
    // Pitch
    this->Internals->pitchYLabel->setHidden(isHex);
    this->Internals->pitchYSpinBox->setHidden(isHex);
    this->Internals->pitchXLabel->setText(
      QString::fromStdString(isHex ? "Pitch: " : "Pitch X: "));

    // Center pins and pitches
    this->Internals->centerPinsCheckbox->setChecked(this->Internals->CurrentCMBAssy->centerPin());

    this->Internals->pitchXSpinBox->setValue(this->Internals->CurrentCMBAssy->pitch().first);
    this->Internals->pitchYSpinBox->setValue(this->Internals->CurrentCMBAssy->pitch().second);

    this->Internals->zAxisRotationComboBox->clear();
    QStringList rotationOptions;
    if (isHex)
    {
      for (auto item : degreesHex)
      {
        rotationOptions << QString::number(item);
      }
    }
    else
    {
      for (auto item : degreesRec)
      {
        rotationOptions << QString::number(item);
      }
    }
    this->Internals->zAxisRotationComboBox->addItems(rotationOptions);
    // Set default rotation to be 0
    this->Internals->zAxisRotationComboBox->setCurrentIndex(isHex ? 2 : 1);
  }
}

void smtkRGGEditAssemblyView::setInfoToBeDisplayed()
{
  this->m_infoDialog->displayInfo(this->configuration());
}
