#include "Box"
#include <config.h>
#include <string.h>

using namespace Futile;

using namespace Atlab;

extern "C" {
void FC_FUNC_(bind_f90_box_iterator_copy_constructor, BIND_F90_BOX_ITERATOR_COPY_CONSTRUCTOR)(BoxIterator::f90_box_iterator_pointer*,
  const BoxIterator::f90_box_iterator*);
void FC_FUNC_(bind_f90_box_iterator_type_new, BIND_F90_BOX_ITERATOR_TYPE_NEW)(BoxIterator::f90_box_iterator_pointer*,
  const int*,
  const int*,
  const int*,
  const int*,
  const int*,
  const int*,
  const int*,
  const int*,
  Cell::f90_cell_pointer*,
  const int*,
  const double*,
  const double*,
  const double*,
  const int*,
  const double*,
  const int*);
void FC_FUNC_(bind_f90_box_iterator_free, BIND_F90_BOX_ITERATOR_FREE)(BoxIterator::f90_box_iterator_pointer*);
void FC_FUNC_(bind_f90_box_iterator_empty, BIND_F90_BOX_ITERATOR_EMPTY)(BoxIterator::f90_box_iterator_pointer*);
void FC_FUNC_(bind_f90_cell_copy_constructor, BIND_F90_CELL_COPY_CONSTRUCTOR)(Cell::f90_cell_pointer*,
  const Cell::f90_cell*);
void FC_FUNC_(bind_f90_cell_type_new, BIND_F90_CELL_TYPE_NEW)(Cell::f90_cell_pointer*,
  const Domain::f90_domain*,
  const double*,
  const double*,
  const size_t*,
  const int*,
  const double*);
void FC_FUNC_(bind_f90_cell_free, BIND_F90_CELL_FREE)(Cell::f90_cell_pointer*);
void FC_FUNC_(bind_cell_null, BIND_CELL_NULL)(Cell::f90_cell_pointer*);
void FC_FUNC_(bind_box_iter, BIND_BOX_ITER)(BoxIterator::f90_box_iterator_pointer*,
  const Cell::f90_cell*,
  const int*,
  const double*,
  const int*,
  const int*,
  const int*,
  const double*);
void FC_FUNC_(bind_box_nbox_from_cutoff, BIND_BOX_NBOX_FROM_CUTOFF)(const Cell::f90_cell*,
  int*,
  const double*,
  const double*,
  const int*);
void FC_FUNC_(bind_box_next_z, BIND_BOX_NEXT_Z)(int*,
  BoxIterator::f90_box_iterator*);
void FC_FUNC_(bind_box_next_y, BIND_BOX_NEXT_Y)(int*,
  BoxIterator::f90_box_iterator*);
void FC_FUNC_(bind_box_next_x, BIND_BOX_NEXT_X)(int*,
  BoxIterator::f90_box_iterator*);
void FC_FUNC_(bind_box_next_point, BIND_BOX_NEXT_POINT)(int*,
  BoxIterator::f90_box_iterator*);
void FC_FUNC_(bind_cell_new, BIND_CELL_NEW)(Cell::f90_cell_pointer*,
  const Domain::f90_domain*,
  const int*,
  const double*);
void FC_FUNC_(bind_cell_r, BIND_CELL_R)(double*,
  const Cell::f90_cell*,
  const int*,
  const int*);
void FC_FUNC_(bind_box_iter_square_gd, BIND_BOX_ITER_SQUARE_GD)(double*,
  const BoxIterator::f90_box_iterator*);
void FC_FUNC_(bind_box_iter_closest_r, BIND_BOX_ITER_CLOSEST_R)(const BoxIterator::f90_box_iterator*,
  double*,
  const double*,
  const int*);
void FC_FUNC_(bind_box_iter_distance, BIND_BOX_ITER_DISTANCE)(double*,
  const BoxIterator::f90_box_iterator*,
  const double*);
void FC_FUNC_(bind_nullify_box_iterator, BIND_NULLIFY_BOX_ITERATOR)(BoxIterator::f90_box_iterator*);
void FC_FUNC_(bind_box_iter_set_nbox, BIND_BOX_ITER_SET_NBOX)(BoxIterator::f90_box_iterator*,
  const int*,
  const double*,
  const double*);
void FC_FUNC_(bind_box_iter_expand_nbox, BIND_BOX_ITER_EXPAND_NBOX)(BoxIterator::f90_box_iterator*);
void FC_FUNC_(bind_box_iter_rewind, BIND_BOX_ITER_REWIND)(BoxIterator::f90_box_iterator*);
void FC_FUNC_(bind_box_iter_split, BIND_BOX_ITER_SPLIT)(BoxIterator::f90_box_iterator*,
  const int*,
  const int*);
void FC_FUNC_(bind_box_iter_merge, BIND_BOX_ITER_MERGE)(BoxIterator::f90_box_iterator*);
}

