/**
 * @file bindings.h
 * @brief Declares functions to register MFEM core library components with pybind11.
 *
 * This header file lists the functions responsible for creating Python bindings
 * for various parts of the MFEM library. Each function typically registers
 * a set of related classes, enums, or functionalities to a pybind11::module,
 * which is expected to be a submodule named `mfem` within the main `serif` Python module.
 *
 * @see /Users/tboudreaux/Programming/SERiF/src/python/bindings.cpp for how these are used.
 */
#pragma once

#include <pybind11/pybind11.h>

/**
 * @brief Registers all core MFEM bindings to the given Python submodule.
 *
 * This function serves as the main entry point for exposing MFEM functionalities
 * to Python. It calls various other `register_*_bindings` and `bind_*_enum`
 * functions to populate the `mfem_submodule`.
 *
 * @param mfem_submodule The pybind11 module (typically `serif.mfem`) to which
 *                       MFEM bindings will be added.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * # Now mfem.Operator, mfem.Vector, mfem.Mesh, etc., are accessible.
 * vec = mfem.Vector(10)
 * print(vec.Size())
 * @endcode
 */
void register_mfem_bindings(pybind11::module &mfem_submodule);

/**
 * @brief Registers mfem::Operator and related classes.
 * @param mfem_submodule The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * # Assuming PyOperator trampoline is bound
 * # op = mfem.Operator() # Or a derived class like mfem.DenseMatrix
 * @endcode
 */
void register_operator_bindings(pybind11::module &mfem_submodule);

/**
 * @brief Registers mfem::Matrix and its derived classes (e.g., mfem::DenseMatrix, mfem::SparseMatrix).
 * @param mfem_submodule The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * mat = mfem.DenseMatrix(2, 2)
 * mat[0,0] = 1.0
 * mat[0,1] = 2.0
 * mat[1,0] = 3.0
 * mat[1,1] = 4.0
 * mat.Print()
 * @endcode
 */
void register_matrix_bindings(pybind11::module &mfem_submodule);

/**
 * @brief Registers mfem::Vector.
 * @param mfem_submodule The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * vec = mfem.Vector(5)
 * vec[0] = 1.5
 * print(vec.Size(), vec[0])
 * @endcode
 */
void register_vector_bindings(pybind11::module &mfem_submodule);

/**
 * @brief Registers mfem::Array.
 * @param mfem_submodule The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * arr_int = mfem.intArray(5) # Assuming intArray is a typedef or specific binding
 * arr_int[0] = 10
 * print(arr_int.Size(), arr_int[0])
 * @endcode
 */
void register_array_bindings(pybind11::module &mfem_submodule);

/**
 * @brief Binds the mfem::AssemblyLevel enum.
 * @param mfem_submodule The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * # level = mfem.AssemblyLevel.LEGACY # Or other enum values
 * # print(level)
 * @endcode
 */
void bind_assembly_level_enum(pybind11::module &mfem_submodule);

/**
 * @brief Registers mfem::BilinearForm and related functionalities.
 * @param mfem_submodule The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * # Assuming FiniteElementSpace (fes) is created
 * # bform = mfem.BilinearForm(fes)
 * # bform.AddDomainIntegrator(mfem.MassIntegrator()) # Assuming MassIntegrator is bound
 * # bform.Assemble()
 * # A = bform.SpMat()
 * @endcode
 */
void register_bilinear_form_bindings(pybind11::module &mfem_submodule);

/**
 * @brief Registers mfem::MixedBilinearForm and related functionalities.
 * @param mfem_submodule The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * # Assuming trial_fes and test_fes are FiniteElementSpaces
 * # mbform = mfem.MixedBilinearForm(trial_fes, test_fes)
 * # mbform.AddDomainIntegrator(mfem.VectorFEMassIntegrator()) # Example
 * # mbform.Assemble()
 * @endcode
 */
void register_mixed_bilinear_form_bindings(pybind11::module &mfem_submodule);

/**
 * @brief Registers mfem::Table.
 * @param mfem_submodule The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * table = mfem.Table()
 * # ... use table methods ...
 * @endcode
 */
void register_table_bindings(pybind11::module &mfem_submodule);

/**
 * @brief Registers mfem::Mesh.
 * @param mfem_submodule The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * mesh = mfem.Mesh.MakeCartesian1D(10) # Example constructor
 * print(mesh.Dimension(), mesh.GetNE())
 * @endcode
 */
void register_mesh_bindings(pybind11::module &mfem_submodule);

