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

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

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

#include "smtk/session/aeva/operators/AdjacencyFeature.h"
#include "smtk/session/aeva/operators/AllPrimitivesFeature.h"
#include "smtk/session/aeva/operators/Boolean.h"
#include "smtk/session/aeva/operators/BooleanIntersect.h"
#include "smtk/session/aeva/operators/BooleanSubtract.h"
#include "smtk/session/aeva/operators/BooleanUnite.h"
#include "smtk/session/aeva/operators/ChartGeneration.h"
#include "smtk/session/aeva/operators/CreateAnnotationResource.h"
#include "smtk/session/aeva/operators/Delete.h"
#include "smtk/session/aeva/operators/Duplicate.h"
#include "smtk/session/aeva/operators/EditFreeformAttributes.h"
#include "smtk/session/aeva/operators/Export.h"
#include "smtk/session/aeva/operators/ExportFeb.h"
#include "smtk/session/aeva/operators/ExportFebModel.h"
#include "smtk/session/aeva/operators/ExportModel.h"
#include "smtk/session/aeva/operators/GrowSelection.h"
#include "smtk/session/aeva/operators/Import.h"
#include "smtk/session/aeva/operators/ImprintGeometry.h"
#include "smtk/session/aeva/operators/ImprintImage.h"
#include "smtk/session/aeva/operators/LinearToQuadratic.h"
#include "smtk/session/aeva/operators/MeshLabel.h"
#include "smtk/session/aeva/operators/NetgenSurfaceRemesher.h"
#include "smtk/session/aeva/operators/NormalFeature.h"
#include "smtk/session/aeva/operators/PointsOfPrimitivesFeature.h"
#include "smtk/session/aeva/operators/ProportionalEdit.h"
#include "smtk/session/aeva/operators/ProximityFeature.h"
#include "smtk/session/aeva/operators/Read.h"
#include "smtk/session/aeva/operators/ReconstructSurface.h"
#include "smtk/session/aeva/operators/Segment.h"
#include "smtk/session/aeva/operators/SelectionResponder.h"
#include "smtk/session/aeva/operators/SmoothSurface.h"
#include "smtk/session/aeva/operators/TextureAtlas.h"
#include "smtk/session/aeva/operators/UnreferencedPrimitives.h"
#include "smtk/session/aeva/operators/VolumeInspect.h"
#include "smtk/session/aeva/operators/VolumeMesher.h"
#include "smtk/session/aeva/operators/Write.h"

#include "smtk/extension/paraview/server/VTKSelectionResponderGroup.h"

#include "smtk/geometry/Generator.h"
#include "smtk/session/aeva/Geometry.h"
#include "smtk/session/aeva/Resource.h"

#include "smtk/operation/groups/CreatorGroup.h"
#include "smtk/operation/groups/DeleterGroup.h"
#include "smtk/operation/groups/ExporterGroup.h"
#include "smtk/operation/groups/ImporterGroup.h"
#include "smtk/operation/groups/InternalGroup.h"
#include "smtk/operation/groups/ReaderGroup.h"
#include "smtk/operation/groups/WriterGroup.h"

#include "smtk/attribute/Resource.h"

#include "smtk/view/OperationIcons.h"

#include "smtk/plugin/Manager.h"

#include "smtk/session/aeva/adjacency_feature_svg.h"
#include "smtk/session/aeva/all_primitives_feature_svg.h"
#include "smtk/session/aeva/boolean_intersect_svg.h"
#include "smtk/session/aeva/boolean_subtract_svg.h"
#include "smtk/session/aeva/boolean_symmetric_subtract_svg.h"
#include "smtk/session/aeva/boolean_unite_svg.h"
#include "smtk/session/aeva/create_annotations_svg.h"
// #include "smtk/session/aeva/deselect_all_svg.h"
#include "smtk/session/aeva/duplicate_svg.h"
#include "smtk/session/aeva/freeform_attributes_svg.h"
#include "smtk/session/aeva/grow_feature_svg.h"
#include "smtk/session/aeva/imprint_geometry_svg.h"
#include "smtk/session/aeva/imprint_image_svg.h"
#include "smtk/session/aeva/normal_feature_svg.h"
#include "smtk/session/aeva/points_of_primitives_feature_svg.h"
#include "smtk/session/aeva/proportional_edit_svg.h"
#include "smtk/session/aeva/proximity_feature_svg.h"
#include "smtk/session/aeva/quadratic_promotion_svg.h"
#include "smtk/session/aeva/reconstruct_surface_svg.h"
#include "smtk/session/aeva/smooth_surface_svg.h"
#include "smtk/session/aeva/unreferenced_primitives_svg.h"
#include "smtk/session/aeva/volume_inspector_svg.h"
#include "smtk/session/aeva/volume_mesh_svg.h"

