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

#include "smtk/session/rgg/Pin.h"
#include "smtk/session/rgg/Resource.h"
#include "smtk/session/rgg/Session.h"
#include "smtk/session/rgg/json/jsonPin.h"

#include "smtk/session/rgg/operators/CreateModel.h"
#include "smtk/session/rgg/operators/OperationHelper.h"

#include "smtk/io/Logger.h"

#include "smtk/PublicPointerDefs.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/model/AuxiliaryGeometry.h"
#include "smtk/model/Model.h"

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

#include <string> // std::to_string
using namespace smtk::model;
using json = nlohmann::json;

namespace smtk
{
namespace session
{
namespace rgg
{

EditPin::Result EditPin::operateInternal()
{
  EntityRefArray entities = this->parameters()->associatedModelEntities<EntityRefArray>();
  if (entities.empty() || (!entities[0].isAuxiliaryGeometry() && !entities[0].isModel()))
  {
    smtkErrorMacro(this->log(), "Cannot edit a non auxiliary geometry nor model");
    return this->createResult(smtk::operation::Operation::Outcome::FAILED);
  }

  // Get pin representation
  std::string pinRepStr;
  smtk::attribute::StringItemPtr pinRepStrItem = this->parameters()->findString("pin representation");
  if (pinRepStrItem != nullptr && !pinRepStrItem->value(0).empty())
  {
    pinRepStr = pinRepStrItem->value(0);
  }
  Pin pin = json::parse(pinRepStr);

  Result result = this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED);
  if (entities[0].isModel())
  { // Creation mode
    EntityRef parent = entities[0];

    smtk::model::AuxiliaryGeometry newPinAux = parent.resource()->addAuxiliaryGeometry(
                                    parent.as<smtk::model::Model>(), 3);
    newPinAux.setName(pin.name());
    newPinAux.setStringProperty("label", pin.label());
    newPinAux.setStringProperty("rggType", Pin::typeDescription);
    newPinAux.setStringProperty("selectable", Pin::typeDescription);
    newPinAux.setStringProperty(Pin::propDescription, pinRepStr);
    newPinAux.setColor(pin.color());

    // A list contains all subparts and layers of the pin
    std::vector<EntityRef> subAuxGeoms;
    OperationHelper::populateChildrenInPinAux(newPinAux, subAuxGeoms, pin);

    result->findComponent("modified")->appendValue(parent.component());
    smtk::attribute::ComponentItem::Ptr createdItem = result->findComponent("created");
    createdItem->appendValue(newPinAux.component());
    for (auto& c : subAuxGeoms)
    {
      createdItem->appendValue(c.component());
    }
  }
  else
  { // Edit mode
    smtk::model::EntityRefArray expunged, modified, tobeDeleted;

    smtk::model::AuxiliaryGeometry pinAux = entities[0].as<AuxiliaryGeometry>();
    auto resource = std::dynamic_pointer_cast<smtk::session::rgg::Resource>(pinAux.resource());

    // Remove all current child auxiliary geometries first
    EntityRefArray children = pinAux.embeddedEntities<EntityRefArray>();
    tobeDeleted.insert(tobeDeleted.end(), children.begin(), children.end());

    pinAux.setName(pin.name());
    pinAux.setStringProperty("label", pin.label());
    pinAux.setStringProperty(Pin::propDescription, pinRepStr);
    pinAux.setColor(pin.color());

    // A list contains all subparts and layers of the pin
    std::vector<EntityRef> subAuxGeoms;
    OperationHelper::populateChildrenInPinAux(pinAux, subAuxGeoms, pin);

    smtk::attribute::ComponentItem::Ptr createdItem = result->findComponent("created");
    smtk::attribute::ComponentItem::Ptr expungedItem = result->findComponent("expunged");
    smtk::attribute::ComponentItem::Ptr modifiedItem = result->findComponent("modified");
    smtk::attribute::ComponentItem::Ptr tessChangedItem = result->findComponent("tess_changed");
    for (auto& c : subAuxGeoms)
    {
      createdItem->appendValue(c.component());
    }
    for (auto& e : tobeDeleted)
    {
      expungedItem->appendValue(e.component());
    }

    modifiedItem->appendValue(pinAux.component());

    // Delete child aux geoms after being been appended to expunged item since once they
    // are deleted, the component of each aux geom would become invalid.
    resource->deleteEntities(tobeDeleted, modified, expunged, this->m_debugLevel > 0);

    // Update all assemblies/ducts who use this entity
    OperationHelper::updateRelatedAssyAndCore(pinAux, createdItem, modifiedItem,
                                              expungedItem, tessChangedItem, this->m_debugLevel > 0);
  }
  return result;
}

const char* EditPin::xmlDescription() const
{
  return EditPin_xml;
}
} // namespace rgg
} //namespace session
} // namespace smtk
