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

using namespace Futile;

using namespace Atlab;

extern "C" {
void FC_FUNC_(bind_f90_domain_copy_constructor, BIND_F90_DOMAIN_COPY_CONSTRUCTOR)(Domain::f90_domain_pointer*,
  const Domain::f90_domain*);
void FC_FUNC_(bind_f90_domain_type_new, BIND_F90_DOMAIN_TYPE_NEW)(Domain::f90_domain_pointer*,
  const double*,
  const double*,
  const double*,
  const int*,
  const double*,
  const double*,
  const double*,
  const int*,
  const double*,
  const int*);
void FC_FUNC_(bind_f90_domain_free, BIND_F90_DOMAIN_FREE)(Domain::f90_domain_pointer*);
void FC_FUNC_(bind_domain_null, BIND_DOMAIN_NULL)(Domain::f90_domain_pointer*);
void FC_FUNC_(bind_domain_new, BIND_DOMAIN_NEW)(Domain::f90_domain_pointer*,
  const FEnumerator::f90_f_enumerator*,
  const FEnumerator::f90_f_enumerator*,
  const FEnumerator::f90_f_enumerator*,
  const FEnumerator::f90_f_enumerator*,
  const double*,
  const double*,
  const double*,
  const double*,
  const double*);
void FC_FUNC_(bind_change_domain_bc, BIND_CHANGE_DOMAIN_BC)(Domain::f90_domain_pointer*,
  const Domain::f90_domain*,
  const char*,
  size_t);
void FC_FUNC_(bind_units_enum_from_str, BIND_UNITS_ENUM_FROM_STR)(FEnumerator::f90_f_enumerator_pointer*,
  const char*,
  const size_t*,
  size_t);
void FC_FUNC_(bind_geocode_to_bc, BIND_GEOCODE_TO_BC)(int*,
  const char*,
  size_t);
void FC_FUNC_(bind_domain_geocode, BIND_DOMAIN_GEOCODE)(char*,
  const Domain::f90_domain*);
void FC_FUNC_(bind_domain_volume, BIND_DOMAIN_VOLUME)(double*,
  const double*,
  const Domain::f90_domain*);
void FC_FUNC_(bind_rxyz_ortho, BIND_RXYZ_ORTHO)(const Domain::f90_domain*,
  double*,
  const double*);
void FC_FUNC(bind_distance, BIND_DISTANCE)(double*,
  const Domain::f90_domain*,
  const double*,
  const double*);
void FC_FUNC_(bind_closest_r, BIND_CLOSEST_R)(const Domain::f90_domain*,
  double*,
  const double*,
  const double*);
void FC_FUNC_(bind_domain_set_from_dict, BIND_DOMAIN_SET_FROM_DICT)(f90_dictionary_pointer*,
  Domain::f90_domain*);
void FC_FUNC_(bind_domain_merge_to_dict, BIND_DOMAIN_MERGE_TO_DICT)(f90_dictionary_pointer*,
  const Domain::f90_domain*);
void FC_FUNC_(bind_dotp_gu, BIND_DOTP_GU)(double*,
  const Domain::f90_domain*,
  const double*,
  const double*);
void FC_FUNC_(bind_dotp_gu_add2, BIND_DOTP_GU_ADD2)(double*,
  const Domain::f90_domain*,
  const double*,
  double*);
void FC_FUNC_(bind_dotp_gu_add1, BIND_DOTP_GU_ADD1)(double*,
  const Domain::f90_domain*,
  double*,
  const double*);
void FC_FUNC(bind_square, BIND_SQUARE)(double*,
  const Domain::f90_domain*,
  const double*);
void FC_FUNC_(bind_square_add, BIND_SQUARE_ADD)(double*,
  const Domain::f90_domain*,
  double*);
void FC_FUNC_(bind_dotp_gd, BIND_DOTP_GD)(double*,
  const Domain::f90_domain*,
  const double*,
  const double*);
void FC_FUNC_(bind_dotp_gd_add2, BIND_DOTP_GD_ADD2)(double*,
  const Domain::f90_domain*,
  const double*,
  double*);
void FC_FUNC_(bind_dotp_gd_add1, BIND_DOTP_GD_ADD1)(double*,
  const Domain::f90_domain*,
  double*,
  const double*);
void FC_FUNC_(bind_dotp_gd_add12, BIND_DOTP_GD_ADD12)(double*,
  const Domain::f90_domain*,
  double*,
  double*);
void FC_FUNC_(bind_square_gd, BIND_SQUARE_GD)(double*,
  const Domain::f90_domain*,
  const double*);
void FC_FUNC_(bind_square_gd_add, BIND_SQUARE_GD_ADD)(double*,
  const Domain::f90_domain*,
  double*);
void FC_FUNC_(bind_angstroem_units, BIND_ANGSTROEM_UNITS)(FEnumerator::f90_f_enumerator_pointer*);
void FC_FUNC_(bind_periodic_bc, BIND_PERIODIC_BC)(FEnumerator::f90_f_enumerator_pointer*);
void FC_FUNC_(bind_free_bc, BIND_FREE_BC)(FEnumerator::f90_f_enumerator_pointer*);
void FC_FUNC_(bind_atomic_units, BIND_ATOMIC_UNITS)(FEnumerator::f90_f_enumerator_pointer*);
void FC_FUNC_(bind_double_precision_enum, BIND_DOUBLE_PRECISION_ENUM)(FEnumerator::f90_f_enumerator_pointer*);
void FC_FUNC_(bind_nanometer_units, BIND_NANOMETER_UNITS)(FEnumerator::f90_f_enumerator_pointer*);
}