namespace smtk
{
namespace session
{
namespace aeva
{

namespace
{
bool styleRegistered = smtk::session::aeva::RegisterStyleFunction::registerClass();

using OperationList = std::tuple<AdjacencyFeature,
  AllPrimitivesFeature,
  Boolean,
  BooleanIntersect,
  BooleanSubtract,
  BooleanUnite,
  ChartGeneration,
  CreateAnnotationResource,
  Delete,
  Duplicate,
  EditFreeformAttributes,
  Export,
  ExportFeb,
  ExportFebModel,
  ExportModel,
  GrowSelection,
  Import,
  ImprintGeometry,
  ImprintImage,
  MeshLabel,
  NetgenSurfaceRemesher,
  NormalFeature,
  LinearToQuadratic,
  PointsOfPrimitivesFeature,
  ProportionalEdit,
  ProximityFeature,
  Read,
  ReconstructSurface,
  Segment,
  SelectionResponder,
  SmoothSurface,
  TextureAtlas,
  UnreferencedPrimitives,
  VolumeInspect,
  VolumeMesher,
  Write>;
}

class RegisterAEVAVTKBackend : public smtk::geometry::Supplier<RegisterAEVAVTKBackend>
{
public:
  bool valid(const Specification& in) const override
  {
    smtk::extension::vtk::geometry::Backend backend;
    return std::get<1>(in).index() == backend.index();
  }