Cell::Cell(const Cell& other)
{
  FC_FUNC_(bind_f90_cell_copy_constructor, BIND_F90_CELL_COPY_CONSTRUCTOR)
    (*this, other);
}

Cell::Cell(const Domain& dom,
    const double habc[3][3],
    const double hgrids[3],
    size_t ndim,
    const int ndims[3],
    double volume_element)
{
  FC_FUNC_(bind_f90_cell_type_new, BIND_F90_CELL_TYPE_NEW)
    (*this, dom, habc[0], hgrids, &ndim, ndims, &volume_element);
}

Cell::~Cell(void)
{
  FC_FUNC_(bind_f90_cell_free, BIND_F90_CELL_FREE)
    (*this);
}

Cell::Cell(void)
{
  FC_FUNC_(bind_cell_null, BIND_CELL_NULL)
    (*this);
}

void Cell::box_nbox_from_cutoff(int out_nbox[2][3],
    const double oxyz[3],
    double cutoff,
    const bool (*inner)) const
{
  int inner_conv = inner ? *inner : 0;
  FC_FUNC_(bind_box_nbox_from_cutoff, BIND_BOX_NBOX_FROM_CUTOFF)
    (*this, out_nbox[0], oxyz, &cutoff,  inner ? &inner_conv : NULL);
}

Cell::Cell(const Domain& dom,
    const int ndims[3],
    const double hgrids[3])
{
  FC_FUNC_(bind_cell_new, BIND_CELL_NEW)
    (*this, dom, ndims, hgrids);
}

double Cell::r(int i,
    int dim) const
{
  double out_t;
  FC_FUNC_(bind_cell_r, BIND_CELL_R)
    (&out_t, *this, &i, &dim);
  return out_t;
}

BoxIterator::BoxIterator(const BoxIterator& other)
{
  FC_FUNC_(bind_f90_box_iterator_copy_constructor, BIND_F90_BOX_ITERATOR_COPY_CONSTRUCTOR)
    (*this, other);
}

BoxIterator::BoxIterator(int i,
    int j,
    int k,
    const int (*i23),
    const int (*i3e),
    const int (*i3s),
    const int (*ind),
    const int (*inext)[3],
    Cell (*mesh),
    const int (*nbox)[2][3],
    const double (*oxyz)[3],
    const double (*rxyz)[3],
    const double (*rxyz_nbox)[3],
    const int (*subbox)[2][3],
    const double (*tmp)[3],
    const bool (*whole))
{
  int whole_conv = whole ? *whole : 0;
  FC_FUNC_(bind_f90_box_iterator_type_new, BIND_F90_BOX_ITERATOR_TYPE_NEW)
    (*this, &i, &j, &k, i23, i3e, i3s, ind, inext ? *inext : NULL, mesh ? (Cell::f90_cell_pointer*)*mesh : NULL, nbox ? *nbox[0] : NULL, oxyz ? *oxyz : NULL, rxyz ? *rxyz : NULL, rxyz_nbox ? *rxyz_nbox : NULL, subbox ? *subbox[0] : NULL, tmp ? *tmp : NULL,  whole ? &whole_conv : NULL);
}

BoxIterator::~BoxIterator(void)
{
  FC_FUNC_(bind_f90_box_iterator_free, BIND_F90_BOX_ITERATOR_FREE)
    (*this);
}

