#include "meca.h"
#include <pybind11/pybind11.h>
#include "python_utilities.h"
//#include <pybind11/numpy.h>
//#include <pybind11/stl.h>
namespace py = pybind11;

class Meca;

/*
 @ToDo : an overload cast for functions either for Mecapoint or Interpolation
*/ 

/// a utility to enrich the cytosim python module
void load_meca_classes(py::module_ &m) {
    py::class_<Meca>(m, "Meca")
        .def("nb_points", &Meca::nb_points)
        .def("dimension", &Meca::dimension)
        .def("prepare", &Meca::prepare)
        .def("apply", &Meca::apply)
        .def("solve", &Meca::solve)
        .def("computeForces", &Meca::computeForces)
        .def("base", [](Meca & mec) { return to_numpy_raw(mec.base(), mec.nb_points(), (int) DIM ); } , PYOWN)
        .def("points", [](Meca & mec) { return to_numpy_raw(mec.points(), mec.nb_points(), (int) DIM ); } , PYOWN)
        .def("force", [](Meca & mec) { return to_numpy_raw(mec.force(), mec.nb_points(), (int) DIM ); } , PYOWN)
        //.def("addForcePt", [](Meca & mec, const Mecapoint & pt, pyarray force) {mec.addForce(pt,to_vector(force));})
        .def("addForce", [](Meca & mec, const Mecapoint & pt, pyarray force) {mec.addForce(pt,to_vector(force));})
        .def("addForce", [](Meca & mec, const Interpolation & pt, pyarray force) {mec.addForce(pt,to_vector(force));})
        .def("addTorque", [](Meca & mec, const Interpolation & pt, pyarray torque) {mec.addTorque(pt,to_torque(torque));})
        .def("addTorqueClamp", [](Meca & mec, const Interpolation & pt, pyarray dir, real w) {mec.addTorqueClamp(pt,to_vector(dir),w);})
        .def("addTorqueParrallel", [](Meca & mec, const Interpolation & pt1, const Interpolation & pt2, real w) {mec.addTorque(pt1,pt2,w);})
        .def("addTorqueAngle", [](Meca & mec, const Interpolation & pt1, const Interpolation & pt2, real cosi, real sinu, real w) 
            {mec.addTorque(pt1,pt2,cosi,sinu,w);})
#if (DIM==2)
        .def("addTorquePoliti", [](Meca & mec, const Interpolation & pt1, const Interpolation & pt2, real cosi, real sinu, real w) 
            {mec.addTorquePoliti(pt1,pt2,cosi,sinu,w);})
#endif
        .def("addPointClamp", [](Meca & mec, const Mecapoint & pt, pyarray pos, real w) {mec.addPointClamp(pt,to_vector(pos),w);})
        .def("addPointClamp", [](Meca & mec, const Interpolation & pt, pyarray pos, real w) {mec.addPointClamp(pt,to_vector(pos),w);})
        .def("addSphereClamp", [](Meca & mec,  py::array pos, const Mecapoint & pt, pyarray cen, real rad, real w) 
            {mec.addSphereClamp(to_vector(pos),pt,to_vector(cen),rad,w);})
        .def("addCylinderClampZ", [](Meca & mec, const Mecapoint & pt, real len, real w) {mec.addCylinderClampZ(pt,len,w);})
        .def("addCylinderClampX", [](Meca & mec, const Mecapoint & pt, real len, real w) {mec.addCylinderClampX(pt,len,w);})
        .def("addSidePointClamp", [](Meca & mec, const Interpolation & pt, pyarray pos, real len, real w)
            {mec.addSidePointClamp(pt,to_vector(pos),len,w);})
        .def("addLineClamp", [](Meca & mec, const Interpolation & pt, pyarray g, pyarray dir, real w)
            {mec.addLineClamp(pt,to_vector(g),to_vector(dir),w);})
        .def("addLineClamp", [](Meca & mec, const Mecapoint & pt, pyarray g, pyarray dir, real w)
            {mec.addLineClamp(pt,to_vector(g),to_vector(dir),w);})
        .def("addPlaneClamp", [](Meca & mec, const Interpolation & pt, pyarray g, pyarray dir, real w)
            {mec.addPlaneClamp(pt,to_vector(g),to_vector(dir),w);})
        .def("addPlaneClamp", [](Meca & mec, const Mecapoint & pt, pyarray g, pyarray dir, real w)
            {mec.addPlaneClamp(pt,to_vector(g),to_vector(dir),w);})
        .def("addLink", [](Meca & mec, const Interpolation & pt1, const Interpolation & pt2, real w)
            {mec.addLink(pt1,pt2,w);})
        .def("addLink", [](Meca & mec, const Mecapoint & pt1, const Interpolation & pt2, real w)
            {mec.addLink(pt1,pt2,w);})
        .def("addLink", [](Meca & mec, const Interpolation & pt1, const Mecapoint & pt2, real w)
            {mec.addLink(pt1,pt2,w);})
        .def("addLink", [](Meca & mec, const Mecapoint & pt1, const Mecapoint & pt2, real w)
            {mec.addLink(pt1,pt2,w);})
        .def("addLongLink", [](Meca & mec, const Interpolation & pt1, const Interpolation & pt2, real l, real w)
            {mec.addLongLink(pt1,pt2,l,w);})
        .def("addLongLink", [](Meca & mec, const Mecapoint & pt1, const Mecapoint & pt2, real l, real w)
            {mec.addLongLink(pt1,pt2,l,w);})
        .def("addLongLink", [](Meca & mec, const Mecapoint & pt1, const Interpolation & pt2, real l, real w)
            {mec.addLongLink(pt1,pt2,l,w);})
#if ( DIM == 2 )       
        .def("addSideLink2D", [](Meca & mec, const Interpolation & pt1, const Interpolation & pt2, real arm, real w)
            {mec.addSideLink2D(pt1,pt2,arm, w);})
        .def("addSideLink2D", [](Meca & mec, const Interpolation & pt1, const Mecapoint & pt2, real arm, real w)
            {mec.addSideLink2D(pt1,pt2,arm, w);})
        .def("addSideSideLink2D", [](Meca & mec, const Interpolation & pt1, const Interpolation & pt2, real arm, real w, real s1, real s2)
            {mec.addSideSideLink2D(pt1,pt2,arm, w, s1, s2);})
        .def("addSideSlidingLink2D", [](Meca & mec, const Interpolation & pt1, const Interpolation & pt2, real arm, real w)
            {mec.addSideSlidingLink2D(pt1,pt2,arm, w);})
        .def("addSideSlidingLink2D", [](Meca & mec, const Interpolation & pt1, const Mecapoint & pt2, real arm, real w)
            {mec.addSideSlidingLink2D(pt1,pt2,arm, w);})
         .def("addSideSlidingLinkS", [](Meca & mec, const Interpolation & pt1, const Interpolation & pt2, real arm, real w)
            {mec.addSideSlidingLinkS(pt1,pt2,arm, w);})
         .def("addSideSlidingLinkSIPt", [](Meca & mec, const Interpolation & pt1, const Mecapoint & pt2, real arm, real w)
            {mec.addSideSlidingLinkS(pt1,pt2,arm, w);})
#elif ( DIM >= 3 )
        .def("addSideLink3D", [](Meca & mec, const Interpolation & pt1, const Mecapoint & pt2, pyarray arm, real w)
            {mec.addSideLink3D(pt1,pt2,to_vector(arm), w);})
        .def("addSideLinkS", [](Meca & mec, const Interpolation & pt1, const Interpolation & pt2, pyarray arm, real l, real w)
            {mec.addSideLinkS(pt1,pt2,to_vector(arm), l,  w);})
        .def("addSideLinkS", [](Meca & mec, const Interpolation & pt1, const Mecapoint & pt2, pyarray arm, real l, real w)
            {mec.addSideLinkS(pt1,pt2,to_vector(arm), l,  w);})
        .def("addSideSlidingLinkS", [](Meca & mec, const Interpolation & pt1, const Interpolation & pt2, pyarray arm, real len, real w)
            {mec.addSideSlidingLinkS(pt1,pt2,to_vector(arm), len,w);})
        .def("addSideSlidingLinkS", [](Meca & mec, const Interpolation & pt1, const Mecapoint & pt2, pyarray arm, real len, real w)
            {mec.addSideSlidingLinkS(pt1,pt2,to_vector(arm), len,w);})
#endif
        .def("addSideLink", [](Meca & mec, const Interpolation & pt1, const Interpolation & pt2, real a, real w)
            {mec.addSideLink(pt1,pt2,a,w);})
        .def("addSideLink", [](Meca & mec, const Interpolation & pt1, const Mecapoint & pt2, real a, real w)
            {mec.addSideLink(pt1,pt2,a,w);})
        .def("addSideSideLink", [](Meca & mec, const Interpolation & pt1, const Interpolation & pt2, real a, real w)
            {mec.addSideSideLink(pt1,pt2,a,w);})
        .def("addSlidingLink", [](Meca & mec, const Interpolation & pt1, const Interpolation & pt2, real w)
            {mec.addSlidingLink(pt1,pt2,w);})
        .def("addSlidingLink", [](Meca & mec, const Interpolation & pt1, const Mecapoint & pt2, real w)
            {mec.addSlidingLink(pt1,pt2,w);})
        .def("addSideSlidingLink", [](Meca & mec, const Interpolation & pt1, const Interpolation & pt2, real len, real w)
            {mec.addSideSlidingLink(pt1,pt2, len,w);})
        .def("addSideSlidingLink", [](Meca & mec, const Interpolation & pt1, const Mecapoint & pt2, real len, real w)
            {mec.addSideSlidingLink(pt1,pt2, len,w);})
        .def("addTriLink", [](Meca & mec, const Interpolation & pt1, real w1, const Interpolation & pt2, real w2, const Interpolation & pt3, real w3)
            {mec.addTriLink(pt1,w1,pt2,w2,pt3,w3);});
}