  GeometryPtr operator()(const Specification& in) override
  {
    auto rsrc = std::dynamic_pointer_cast<smtk::session::aeva::Resource>(std::get<0>(in));
    if (rsrc)
    {
      auto* provider = new Geometry(rsrc);
      return GeometryPtr(provider);
    }
    throw std::invalid_argument("Not an aeva resource.");
    return nullptr;
  }
};

void Registrar::registerTo(const smtk::common::Managers::Ptr& managers)
{
  managers->insert(smtk::session::aeva::NameManager::create());

  /*
  if (managers->contains<smtk::resource::Manager>())
  {
    managers->get<smtk::geometry::Manager::Ptr>()->registerResourceManager(
      managers->get<smtk::resource::Manager::Ptr>());
  }
  */
  smtk::plugin::Manager::instance()->registerPluginsTo(
    managers->get<smtk::session::aeva::NameManager::Ptr>());
}

void Registrar::unregisterFrom(const smtk::common::Managers::Ptr& managers)
{
  managers->erase<smtk::session::aeva::NameManager::Ptr>();
}

void Registrar::registerTo(const smtk::resource::Manager::Ptr& resourceManager)
{
  // resource type to a manager
  resourceManager->registerResource<smtk::session::aeva::Resource>(read, write);
  RegisterAEVAVTKBackend::registerClass();
}

void Registrar::registerTo(const smtk::operation::Manager::Ptr& operationManager)
{
  // Register operations to the operation manager
  operationManager->registerOperations<OperationList>();

  smtk::operation::ImporterGroup(operationManager)
    .registerOperation<smtk::session::aeva::Resource, smtk::session::aeva::Import>();

  smtk::operation::ExporterGroup(operationManager)
    .registerOperation<smtk::session::aeva::Resource, smtk::session::aeva::Export>();

  smtk::operation::ExporterGroup(operationManager)
    .registerOperation<smtk::session::aeva::Resource, smtk::session::aeva::ExportFeb>();

  smtk::view::VTKSelectionResponderGroup responders(operationManager, nullptr);
  responders.registerOperation<smtk::resource::Resource, smtk::session::aeva::SelectionResponder>();

  smtk::operation::InternalGroup internal(operationManager);
  internal.registerOperation<smtk::session::aeva::SelectionResponder>();

  smtk::operation::WriterGroup(operationManager)
    .registerOperation<smtk::session::aeva::Resource, smtk::session::aeva::Write>();
  smtk::operation::ReaderGroup(operationManager)
    .registerOperation<smtk::session::aeva::Resource, smtk::session::aeva::Read>();

  smtk::operation::CreatorGroup(operationManager)
    .registerOperation<smtk::session::aeva::Resource,
      smtk::session::aeva::CreateAnnotationResource>();
  smtk::operation::DeleterGroup(operationManager).registerOperation<smtk::session::aeva::Delete>();
}

void Registrar::unregisterFrom(const smtk::resource::Manager::Ptr& resourceManager)
{
  resourceManager->unregisterResource<smtk::session::aeva::Resource>();
}

void Registrar::unregisterFrom(const smtk::operation::Manager::Ptr& operationManager)
{
  smtk::operation::ImporterGroup(operationManager)
    .unregisterOperation<smtk::session::aeva::Import>();
  smtk::operation::ExporterGroup(operationManager)
    .unregisterOperation<smtk::session::aeva::Export>();
  smtk::operation::ExporterGroup(operationManager)
    .unregisterOperation<smtk::session::aeva::ExportFeb>();

  smtk::view::VTKSelectionResponderGroup responders(operationManager, nullptr);
  responders.unregisterOperation<smtk::session::aeva::SelectionResponder>();

  smtk::operation::InternalGroup internal(operationManager);
  internal.unregisterOperation<smtk::session::aeva::SelectionResponder>();

  smtk::operation::WriterGroup(operationManager).unregisterOperation<smtk::session::aeva::Write>();
  smtk::operation::ReaderGroup(operationManager).unregisterOperation<smtk::session::aeva::Read>();

  smtk::operation::CreatorGroup(operationManager)
    .unregisterOperation<smtk::session::aeva::CreateAnnotationResource>();
  smtk::operation::DeleterGroup(operationManager)
    .unregisterOperation<smtk::session::aeva::Delete>();

  operationManager->unregisterOperations<OperationList>();
}

void Registrar::registerTo(const smtk::view::Manager::Ptr& viewManager)
{
  auto& opIcons(viewManager->operationIcons());
  opIcons.registerOperation<AdjacencyFeature>(
    [](const std::string& /*unused*/) { return adjacency_feature_svg; });
  opIcons.registerOperation<AllPrimitivesFeature>(
    [](const std::string& /*unused*/) { return all_primitives_feature_svg; });
  opIcons.registerOperation<BooleanIntersect>(
    [](const std::string& /*unused*/) { return boolean_intersect_svg; });
  opIcons.registerOperation<BooleanSubtract>(
    [](const std::string& /*unused*/) { return boolean_subtract_svg; });
#if 0
  opIcons.registerOperation<BooleanSymmetricSubtract>(
    [](const std::string& /*unused*/) { return boolean_symmetric_subtract_svg; });
#endif
  opIcons.registerOperation<BooleanUnite>(
    [](const std::string& /*unused*/) { return boolean_unite_svg; });
  opIcons.registerOperation<CreateAnnotationResource>(
    [](const std::string& /*unused*/) { return create_annotations_svg; });
  opIcons.registerOperation<Duplicate>([](const std::string& /*unused*/) { return duplicate_svg; });
  opIcons.registerOperation<EditFreeformAttributes>(
    [](const std::string& /*unused*/) { return freeform_attributes_svg; });
  opIcons.registerOperation<GrowSelection>(
    [](const std::string& /*unused*/) { return grow_feature_svg; });
  opIcons.registerOperation<ImprintGeometry>(
    [](const std::string& /*unused*/) { return imprint_geometry_svg; });
  opIcons.registerOperation<ImprintImage>(
    [](const std::string& /*unused*/) { return imprint_image_svg; });
  opIcons.registerOperation<LinearToQuadratic>(
    [](const std::string& /*unused*/) { return quadratic_promotion_svg; });
  opIcons.registerOperation<NormalFeature>(
    [](const std::string& /*unused*/) { return normal_feature_svg; });
  opIcons.registerOperation<PointsOfPrimitivesFeature>(
    [](const std::string& /*unused*/) { return points_of_primitives_feature_svg; });
  opIcons.registerOperation<ProportionalEdit>(
    [](const std::string& /*unused*/) { return proportional_edit_svg; });
  opIcons.registerOperation<ProximityFeature>(
    [](const std::string& /*unused*/) { return proximity_feature_svg; });
  opIcons.registerOperation<ReconstructSurface>(
    [](const std::string& /*unused*/) { return reconstruct_surface_svg; });
  opIcons.registerOperation<SmoothSurface>(
    [](const std::string& /*unused*/) { return smooth_surface_svg; });
  opIcons.registerOperation<UnreferencedPrimitives>(
    [](const std::string& /*unused*/) { return unreferenced_primitives_svg; });
  opIcons.registerOperation<VolumeInspect>(
    [](const std::string& /*unused*/) { return volume_inspector_svg; });
  opIcons.registerOperation<VolumeMesher>(
    [](const std::string& /*unused*/) { return volume_mesh_svg; });
}

void Registrar::unregisterFrom(const smtk::view::Manager::Ptr& viewManager)
{
  auto& opIcons(viewManager->operationIcons());
  opIcons.unregisterOperation<AdjacencyFeature>();
  opIcons.unregisterOperation<AllPrimitivesFeature>();
  opIcons.unregisterOperation<BooleanIntersect>();
  opIcons.unregisterOperation<BooleanSubtract>();
#if 0
  opIcons.unregisterOperation<BooleanSymmetricSubtract>();
#endif
  opIcons.unregisterOperation<BooleanUnite>();
  opIcons.unregisterOperation<CreateAnnotationResource>();
  opIcons.unregisterOperation<Duplicate>();
  opIcons.unregisterOperation<EditFreeformAttributes>();
  opIcons.unregisterOperation<GrowSelection>();
  opIcons.unregisterOperation<ImprintGeometry>();
  opIcons.unregisterOperation<ImprintImage>();
  opIcons.unregisterOperation<LinearToQuadratic>();
  opIcons.unregisterOperation<NormalFeature>();
  opIcons.unregisterOperation<PointsOfPrimitivesFeature>();
  opIcons.unregisterOperation<ProportionalEdit>();
  opIcons.unregisterOperation<ProximityFeature>();
  opIcons.unregisterOperation<ReconstructSurface>();
  opIcons.unregisterOperation<SmoothSurface>();
  opIcons.unregisterOperation<UnreferencedPrimitives>();
  opIcons.unregisterOperation<VolumeInspect>();
  opIcons.unregisterOperation<VolumeMesher>();
}

}
}
}
