//=============================================================================
// 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/qt/rggNucAssembly.h"
#include "smtk/session/rgg/qt/qtLattice.h"

#include "smtk/io/Logger.h"
#include "smtk/model/AuxiliaryGeometry.h"
#include "smtk/model/Resource.h"

#include "smtk/session/rgg/Pin.h"

#include "smtk/session/rgg/json/jsonCore.h"
#include "smtk/session/rgg/json/jsonAssembly.h"
#include "smtk/session/rgg/json/jsonPin.h"

using namespace smtk::session::rgg;

rggNucAssembly::rggNucAssembly(smtk::model::EntityRef entity)
  : rggLatticeContainer(entity)
{
  m_resource = entity.resource();
  this->resetBySMTKAssembly(entity);
}

rggNucAssembly::~rggNucAssembly()
{
}

QString rggNucAssembly::extractLabel(QString const& el)
{ // Extract the label out of context menu action name
  QString seperator("(");
  QStringList ql = el.split(seperator);
  return ql[1].left(ql[1].size() - 1);
}

void rggNucAssembly::fillList(std::vector<std::pair<QString, smtk::model::EntityRef> >& l)
{
  auto generatePair = [](smtk::model::EntityRef& entity) {
    std::string newName;
    if (entity.hasStringProperty("label"))
    {
      newName = entity.name() + " (" + entity.stringProperty("label")[0] + ")";
    }
    else
    {
      newName = entity.name() + " ()";
    }
    return std::pair<QString, smtk::model::EntityRef>(QString::fromStdString(newName), entity);
  };

  // Add empty cell first
  if (m_resource)
  {
    if (m_resource->findEntitiesByProperty("label", "XX").size() > 0)
    {
      l.push_back(generatePair(m_resource->findEntitiesByProperty("label", "XX")[0]));
    }
  }
  smtk::model::EntityRefArray pins = m_resource->findEntitiesByProperty("rggType", Pin::typeDescription);
  // Add all available pins that match the assembly's geometry type
  for (auto& pin : pins)
  {
    if (pin.as<smtk::model::AuxiliaryGeometry>().auxiliaryGeometries().size() > 0)
    { // Do not add sub pin parts and layers
      l.push_back(generatePair(pin));
    }
  }
}

smtk::model::EntityRef rggNucAssembly::getFromLabel(const QString& label)
{
  smtk::model::EntityRefArray pins = m_resource->findEntitiesByProperty("label", label.toStdString());
  for (auto& pin : pins)
  {
    if (pin.hasStringProperty("rggType") &&
      pin.stringProperty("rggType")[0] == Pin::typeDescription)
    { // Each pin has a unique label
      return pin;
    }
  }
  return smtk::model::EntityRef();
}

bool rggNucAssembly::IsHexType()
{
  return m_lattice->GetGeometryType() == HEXAGONAL;
}

void rggNucAssembly::calculateExtraTranslation(double& transX, double& transY)
{
  (void)transX;
  (void)transY;
  // TODO: rggNucAssembly::calculateExtraTranslation"
}

void rggNucAssembly::calculateTranslation(double& transX, double& transY)
{
  (void)transX;
  (void)transY;
  // "TODO: rggNucAssembly::calculateTranslation"
}

void rggNucAssembly::setUpdateUsed()
{
  // "TODO: rggNucAssembly::setUpdateUsed"
}

void rggNucAssembly::getRadius(double& ri, double& rj) const
{
  (void)ri;
  (void)rj;
  // "TODO: rggNucAssembly::getRadius"
}

void rggNucAssembly::resetBySMTKAssembly(const smtk::model::EntityRef& assy)
{
  m_entity = assy;
  smtk::model::ResourcePtr resource = assy.resource();

  // Create a new assembly
  if (m_entity.isModel() && !m_entity.hasStringProperty(Assembly::propDescription))
  {
    bool isHex = m_entity.integerProperty(Core::geomDescription)[0] ==
        static_cast<int>(Core::GeomType::Hex);
    m_lattice->SetGeometryType(
      isHex ? rggGeometryType::HEXAGONAL : rggGeometryType::RECTILINEAR);
    int latticeSize = isHex? 1 : 4;
    m_assy.setLatticeSize(latticeSize, latticeSize);
  }
  else
  {
    m_assy = nlohmann::json::parse(m_entity.stringProperty(Assembly::propDescription)[0]);
    // Update lattice related info
    if (assy.owningModel().hasIntegerProperty(Core::geomDescription))
    {
      bool isHex = assy.owningModel().integerProperty(Core::geomDescription)[0] ==
          static_cast<int>(Core::GeomType::Hex);
      m_lattice->SetGeometryType(
        isHex ? rggGeometryType::HEXAGONAL : rggGeometryType::RECTILINEAR);
    }
  }


  auto& latticeSize = m_assy.latticeSize();
  m_lattice->SetDimensions(static_cast<size_t>(latticeSize.first),
                                static_cast<size_t>(latticeSize.second));

  const auto& uuidToSchema = m_assy.layout();
  for (auto& iter : uuidToSchema)
  {
    smtk::model::EntityRef pin = smtk::model::EntityRef(resource, iter.first);
    if (!pin.isValid())
    {
      smtkErrorMacro(
        smtk::io::Logger::instance(), "Assembly's pin ID " << iter.first << " is not valid");
      continue;
    }
    const auto& schema = iter.second;
    for (size_t i = 0; i < schema.size(); i++)
    {
      m_lattice->SetCell(static_cast<size_t>(schema[i].first),
                              static_cast<size_t>(schema[i].second), pin);
    }
  }
}

std::string rggNucAssembly::name() const
{
  return this->m_assy.name().empty()? "Assy-" + Assembly::generateUniqueLabel()
                                    : this->m_assy.name();
}

std::string rggNucAssembly::label() const
{
  return this->m_assy.label().empty()? Assembly::generateUniqueLabel()
                                    : this->m_assy.name();
}

const smtk::model::FloatList& rggNucAssembly::color() const
{
  return this->m_assy.color();
}

// Hex core only uses pitch0
const std::pair<double, double>& rggNucAssembly::pitch() const
{
  return this->m_assy.pitch();
}

// Hex core only uses size0
const std::pair<int, int>& rggNucAssembly::latticeSize() const
{
  return this->m_assy.latticeSize();
}

const bool rggNucAssembly::centerPin() const
{
  return this->m_assy.centerPin();
}


void rggNucAssembly::setAssyDuct(smtk::model::EntityRef duct)
{
  m_assy.setAssociatedDuct(duct.entity());
}

smtk::model::EntityRef rggNucAssembly::getAssyDuct()
{
  return smtk::model::EntityRef(m_resource, m_assy.associatedDuct());
}