/**
 * @brief Registers mfem::BasisType enum and related constants.
 * @param mfem_submodule The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * basis_type = mfem.BasisType.GaussLobatto
 * print(basis_type)
 * @endcode
 */
void register_basis_type_bindings(pybind11::module &mfem_submodule);

/**
 * @brief Registers mfem::FiniteElementCollection base class.
 * @param mfem_submodule The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * # fec = mfem.FiniteElementCollection() # Typically use derived classes
 * @endcode
 */
void register_finite_element_collection_bindings(pybind11::module &mfem_submodule);

/**
 * @brief Registers mfem::H1_FECollection.
 * @param mfem_submodule The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * order = 1
 * dim = 2
 * fec = mfem.H1_FECollection(order, dim)
 * print(fec.GetName())
 * @endcode
 */
void register_H1_FECollection_bindings(pybind11::module &mfem_submodule);

/**
 * @brief Registers mfem::RT_FECollection.
 * @param mfem_submodule The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * order = 1
 * dim = 2
 * fec = mfem.RT_FECollection(order-1, dim) # RT_FECollection uses p-1 for order p
 * print(fec.GetName())
 * @endcode
 */
void register_RT_FECollection_bindings(pybind11::module &mfem_submodule);

/**
 * @brief Registers mfem::ND_FECollection (Nedelec finite elements).
 * @param mfem_submodule The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * order = 1
 * dim = 3
 * fec = mfem.ND_FECollection(order, dim)
 * print(fec.GetName())
 * @endcode
 */
void register_ND_FECollection_bindings(pybind11::module &mfem_submodule);

/**
 * @brief Binds the mfem::Ordering::Type enum.
 * @param mfem_submodule The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * ordering = mfem.Ordering.byNODES
 * print(ordering)
 * @endcode
 */
void bind_ordering_enum(pybind11::module &mfem_submodule);

/**
 * @brief Registers mfem::FiniteElementSpace.
 * @param mfem_submodule The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * mesh = mfem.Mesh.MakeCartesian1D(5)
 * fec = mfem.H1_FECollection(1, mesh.Dimension())
 * fes = mfem.FiniteElementSpace(mesh, fec)
 * print(fes.GetNDofs())
 * @endcode
 */
void register_finite_element_space_bindings(pybind11::module &mfem_submodule);

/**
 * @brief Registers mfem::Coefficient, mfem::VectorCoefficient and related classes/trampolines.
 * @param m The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * const_coeff = mfem.ConstantCoefficient(2.5)
 * # vec_coeff = mfem.VectorConstantCoefficient(mfem.Vector([1.0, 2.0]))
 *
 * # Using a Python-derived coefficient (if PyCoefficient trampoline is bound)
 * class MyCoeff(mfem.Coefficient):
 *     def Eval(self, T, ip):
 *         return 1.0
 * my_c = MyCoeff()
 * @endcode
 */
void register_coefficient_bindings(pybind11::module &m);

/**
 * @brief Registers mfem::ElementTransformation.
 * @param m The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * # ElementTransformation objects are usually obtained from Mesh or FiniteElementSpace
 * # mesh = mfem.Mesh.MakeCartesian1D(1)
 * # el_trans = mesh.GetElementTransformation(0)
 * # print(el_trans.ElementNo)
 * @endcode
 */
void register_eltrans_bindings(pybind11::module &m);

/**
 * @brief Registers mfem::IntegrationRule and mfem::IntegrationPoint.
 * @param m The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * # Get a standard integration rule
 * ir = mfem.IntRules.Get(mfem.Geometry.SEGMENT, 3) # Order 3 for a segment
 * for i in range(ir.GetNPoints()):
 *     ip = ir.IntPoint(i)
 *     # print(f"Point {i}: coords {ip.x}, weight {ip.weight}")
 * @endcode
 */
void register_intrule_bindings(pybind11::module &m);

/**
 * @brief Registers mfem::GridFunction.
 * @param mfem_submodule The `serif.mfem` Python submodule.
 * @par Python Usage Example:
 * @code{.py}
 * import serif.mfem as mfem
 * mesh = mfem.Mesh.MakeCartesian1D(5)
 * fec = mfem.H1_FECollection(1, mesh.Dimension())
 * fes = mfem.FiniteElementSpace(mesh, fec)
 * gf = mfem.GridFunction(fes)
 * gf.Assign(0.0) # Set all values to 0
 * print(gf.Size())
 * @endcode
 */
void register_grid_function_bindings(pybind11::module &mfem_submodule);

