#include "implement_an_operator.h"

#include "smtk/AutoInit.h"

#include "smtk/common/UUID.h"

#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/ComponentItem.h"
#include "smtk/attribute/Definition.h"
#include "smtk/attribute/IntItem.h"
#include "smtk/attribute/Resource.h"

#include "smtk/model/DefaultSession.h"
#include "smtk/model/Group.h"
#include "smtk/model/Model.h"
#include "smtk/model/Resource.h"
#include "smtk/model/Session.h"
#include "smtk/model/Volume.h"

#include "smtk/common/testing/cxx/helpers.h"
#include "smtk/model/testing/cxx/helpers.h"

#include "smtk/Options.h"

// ++ 1 ++
// Include the encoded XML describing the operator class.
// This is generated by CMake.
#include "implement_an_operator_xml.h"
// -- 1 --

using namespace smtk::common;
using namespace smtk::model;
using smtk::attribute::IntItem;

namespace ex
{

// ++ 2 ++
smtk::operation::XMLOperation::Result CounterOperation::operateInternal()
{
  // Get the attribute holding parameter values:
  auto params = this->parameters();

  // Get the input model to be processed:
  Model model = params->findComponent("model")->valueAs<smtk::model::Entity>();

  // Decide whether we should count cells or groups
  // of the model:
  int countGroups = params->findInt("count groups")->value();

  // Create the attribute holding the results of
  // our operation using a convenience method
  // provided by the Operation base class.
  // Our operation is simple; we always succeed.
  auto result = this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED);

  // Fetch the item to store our output:
  smtk::attribute::IntItemPtr cellCount = result->findInt("count");

  cellCount->setValue(
    countGroups ? static_cast<int>(model.groups().size()) : static_cast<int>(model.cells().size()));

  return result;
}
// -- 2 --

// ++ 3 ++
// Implement virtual overrides from base class to handle registration.
const char* CounterOperation::xmlDescription() const
{
  return implement_an_operator_xml;
}
// -- 3 --

} // namespace ex

void testOperation(Model model)
{
  auto op = ex::CounterOperation::create();

  smtk::attribute::ComponentItemPtr input = op->parameters()->findComponent("model");
  input->setObjectValue(model.component());

  test(!!op, "Could not create operator.");
  test(
    op->operate()->findInt("count")->value() == 1,
    "Did not return the proper number of top-level cells.");

  op->parameters()->findInt("count groups")->setValue(1);
  test(
    op->operate()->findInt("count")->value() == 0,
    "Did not return the proper number of top-level group.");
}

int main()
{
  int status = 0;

  Resource::Ptr resource = Resource::create();
  SessionRef session; // = resource->createSession("native");
  UUIDArray uids = smtk::model::testing::createTet(resource);

  Model model = resource->addModel(3, 3, "TestModel");
  Volume tet = Volume(resource, uids[21]);
  model.setSession(session);
  model.addCell(tet);

  try
  {

    testOperation(model);
  }
  catch (const std::string& msg)
  {
    (void)msg; // Ignore the message; it's already been printed.
    std::cerr << "Exiting...\n";
    status = -1;
  }

  return status;
}