BoxIterator::BoxIterator(void)
{
  FC_FUNC_(bind_f90_box_iterator_empty, BIND_F90_BOX_ITERATOR_EMPTY)
    (*this);
}

BoxIterator::BoxIterator(const Cell& mesh,
    const int (*nbox)[2][3],
    const double (*origin)[3],
    const int (*i3s),
    const int (*n3p),
    const bool (*centered),
    const double (*cutoff))
{
  int centered_conv = centered ? *centered : 0;
  FC_FUNC_(bind_box_iter, BIND_BOX_ITER)
    (*this, mesh, nbox ? *nbox[0] : NULL, origin ? *origin : NULL, i3s, n3p,  centered ? &centered_conv : NULL, cutoff);
}

bool BoxIterator::box_next_z(void)
{
  int out_ok;
  FC_FUNC_(bind_box_next_z, BIND_BOX_NEXT_Z)
    (&out_ok, *this);
  return out_ok;
}

bool BoxIterator::box_next_y(void)
{
  int out_ok;
  FC_FUNC_(bind_box_next_y, BIND_BOX_NEXT_Y)
    (&out_ok, *this);
  return out_ok;
}

bool BoxIterator::box_next_x(void)
{
  int out_ok;
  FC_FUNC_(bind_box_next_x, BIND_BOX_NEXT_X)
    (&out_ok, *this);
  return out_ok;
}

bool BoxIterator::box_next_point(void)
{
  int out_box_next_point;
  FC_FUNC_(bind_box_next_point, BIND_BOX_NEXT_POINT)
    (&out_box_next_point, *this);
  return out_box_next_point;
}

double BoxIterator::box_iter_square_gd(void) const
{
  double out_box_iter_square_gd;
  FC_FUNC_(bind_box_iter_square_gd, BIND_BOX_ITER_SQUARE_GD)
    (&out_box_iter_square_gd, *this);
  return out_box_iter_square_gd;
}

void BoxIterator::box_iter_closest_r(double out_r[3],
    const double rxyz[3],
    const bool (*orthorhombic)) const
{
  int orthorhombic_conv = orthorhombic ? *orthorhombic : 0;
  FC_FUNC_(bind_box_iter_closest_r, BIND_BOX_ITER_CLOSEST_R)
    (*this, out_r, rxyz,  orthorhombic ? &orthorhombic_conv : NULL);
}

double BoxIterator::box_iter_distance(const double rxyz0[3]) const
{
  double out_box_iter_distance;
  FC_FUNC_(bind_box_iter_distance, BIND_BOX_ITER_DISTANCE)
    (&out_box_iter_distance, *this, rxyz0);
  return out_box_iter_distance;
}

void BoxIterator::nullify_box_iterator(void)
{
  FC_FUNC_(bind_nullify_box_iterator, BIND_NULLIFY_BOX_ITERATOR)
    (*this);
}

void BoxIterator::box_iter_set_nbox(const int (*nbox)[2][3],
    const double (*oxyz)[3],
    const double (*cutoff))
{
  FC_FUNC_(bind_box_iter_set_nbox, BIND_BOX_ITER_SET_NBOX)
    (*this, nbox ? *nbox[0] : NULL, oxyz ? *oxyz : NULL, cutoff);
}

void BoxIterator::box_iter_expand_nbox(void)
{
  FC_FUNC_(bind_box_iter_expand_nbox, BIND_BOX_ITER_EXPAND_NBOX)
    (*this);
}

void BoxIterator::box_iter_rewind(void)
{
  FC_FUNC_(bind_box_iter_rewind, BIND_BOX_ITER_REWIND)
    (*this);
}

void BoxIterator::box_iter_split(int ntasks,
    int itask)
{
  FC_FUNC_(bind_box_iter_split, BIND_BOX_ITER_SPLIT)
    (*this, &ntasks, &itask);
}

void BoxIterator::box_iter_merge(void)
{
  FC_FUNC_(bind_box_iter_merge, BIND_BOX_ITER_MERGE)
    (*this);
}

