#include <tiledb/tiledb>
#include <tiledb/tiledb_experimental>

#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <pybind11/pytypes.h>
#include <pybind11/stl.h>

#include "metadata.h"

namespace libtiledbcpp {

using namespace tiledb;
using namespace tiledbpy::common;
namespace py = pybind11;

void init_group(py::module &m) {
  py::class_<Group>(m, "Group")
      .def(
          py::init<const Context &, const std::string &, tiledb_query_type_t>(),
          py::keep_alive<1, 2>())
      .def(py::init<const Context &, const std::string &, tiledb_query_type_t,
                    const Config &>(),
           py::keep_alive<1, 2>())

      .def("_open", &Group::open)
      .def("_set_config", &Group::set_config)
      .def("_config", &Group::config)
      .def("_close", [](Group &self) { self.close(true); })
      .def_property_readonly("_isopen", &Group::is_open)
      .def_property_readonly("_uri", &Group::uri)
      .def_property_readonly("_query_type", &Group::query_type)

      .def("_put_metadata",
           [](Group &group, const std::string &key, py::array value) {
             MetadataAdapter<Group> a;
             a.put_metadata_numpy(group, key, value);
           })
      .def("_put_metadata",
           [](Group &group, const std::string &key,
              tiledb_datatype_t value_type, uint32_t value_num,
              py::buffer value) {
             MetadataAdapter<Group> a;
             a.put_metadata(group, key, value_type, value_num, value);
           })
      .def("_delete_metadata", &Group::delete_metadata)
      .def("_has_metadata",
           [](Group &group, const std::string &key) {
             MetadataAdapter<Group> a;
             return a.has_metadata(group, key);
           })
      .def("_metadata_num", &Group::metadata_num)
      .def("_get_metadata",
           [](Group &group, const std::string &key, bool is_ndarray) {
             MetadataAdapter<Group> a;
             return a.get_metadata(group, key, is_ndarray);
           })
      .def("_get_key_from_index",
           [](Group &group, uint64_t index) {
             MetadataAdapter<Group> a;
             return a.get_key_from_index(group, index);
           })

      .def("_add", &Group::add_member, py::arg("uri"),
           py::arg("relative") = false, py::arg("name") = std::nullopt
#if TILEDB_VERSION_MAJOR >= 2 && TILEDB_VERSION_MINOR >= 27
           ,
           py::arg("type") = std::nullopt
#endif
           )
      .def("_remove", &Group::remove_member)
      .def("_delete_group", &Group::delete_group)
      .def("_member_count", &Group::member_count)
      .def("_member",
           static_cast<Object (Group::*)(uint64_t) const>(&Group::member))
      .def("_member",
           static_cast<Object (Group::*)(std::string) const>(&Group::member))
      .def("_has_member",
           [](Group &group, std::string obj) {
             MetadataAdapter<Group> a;
             return a.has_member(group, obj);
           })
      .def("_is_relative", &Group::is_relative)
      .def("_dump", &Group::dump)

      /* static methods */
      .def_static("_create", &Group::create)
      .def_static("_consolidate_metadata", &Group::consolidate_metadata,
                  py::arg("ctx"), py::arg("uri"),
                  py::arg("config") = (Config *)nullptr)
      .def_static("_vacuum_metadata", &Group::vacuum_metadata, py::arg("ctx"),
                  py::arg("uri"), py::arg("config") = (Config *)nullptr);
}

} // namespace libtiledbcpp
