/////////////////////////////////////////////////////////////
//                                                         //
// Copyright (c) 2007-2017 by The University of Queensland //
// Centre for Geoscience Computing                         //
// http://earth.uq.edu.au/centre-geoscience-computing      //
//                                                         //
// Primary Business: Brisbane, Queensland, Australia       //
// Licensed under the Open Software License version 3.0    //
// http://www.apache.org/licenses/LICENSE-2.0              //
//                                                         //
/////////////////////////////////////////////////////////////

#include <boost/version.hpp>
#include <iostream>
#include <fstream>
#include <sstream>
#include "MNTable3DPy.h"

using namespace std;

using namespace boost::python;

    using boost::python::arg;
    void exportMNTable3D()
    {
      // Disable autogeneration of C++ signatures (Boost 1.34.0 and higher)
      // for Epydoc which stumbles over indentation in the automatically generated strings.
      boost::python::docstring_options no_autogen(true,false);

      class_<MNTable3D>(
        "MNTable3D",
        "A multi-group neighbour table for constructing 3D particle assemblies",
        init<>()
      )
      .def(init<const MNTable3D &>())
      .def(
        init<Vector3&,Vector3&,double, unsigned int>(
          ( boost::python::arg("minPoint"), boost::python::arg("maxPoint"), boost::python::arg("gridSize"), boost::python::arg("numGroups")=1 ),
          "Constructs a neighbour table with specified bounds, cell size and initial number of particle groups.\n"
          "@type minPoint: L{Vector3}\n"
          "@kwarg minPoint: front-lower-left point of the particle region\n"
          "@type maxPoint: L{Vector3}\n"
          "@kwarg maxPoint: back-upper-right point of the particle region\n"
          "@type gridSize: double\n"
          "@kwarg gridSize: the cell size for neighbour searches\n"
          "@type numGroups: unsigned int\n"
          "@kwarg numGroups: the initial number of groups (default: 1)\n"
        )
      )
      .def(
        "insertFromRawFile",
        &MNTable3D::insertFromRawFile,
        ( boost::python::arg("fileName"), boost::python::arg("scale"), boost::python::arg("tolerance") ),
        "Reads in an existing geometry from a file in a raw ASCII format\n"
        "@type fileName: string\n"
        "@kwarg fileName: the name of the raw ASCII geometry file to read\n"
        "@type scale: double\n"
        "@kwarg scale: a scaling factor to apply to coordinates and radius\n"
        "@type tolerance: double\n"
        "@kwarg tolerance: the tolerance for determining whether particles collide\n"
      )
      .def(
        "tagParticlesInSphere",
        &MNTable3D::tagParticlesInSphere,
        ( boost::python::arg("sphere"), boost::python::arg("tag"), boost::python::arg("groupID")=0 ),
        "Assigns the specified tag to all particles from group C{groupID} "
        "that lie within the given L{Sphere}.\n"
        "@type sphere: L{Sphere}\n"
        "@kwarg sphere: the sphere within which to tag particles\n"
        "@type tag: int\n"
        "@kwarg tag: the tag to assign particles\n"
        "@type groupID: unsigned int\n"
        "@kwarg groupID: the group ID of particles to tag (default: 0)\n"
        "@rtype: void\n"
      )
      .def(
        "tagParticlesAlongPlane",
        &MNTable3D::tagParticlesAlongPlane,
        ( boost::python::arg("plane"), boost::python::arg("distance"), boost::python::arg("tag"), boost::python::arg("groupID")=0 ),
        "Assigns the specified tag to all particles from group C{groupID} "
        "that lie within the specified distance of the given L{Plane}.\n"
        "@type plane: L{Plane}\n"
        "@kwarg plane: the plane along which to tag particles\n"
        "@type distance: double\n"
        "@kwarg distance: the maximum distance between tagged particles and the plane\n"
        "@type tag: int\n"
        "@kwarg tag: the tag to assign particles\n"
        "@type groupID: unsigned int\n"
        "@kwarg groupID: the group ID of particles to tag (default: 0)\n"
        "@rtype: void\n"
      )
      .def(
        "tagParticlesInVolume",
        &MNTable3D::tagParticlesInVolume,
        ( boost::python::arg("volume"), boost::python::arg("tag"), boost::python::arg("groupID")=0 ),
        "Assigns the specified tag to all particles within the specified L{AVolume3D} from group C{groupID}\n"
        "that lie within the specified volume.\n"
        "@type volume: L{AVolume3D}\n"
        "@kwarg volume: the volume within which to tag particles\n"
        "@type tag: int\n"
        "@kwarg tag: the tag to assign particles\n"
        "@type groupID: unsigned int\n"
        "@kwarg groupID: the group ID of particles to tag (default: 0)\n"
        "@rtype: void\n"
      )
      .def(
        "tagParticlesAlongPlaneWithMask",
        &MNTable3D::tagParticlesAlongPlaneWithMask,
        ( boost::python::arg("plane"), boost::python::arg("distance"), boost::python::arg("tag"),boost::python::arg("mask"), boost::python::arg("groupID")=0 ),
        "Assigns the specified tag to all particles from group C{groupID} "
        "that lie within the specified distance of a given L{Plane}."
	" Use of tag masks is permitted.\n"
        "@type plane: L{Plane}\n"
        "@kwarg plane: the plane along which to tag particles\n"
        "@type distance: double\n"
        "@kwarg distance: the maximum distance between tagged particles and the plane\n"
        "@type tag: int\n"
        "@kwarg tag: the tag to assign particles\n"
        "@type mask: int\n"
        "@kwarg mask: the mask determining which bits of the tag are used\n"
        "@type groupID: unsigned int\n"
        "@kwarg groupID: the group ID of particles to tag (default: 0)\n"
        "@rtype: void\n"
      )
     .def(
        "tagParticlesAlongJoints",
        &MNTable3D::tagParticlesAlongJoints,
        ( boost::python::arg("jointset"), boost::python::arg("distance"), boost::python::arg("tag"),boost::python::arg("mask"), boost::python::arg("groupID")=0 ),
        "Assigns the specified tag to all particles from group C{groupID} "
        "that lie within the specified distance of a given L{TriPatchSet}\n"
        "@type joints: L{TriPatchSet}\n"
        "@kwarg joints: the set of triangular patches along which to tag particles\n"
        "@type distance: double\n"
        "@kwarg distance: the maximum distance between tagged particles and the triangles in the L{TriPatchSet}\n"
        "@type tag: int\n"
        "@kwarg tag: the tag to assign particles\n"
        "@type mask: int\n"
        "@kwarg mask: the mask determining which bits of the tag are used\n"
        "@type groupID: unsigned int\n"
        "@kwarg groupID: the group ID of particles to tag (default: 0)\n"
        "@rtype: void\n"
      )
      .def(
        "removeParticlesWithTag",
        &MNTable3D::removeParticlesWithTag,
        ( boost::python::arg("tag"), boost::python::arg("groupID")=0 ),
        "Removes all particles with the specified tag from a given group\n"
        "@type tag: int\n"
        "@kwarg tag: the tag of particles to remove\n"
        "@type groupID: unsigned int\n"
        "@kwarg groupID: the group ID of particles to remove (default: 0)\n"
        "@rtype: void\n"
      )
      .def(
        "removeTaggedParticles",
        &MNTable3D::removeParticlesWithTagMask,
        (boost::python::arg("groupID")=0, boost::python::arg("tag"), boost::python::arg("mask")),
        "Removes all particles with a specified tag from a given group\n"
        "@type groupID: unsigned int\n"
        "@kwarg groupID: the group ID of particles to remove (default: 0)\n"
        "@type tag: int\n"
        "@kwarg tag: the tag of particles to remove\n"
        "@type mask: int\n"
        "@kwarg mask: the mask determining which bits of the tag are used\n"
        "@rtype: void\n"
      )
      .def(
	   "removeParticlesInVolume",
	   &MNTable3D::removeParticlesInVolume,
	   (boost::python::arg("volume"), boost::python::arg("groupID")=0, boost::python::arg("full")),
	   "Removes all particles inside the specified L{AVolume3D}\n"
           "@type volume: L{AVolume3D}\n"
           "@kwarg volume: the volume within which to remove particles\n"
           "@type groupID: unsigned int\n"
           "@kwarg groupID: the group ID of particles to remove (default: 0)\n"
           "@type full: bool\n"
           "@kwarg full: remove particles fully inside volume or only with centres inside the volume\n"
      )
      .def(
        "removeParticlesInGroup",
        &MNTable3D::removeParticlesInGroup,
        ( boost::python::arg("groupID") ),
        "Removes all particles from a given group\n"
        "@type groupID: unsigned int\n"
        "@kwarg groupID: the group ID of particles to remove \n"
        "@rtype: void\n"
      )
      .def(
	   "renumberParticlesContinuous",
	   &MNTable3D::renumberParticlesContinuous,
	   "Changes all particle IDs so that they are continuous "
	   "i.e. 0,1,..,Np-1\n"
      )
      .def(
        "generateBonds",
        &MNTable3D::generateBonds,
        (boost::python::arg("groupID")=0, boost::python::arg("tolerance"), boost::python::arg("bondID") ),
        "Generates bonds between particle pairs separated by less than the specified tolerance\n"
        "@type groupID: int\n"
        "@kwarg groupID: the group ID of particles to bond together (default: 0)\n"
        "@type tolerance: double\n"
        "@kwarg tolerance: maximum distance separating bonded particles\n"
        "@type bondID: int\n"
        "@kwarg bondID: the bond ID to assign generated bonds\n"
        "@rtype: void\n"
      )
      .def(
        "generateBondsTagged",
        &MNTable3D::generateBondsTagged,
        (boost::python::arg("groupID")=0, boost::python::arg("tolerance"), boost::python::arg("bondID"), boost::python::arg("particleTag1"), boost::python::arg("particleTag2") ),
        "Generates bonds between particle pairs with the two specified tags.\n"
        "@type groupID: int\n"
        "@kwarg groupID: the group ID of particles to bond together (default: 0)\n"
        "@type tolerance: double\n"
        "@kwarg tolerance: maximum distance separating bonded particles\n"
        "@type bondID: int\n"
        "@kwarg bondID: the bond ID to assign generated bonds\n"
        "@type particleTag1: int\n"
        "@kwarg particleTag1: the first tag of particles to bond\n"
        "@type particleTag2: int\n"
        "@kwarg particleTag2: the second tag of particles to bond\n"
        "@rtype: void\n"
      )
      .def(
        "generateRandomBonds",
        &MNTable3D::generateRandomBonds,
        (boost::python::arg("groupID")=0, boost::python::arg("tolerance"), boost::python::arg("probability"), boost::python::arg("bondID"), boost::python::arg("particleTag"), boost::python::arg("tagMask") ),
        "Generates bonds between particle pairs. Bonds will be created with a specified probability <= 1\n"
        "@type groupID: int\n"
        "@kwarg groupID: the group ID of particles to bond together (default: 0)\n"
        "@type tolerance: double\n"
        "@kwarg tolerance: maximum distance separating bonded particles\n"
        "@type probability: double\n"
        "@kwarg probability: the probability that a bond will be generated (0 < probability <= 1)\n"
        "@type bondID: int\n"
        "@kwarg bondID: the bond ID to assign generated bonds\n"
        "@type particleTag: int\n"
        "@kwarg particleTag: the tag of particles to bond\n"
        "@type tagMask: int\n"
        "@kwarg tagMask: the mask determining which bits of the tag are used\n"
        "@rtype: void\n"
      )
      .def(
        "generateBondsWithJointSet",
        &MNTable3D::generateBondsWithJointSet,
        (boost::python::arg("JointSet"), boost::python::arg("groupID")=0, boost::python::arg("tolerance"), boost::python::arg("bondTag") ),
        "Generates bonds between particle pairs, checking whether the bond crosses a specified L{TriPatchSet} representing joints.\n"
        "A bond that crosses a triangle will be assigned a bondTag equal to the tag of the triangle.\n"
        "A bond that does not cross any triangle will be assigned a bondTag equal to the supplied bondTag.\n"
	"@type JointSet: L{TriPatchSet}\n"
	"@kwarg JointSet: Set of triangles to check for crossing bonds\n"
        "@type groupID: int\n"
        "@kwarg groupID: the group ID of particles to bond together (default: 0)\n"
        "@type tolerance: double\n"
        "@kwarg tolerance: maximum distance separating bonded particles\n"
        "@type bondTag: int\n"
        "@kwarg bondTag: the bond tag to assign to bonds that do not cross a joint (DO NOT set this equal to the tag of any triangle contained in the joint set).\n"
        "@rtype: void\n"
      )
      .def(
        "generateClusterBonds",
        &MNTable3D::generateClusterBonds,
        (boost::python::arg("groupID")=0, boost::python::arg("tolerance"), boost::python::arg("bondTag1"), boost::python::arg("bondTag2")),
        "Generates bonds with different tags depending on whether the particle tags are the same or different.\n"
        "@type groupID: int\n"
        "@kwarg groupID: the group ID of particles to bond together (default: 0)\n"
        "@type tolerance: double\n"
        "@kwarg tolerance: maximum distance separating bonded particles\n"
        "@type bondTag1: int\n"
        "@kwarg bondTag1: the bond tag to assign for bonds joining particles of the same tag\n"
        "@type bondTag2: int\n"
        "@kwarg bondTag2: the bond tag to assign for bonds joining particles of differing tag\n"
      )
      .def(
        "getSumVolume",
        &MNTable3D::getSumVolume,
        ( boost::python::arg("groupID")=0 ),
        "Returns the sum of the particle volumes in the specified group.\n"
        "@type groupID: int\n"
        "@kwarg groupID: the group ID of particles whose volumes are summed (default: 0).\n"
        "@rtype: double\n"
      )
      .def(
        "getSphereListFromGroup",
        &MNTable3D::getSphereListFromGroup,
        ( boost::python::arg("groupID")=0 ),
        "Returns a python list of L{Sphere} objects with the specified group ID\n"
        "@type groupID: int\n"
        "@kwarg groupID: the group ID of spheres to return as a list (default: 0)\n"
        "@rtype: boost::python::list\n"
      )
      .def(
        "getSphereListDist",
        &MNTable3D::getSphereListDist,
        ( boost::python::arg("posn"),boost::python::arg("dist"),boost::python::arg("groupID")=0 ),
        "Returns a python list of L{Sphere} objects within a defined distance \n"
        "of the specified point\n"
        "@type posn: L{Vector3}\n"
        "@kwarg posn: the location of the point\n"
        "@type dist: double\n"
        "@kwarg dist: the distance from the point to particles that will be returned\n"
        "@type groupID: int\n"
        "@kwarg groupID: the group ID of spheres to return as a list (default: 0)\n"
        "@rtype: boost::python::list\n"
      )
      .def(
        "GrowNGroups",
        &MNTable3D::GrowNGroups,
        ( boost::python::arg("numGroups") ),
        "Expands the neighbour table to permit the specified number of particle groups\n"
        "@type numGroups: int\n"
        "@kwarg numGroups: number of groups to create\n"
        "@rtype: void\n"
      )
      .def(
        "write",
        &MNTable3D::write,
        ( boost::python::arg("fileName"), boost::python::arg("outputStyle") ),
        "Writes the particle assembly and bonding information to the specified\n"
        "file using the specified output style (0: debug; 1: geo; 2: vtk)\n"
        "@type fileName: string\n"
        "@kwarg fileName: the name of the file to write\n"
        "@type outputStyle: int\n"
        "@kwarg outputStyle: output style (0: debug; 1: geo; 2: vtk)\n"
        "@rtype: void\n"
      )
      .def(
        "tagParticlesInGroup",
        &MNTable3D::tagParticlesInGroupNT,
        (boost::python::arg("groupID")=0, boost::python::arg("tag") ),
        "Tag all particles with the specified C{groupID}\n"
        "@type groupID: int\n"
        "@kwarg groupID: the group ID of particles to tag (default: 0)\n"
        "@type tag: int\n"
        "@kwarg tag: the tag to be assigned to the selected particles\n"
      )
      .def(
        "tagParticlesInGroup",
        &MNTable3D::tagParticlesInGroup,
        (boost::python::arg("groupID")=0, boost::python::arg("tag"), boost::python::arg("mask") ),
        "@type mask: int\n"
        "@kwarg mask: only the bits which are 1 in the mask are actually\n"
        " changed in the tag (optional)\n"
        "@rtype: void\n"
      )
      .def(
        "tagParticlesToClosest",
        &MNTable3D::tagParticlesToClosest,
        ( boost::python::arg("groupID1")=0, boost::python::arg("groupID2") ),
        "Tags particles in C{groupID1} closest to spheres in C{groupID2}.\n"
        "The particle ID of C{groupID2} specifies the tag assigned to nearest particles in C{groupID1}.\n"
        "@type groupID1: int\n"
        "@kwarg groupID1: the group ID of particles to tag (default: 0).\n"
        "@type groupID2: int\n"
        "@kwarg groupID2: the group ID of closest spheres.\n"
      )
      .def(
        "tagParticlesToClosestAnisotropic",
        &MNTable3D::tagParticlesToClosestAnisotropic,
        ( boost::python::arg("groupID1")=0, boost::python::arg("groupID2"), boost::python::arg("wx"), boost::python::arg("wy"), boost::python::arg("wz") ),
        "Tags particles in C{groupID1} closest to spheres in C{groupID2} using distance weighting factors for aniostropic particle cluster shapes.\n"
        "The particle ID of C{groupID2} specifies the tag assigned to nearest particles in C{groupID1}.\n"
        "@type groupID1: int\n"
        "@kwarg groupID1: the group ID of particles to tag (default: 0).\n"
        "@type groupID2: int\n"
        "@kwarg groupID2: the group ID of closest spheres.\n"
        "@type wx: double\n"
        "@kwarg wx: the weighting of the x-component of the distance.\n"
        "@type wy: double\n"
        "@kwarg wy: the weighting of the y-component of the distance.\n"
        "@type wz: double\n"
        "@kwarg wz: the weighting of the z-component of the distance.\n"
      )
      .def(
        "insert",
        &MNTable3D::insert,
        ( boost::python::arg("sphere"), boost::python::arg("groupID")=0 ),
        "Inserts sphere\n"
        "@type sphere: L{Sphere}\n"
        "@kwarg sphere: the sphere to insert\n"
        "@type groupID: int\n"
        "@kwarg groupID: the group ID of the inserted sphere (default: 0).\n"
      )
      .def(
        "insertBond",
        &MNTable3D::insertBond,
        ( boost::python::arg("Id1"), boost::python::arg("Id2"), boost::python::arg("tag") ),
        "Inserts bond between particles with the specified particle IDs\n"
        "@type Id1: int\n"
        "@kwarg Id1: ID of first particle to bond\n"
        "@type Id2: int\n"
        "@kwarg Id2: ID of second particle to bond\n"
        "@type tag: int\n"
        "@kwarg tag: the bond tag to assign to the generated bond\n"
      )
      .def(
        "setOutputPrecision",
        &MNTable3D::SetOutputPrecision,
        ( boost::python::arg("precision")),
        "Set the number of significant digits for file output\n"
        "@type precision: int\n"
        "@kwarg precision: the number of significant digits (default: 10).\n"
      )
      .def(
        "initBlockWriting",
        &MNTable3D::initBlockWriting,
        ( boost::python::arg("filename")),
        "Initialize data needed for blocked writing\n"
        "@type filename: string\n"
        "@kwarg filename: The name of the geo file to be written.\n"
      )	
      .def(
        "writeBlock",
        &MNTable3D::writeBlock,
        ( boost::python::arg("minPoint"), boost::python::arg("maxPoint")),
        "Write particles in block to already initialized file\n"
        "@type minPoint: Vector3\n"
        "@kwarg minPoint: The minimum point of the block.\n"
	"@type maxPoint: Vector3\n"
        "@kwarg maxPoint: The maximum point of the block.\n"
      )	
      .def(
        "writeBondsBlocked",
        &MNTable3D::writeBondsBlocked,
        "Writing the current bonds to a temporary file.\n"
      )
      .def(
        "removeBonds",
        &MNTable3D::removeBonds,
        "Delete all currently stored bonds.\n"
      )
      .def(
        "finishBlockWriting",
        &MNTable3D::finishBlockWriting,
        "Finish block writing.\n"
      )
      .def(self_ns::str(self))
      ;
    }