Futile::FEnumerator Atlab::units_enum_from_str(const char* str)
{
  FEnumerator::f90_f_enumerator_pointer out_units;
  size_t str_chk_len, str_len = str_chk_len = str ? strlen(str) : 0;
  FC_FUNC_(bind_units_enum_from_str, BIND_UNITS_ENUM_FROM_STR)
    (&out_units, str, &str_len, str_chk_len);
  return FEnumerator(out_units);
}

void Atlab::geocode_to_bc(int out_bc[3],
    char geocode)
{
  size_t geocode_chk_len = 1;
  FC_FUNC_(bind_geocode_to_bc, BIND_GEOCODE_TO_BC)
    (out_bc, &geocode, geocode_chk_len);
}

double Atlab::domain_volume(const double acell[3],
    const Domain& dom)
{
  double out_cell_volume;
  FC_FUNC_(bind_domain_volume, BIND_DOMAIN_VOLUME)
    (&out_cell_volume, acell, dom);
  return out_cell_volume;
}

void Atlab::domain_set_from_dict(Futile::Dictionary& dict,
    Domain& dom)
{
  FC_FUNC_(bind_domain_set_from_dict, BIND_DOMAIN_SET_FROM_DICT)
    (dict, dom);
}

void Atlab::domain_merge_to_dict(Futile::Dictionary& dict,
    const Domain& dom)
{
  FC_FUNC_(bind_domain_merge_to_dict, BIND_DOMAIN_MERGE_TO_DICT)
    (dict, dom);
}

Futile::FEnumerator Atlab::angstroem_units(void)
{
  FEnumerator::f90_f_enumerator_pointer out_self;
  FC_FUNC_(bind_angstroem_units, BIND_ANGSTROEM_UNITS)
    (&out_self);
  return FEnumerator(out_self);
}

Futile::FEnumerator Atlab::periodic_bc(void)
{
  FEnumerator::f90_f_enumerator_pointer out_self;
  FC_FUNC_(bind_periodic_bc, BIND_PERIODIC_BC)
    (&out_self);
  return FEnumerator(out_self);
}

Futile::FEnumerator Atlab::free_bc(void)
{
  FEnumerator::f90_f_enumerator_pointer out_self;
  FC_FUNC_(bind_free_bc, BIND_FREE_BC)
    (&out_self);
  return FEnumerator(out_self);
}

Futile::FEnumerator Atlab::atomic_units(void)
{
  FEnumerator::f90_f_enumerator_pointer out_self;
  FC_FUNC_(bind_atomic_units, BIND_ATOMIC_UNITS)
    (&out_self);
  return FEnumerator(out_self);
}

Futile::FEnumerator Atlab::double_precision_enum(void)
{
  FEnumerator::f90_f_enumerator_pointer out_self;
  FC_FUNC_(bind_double_precision_enum, BIND_DOUBLE_PRECISION_ENUM)
    (&out_self);
  return FEnumerator(out_self);
}

Futile::FEnumerator Atlab::nanometer_units(void)
{
  FEnumerator::f90_f_enumerator_pointer out_self;
  FC_FUNC_(bind_nanometer_units, BIND_NANOMETER_UNITS)
    (&out_self);
  return FEnumerator(out_self);
}

Domain::Domain(const Domain& other)
{
  FC_FUNC_(bind_f90_domain_copy_constructor, BIND_F90_DOMAIN_COPY_CONSTRUCTOR)
    (*this, other);
}

Domain::Domain(const double abc[3][3],
    const double acell[3],
    const double angrad[3],
    const int bc[3],
    double detgd,
    const double gd[3][3],
    const double gu[3][3],
    bool orthorhombic,
    const double uabc[3][3],
    int units)
{
  int orthorhombic_conv = orthorhombic;
  FC_FUNC_(bind_f90_domain_type_new, BIND_F90_DOMAIN_TYPE_NEW)
    (*this, abc[0], acell, angrad, bc, &detgd, gd[0], gu[0], &orthorhombic_conv, uabc[0], &units);
}

