//=========================================================================
//  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/aeva/operators/EditFreeformAttributes.h"

#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/ComponentItem.h"
#include "smtk/attribute/DoubleItem.h"
#include "smtk/attribute/GroupItem.h"
#include "smtk/attribute/IntItem.h"
#include "smtk/attribute/StringItem.h"
#include "smtk/resource/Component.h"
#include "smtk/resource/properties/CoordinateFrame.h"

#include "smtk/session/aeva/EditFreeformAttributes_xml.h"

using smtk::attribute::ComponentItem;
using smtk::attribute::DoubleItem;
using smtk::attribute::IntItem;
using smtk::attribute::GroupItem;
using smtk::attribute::StringItem;
using smtk::model::Float;
using smtk::model::FloatList;
using smtk::model::Integer;
using smtk::model::IntegerList;
using smtk::model::String;
using smtk::model::StringList;
using smtk::resource::Component;
using smtk::resource::properties::CoordinateFrame;

namespace smtk
{
namespace session
{
namespace aeva
{

template<typename V, typename VL, typename VI>
void EditFreeformAttributesValue(const std::string& name,
  typename VI::Ptr item,
  smtk::attribute::ReferenceItemPtr& entities)
{
  if (!item || item->numberOfValues() == 0)
  {
    // Erase the property of this type from these entities,
    // if they had the property in the first place.
    for (smtk::resource::PersistentObjectPtr entity : *entities)
    {
      entity->properties().erase<VL>(name);
    }
  }
  else
  {
    // Get the array of values from the item.
    VL values;
    values.reserve(item->numberOfValues());
    for (std::size_t i = 0; i < item->numberOfValues(); ++i)
    {
      values.push_back(item->value(i));
    }

    // Add or overwrite the property with the values.
    for (smtk::resource::PersistentObjectPtr entity : *entities)
    {
      entity->properties().get<VL>()[name] = values;
    }
  }
}

template<typename V, typename VI>
void EditFreeformAttributesValue(const std::string& name,
  typename VI::Ptr item,
  smtk::attribute::ReferenceItemPtr& entities)
{
  if (!item || item->numberOfValues() == 0)
  {
    // Erase the property of this type from these entities,
    // if they had the property in the first place.
    for (smtk::resource::PersistentObjectPtr entity : *entities)
    {
      entity->properties().erase<V>(name);
    }
  }
  else
  {
    // Get the array of values from the item.
    V value = item->value();

    // Add or overwrite the property with the values.
    for (smtk::resource::PersistentObjectPtr entity : *entities)
    {
      entity->properties().get<V>()[name] = value;
    }
  }
}

void EditFreeformAttributesValue(const std::string& name,
  GroupItem::Ptr item,
  smtk::attribute::ReferenceItemPtr& entities)
{
  if (!item || item->numberOfGroups() == 0)
  {
    // Erase the property of this type from these entities,
    // if they had the property in the first place.
    for (smtk::resource::PersistentObjectPtr entity : *entities)
    {
      entity->properties().erase<CoordinateFrame>(name);
    }
  }
  else
  {
    // Construct a CoordinateFrame object from the GroupItem.
    smtk::attribute::ConstGroupItemPtr constItem(item);
    CoordinateFrame value;
    for (int ii = 0; ii < 3; ++ii)
    {
      value.origin[ii] = constItem->findAs<DoubleItem>(0, "Origin")->value(ii);
      value.xAxis[ii] = constItem->findAs<DoubleItem>(0, "XAxis")->value(ii);
      value.yAxis[ii] = constItem->findAs<DoubleItem>(0, "YAxis")->value(ii);
      value.zAxis[ii] = constItem->findAs<DoubleItem>(0, "ZAxis")->value(ii);
    }
    auto parentItem = constItem->findAs<ComponentItem>(0, "Parent");
    value.parent = parentItem->numberOfValues() > 0 && parentItem->isSet(0) ?
      parentItem->value(0) : nullptr;

    // Add or overwrite the property with the values.
    for (smtk::resource::PersistentObjectPtr entity : *entities)
    {
      entity->properties().get<CoordinateFrame>()[name] = value;
    }
  }
}

EditFreeformAttributes::Result EditFreeformAttributes::operateInternal()
{
  smtk::attribute::StringItemPtr nameItem = this->parameters()->findString("name");
  smtk::attribute::StringItemPtr stringItem = this->parameters()->findString("string value");
  smtk::attribute::DoubleItemPtr floatItem = this->parameters()->findDouble("float value");
  smtk::attribute::IntItemPtr integerItem = this->parameters()->findInt("integer value");
  smtk::attribute::GroupItemPtr groupItem = this->parameters()->findGroup("Coordinate Frame");

  auto associations = this->parameters()->associations();

  if (nameItem->value(0).empty())
  {
    return this->createResult(smtk::operation::Operation::Outcome::FAILED);
  }

  if (stringItem->numberOfValues() == 0)
  {
    EditFreeformAttributesValue<String, StringItem>(nameItem->value(0), stringItem, associations);
    EditFreeformAttributesValue<String, StringList, StringItem>(
      nameItem->value(0), stringItem, associations);
  }
  else if (stringItem->numberOfValues() == 1)
  {
    EditFreeformAttributesValue<String, StringItem>(nameItem->value(0), stringItem, associations);
  }
  else
  {
    EditFreeformAttributesValue<String, StringList, StringItem>(
      nameItem->value(0), stringItem, associations);
  }

  if (floatItem->numberOfValues() == 0)
  {
    EditFreeformAttributesValue<Float, DoubleItem>(nameItem->value(0), floatItem, associations);
    EditFreeformAttributesValue<Float, FloatList, DoubleItem>(
      nameItem->value(0), floatItem, associations);
  }
  else if (floatItem->numberOfValues() == 1)
  {
    EditFreeformAttributesValue<Float, DoubleItem>(nameItem->value(0), floatItem, associations);
  }
  else
  {
    EditFreeformAttributesValue<Float, FloatList, DoubleItem>(
      nameItem->value(0), floatItem, associations);
  }

  if (integerItem->numberOfValues() == 0)
  {
    EditFreeformAttributesValue<Integer, IntItem>(nameItem->value(0), integerItem, associations);
    EditFreeformAttributesValue<Integer, IntegerList, IntItem>(
      nameItem->value(0), integerItem, associations);
  }
  else if (integerItem->numberOfValues() == 1)
  {
    EditFreeformAttributesValue<Integer, IntItem>(nameItem->value(0), integerItem, associations);
  }
  else
  {
    EditFreeformAttributesValue<Integer, IntegerList, IntItem>(
      nameItem->value(0), integerItem, associations);
  }

  // groupItem for CoordinateFrame
  EditFreeformAttributesValue(nameItem->value(0), groupItem, associations);

  Result result = this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED);

  smtk::attribute::ComponentItemPtr modifiedItem = result->findComponent("modified");
  for (smtk::resource::PersistentObjectPtr association : *associations)
  {
    if (auto component = std::dynamic_pointer_cast<Component>(association))
    {
      modifiedItem->appendValue(component);
    }
  }

  return result;
}

const char* EditFreeformAttributes::xmlDescription() const
{
  return EditFreeformAttributes_xml;
}

}
}
}
