//=========================================================================
//  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.
//=========================================================================

#ifndef pybind_smtk_session_aeva_Session_h
#define pybind_smtk_session_aeva_Session_h

#include <pybind11/pybind11.h>

#include "smtk/session/aeva/Session.h"
#include "smtk/extension/vtk/pybind11/PybindVTKTypeCaster.h"
#include "vtkDataSet.h"
#include "vtkUnstructuredGrid.h"
#include "vtkPolyData.h"

namespace py = pybind11;

inline PySharedPtrClass< smtk::session::aeva::Session, smtk::model::Session > pybind11_init_smtk_session_aeva_Session(py::module &m)
{
  PySharedPtrClass< smtk::session::aeva::Session, smtk::model::Session > instance(m, "Session");
  instance
    .def("typeName", &smtk::session::aeva::Session::typeName)
    // .def("shared_from_this", (std::shared_ptr<smtk::session::aeva::Session> (smtk::session::aeva::Session::*)()) &smtk::session::aeva::Session::shared_from_this)
    // .def("shared_from_this", (std::shared_ptr<const smtk::session::aeva::Session> (smtk::session::aeva::Session::*)() const) &smtk::session::aeva::Session::shared_from_this)
    .def_static("create", (std::shared_ptr<smtk::session::aeva::Session> (*)()) &smtk::session::aeva::Session::create)
    // .def_static("create", (std::shared_ptr<smtk::session::aeva::Session> (*)(::std::shared_ptr<smtk::session::aeva::Session> &)) &smtk::session::aeva::Session::create, py::arg("ref"))
    .def("allSupportedInformation", &smtk::session::aeva::Session::allSupportedInformation)
    .def("defaultFileExtension", &smtk::session::aeva::Session::defaultFileExtension, py::arg("model"))
    .def("findStorage", &smtk::session::aeva::Session::findStorage, py::arg("uid"))
    .def(
      "addStorage",
      &smtk::session::aeva::Session::addStorage,
      py::arg_v("uid", smtk::common::UUID::null(), "The UUID of an aeva component."),
      py::arg_v("storage", nullptr, "A VTK data object to bind to the aeva component."))
    .def("removeStorage", &smtk::session::aeva::Session::removeStorage, py::arg("uid"))
    // We are not passed arguments by reference, so we use a custom lambda here:
    .def("primaryGeometries", [](
        const smtk::session::aeva::Session& session,
        vtkSmartPointer<vtkDataObject> const& data,
        int fieldType,
        const std::shared_ptr<smtk::session::aeva::Resource>& resource)
      {
        std::set<smtk::model::Entity*> result;
        session.primaryGeometries(data, fieldType, result, resource.get());
        return result;
      },
      py::arg_v(
        "data",
        nullptr,
        "The vtkDataObject storage from which to find primary geometry."),
      py::arg_v(
        "fieldType",
        int(vtkDataObject::CELL),
        "A vtkDataObject::AttributeTypes enum specifying which IDs of the data are used."),
      py::arg_v(
        "resource",
        nullptr,
        "The aeva model resource in which to look for primary geometry.")
    )
    .def("primaryGeometries", [](
       const smtk::session::aeva::Session& session,
       const std::shared_ptr<smtk::model::Entity>& entity)
      {
        std::set<smtk::model::Entity *> result;
        session.primaryGeometries(entity.get(), result);
        return result;
      }, py::arg_v(
        "entity",
        nullptr,
        "The side- or node-set whose primary geometries should be returned.")
    )
    .def("primaryGeometry", &smtk::session::aeva::Session::primaryGeometry, py::arg("in"))
    .def(
      "primaryGeometryForCell",
      &smtk::session::aeva::Session::primaryGeometryForCell,
      py::arg("globalId"),
      "Return the owner of the given cell ID.")
    .def(
      "primaryGeometryForPoint",
      &smtk::session::aeva::Session::primaryGeometryForPoint,
      py::arg("globalId"),
      "Return the owner of the given point ID.")
    .def(
      "sideSets",
      [](smtk::session::aeva::Session& session, const smtk::model::Entity* primary)
      {
        std::set<smtk::model::Entity*> sideSets;
        session.sideSets(primary, sideSets);
        return sideSets;
      },
      py::arg("primary"),
      "Return the side-sets that reference the given primary geometry.")
    .def("buildGlobalIdLookup", &smtk::session::aeva::Session::buildGlobalIdLookup, py::arg("resource"), py::arg("centering"))
    .def("visitPrimaryDataById", &smtk::session::aeva::Session::visitPrimaryDataById, py::arg("resource"), py::arg("ids"), py::arg("context"), py::arg("visitor"))
    .def_static("offsetGlobalIds", &smtk::session::aeva::Session::offsetGlobalIds, py::arg("data"), py::arg("pointIdOffset"), py::arg("cellIdOffset"), py::arg("maxPointId"), py::arg("maxCellId"))
    .def_static("setPrimary", &smtk::session::aeva::Session::setPrimary, py::arg("c"), py::arg("isPrimary"))
    .def_static("isPrimary", &smtk::session::aeva::Session::isPrimary, py::arg("c"))
    .def_static("isSideSet", &smtk::session::aeva::Session::isSideSet, py::arg("c"))
    .def_readonly_static("type_name", &smtk::session::aeva::Session::type_name)
    .def_readonly_static("Dimension", &smtk::session::aeva::Session::Dimension)
    ;
  return instance;
}

#endif