Domain::~Domain(void)
{
  FC_FUNC_(bind_f90_domain_free, BIND_F90_DOMAIN_FREE)
    (*this);
}

Domain::Domain(void)
{
  FC_FUNC_(bind_domain_null, BIND_DOMAIN_NULL)
    (*this);
}

Domain::Domain(const Futile::FEnumerator& units,
    const Futile::FEnumerator bc[3],
    const double (*abc)[3][3],
    const double (*alpha_bc),
    const double (*beta_ac),
    const double (*gamma_ab),
    const double (*acell)[3])
{
  FC_FUNC_(bind_domain_new, BIND_DOMAIN_NEW)
    (*this, units, bc[0], bc[1], bc[2], abc ? *abc[0] : NULL, alpha_bc, beta_ac, gamma_ab, acell ? *acell : NULL);
}

Domain::Domain(const Domain& dom_in,
    char geocode)
{
  size_t geocode_chk_len = 1;
  FC_FUNC_(bind_change_domain_bc, BIND_CHANGE_DOMAIN_BC)
    (*this, dom_in, &geocode, geocode_chk_len);
}

char Domain::geocode(void) const
{
  char out_dom_geocode;
  FC_FUNC_(bind_domain_geocode, BIND_DOMAIN_GEOCODE)
    (&out_dom_geocode, *this);
  return out_dom_geocode;
}

void Domain::rxyz_ortho(double out_rxyz_ortho[3],
    const double rxyz[3]) const
{
  FC_FUNC_(bind_rxyz_ortho, BIND_RXYZ_ORTHO)
    (*this, out_rxyz_ortho, rxyz);
}

double Domain::distance(const double r[3],
    const double c[3]) const
{
  double out_d;
  FC_FUNC(bind_distance, BIND_DISTANCE)
    (&out_d, *this, r, c);
  return out_d;
}

void Domain::closest_r(double out_r[3],
    const double v[3],
    const double center[3]) const
{
  FC_FUNC_(bind_closest_r, BIND_CLOSEST_R)
    (*this, out_r, v, center);
}

double Domain::dotp_gu(const double v1[3],
    const double v2[3]) const
{
  double out_dotp_gu;
  FC_FUNC_(bind_dotp_gu, BIND_DOTP_GU)
    (&out_dotp_gu, *this, v1, v2);
  return out_dotp_gu;
}

double Domain::dotp_gu(const double v1[3],
    double* v2_add) const
{
  double out_dotp;
  FC_FUNC_(bind_dotp_gu_add2, BIND_DOTP_GU_ADD2)
    (&out_dotp, *this, v1, v2_add);
  return out_dotp;
}

double Domain::dotp_gu(double* v1_add,
    const double v2[3]) const
{
  double out_dotp;
  FC_FUNC_(bind_dotp_gu_add1, BIND_DOTP_GU_ADD1)
    (&out_dotp, *this, v1_add, v2);
  return out_dotp;
}

double Domain::square_gu(const double v[3]) const
{
  double out_square;
  FC_FUNC(bind_square, BIND_SQUARE)
    (&out_square, *this, v);
  return out_square;
}

double Domain::square_gu(double* v_add) const
{
  double out_square;
  FC_FUNC_(bind_square_add, BIND_SQUARE_ADD)
    (&out_square, *this, v_add);
  return out_square;
}

double Domain::dotp_gd(const double v1[3],
    const double v2[3]) const
{
  double out_dotp_gd;
  FC_FUNC_(bind_dotp_gd, BIND_DOTP_GD)
    (&out_dotp_gd, *this, v1, v2);
  return out_dotp_gd;
}

double Domain::dotp_gd(const double v1[3],
    double* v2_add) const
{
  double out_dotp;
  FC_FUNC_(bind_dotp_gd_add2, BIND_DOTP_GD_ADD2)
    (&out_dotp, *this, v1, v2_add);
  return out_dotp;
}

double Domain::dotp_gd(double* v1_add,
    const double v2[3]) const
{
  double out_dotp;
  FC_FUNC_(bind_dotp_gd_add1, BIND_DOTP_GD_ADD1)
    (&out_dotp, *this, v1_add, v2);
  return out_dotp;
}

double Domain::dotp_gd(double* v1_add,
    double* v2_add) const
{
  double out_dotp;
  FC_FUNC_(bind_dotp_gd_add12, BIND_DOTP_GD_ADD12)
    (&out_dotp, *this, v1_add, v2_add);
  return out_dotp;
}

double Domain::square_gd(const double v[3]) const
{
  double out_square_gd;
  FC_FUNC_(bind_square_gd, BIND_SQUARE_GD)
    (&out_square_gd, *this, v);
  return out_square_gd;
}

double Domain::square_gd(double* v_add) const
{
  double out_square;
  FC_FUNC_(bind_square_gd_add, BIND_SQUARE_GD_ADD)
    (&out_square, *this, v_add);
  return out_square;
}

