#ifndef AT_DOMAIN_H
#define AT_DOMAIN_H

#include "futile_cst.h"
#include <stdbool.h>
#include "dict.h"
#include "f_enums.h"

F_DEFINE_TYPE(domain);

f90_domain_pointer f90_domain_copy_constructor(const f90_domain* other);

f90_domain_pointer f90_domain_type_new(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);

void f90_domain_free(f90_domain_pointer self);

f90_domain_pointer domain_null(void);

f90_domain_pointer domain_new(const f90_f_enumerator* units,
  const f90_f_enumerator* bc[3],
  const double (*abc)[3][3],
  const double (*alpha_bc),
  const double (*beta_ac),
  const double (*gamma_ab),
  const double (*acell)[3]);

f90_domain_pointer change_domain_bc(const f90_domain* dom_in,
  char geocode);

f90_f_enumerator_pointer units_enum_from_str(const char* str);

/* void geocode_to_bc_enum(char geocode); */

void geocode_to_bc(int out_bc[3],
  char geocode);

char domain_geocode(const f90_domain* dom);

double domain_volume(const double acell[3],
  const f90_domain* dom);

/* void bc_periodic_dims(const int bc[3]); */

/* void domain_periodic_dims(const f90_domain* dom); */

void rxyz_ortho(const f90_domain* dom,
  double out_rxyz_ortho[3],
  const double rxyz[3]);

double distance(const f90_domain* dom,
  const double r[3],
  const double c[3]);

void closest_r(const f90_domain* dom,
  double out_r[3],
  const double v[3],
  const double center[3]);

void domain_set_from_dict(f90_dictionary_pointer dict,
  f90_domain* dom);

void domain_merge_to_dict(f90_dictionary_pointer dict,
  const f90_domain* dom);

double dotp_gu(const f90_domain* dom,
  const double v1[3],
  const double v2[3]);

double dotp_gu_add2(const f90_domain* dom,
  const double v1[3],
  double* v2_add);

double dotp_gu_add1(const f90_domain* dom,
  double* v1_add,
  const double v2[3]);

double square(const f90_domain* dom,
  const double v[3]);

double square_add(const f90_domain* dom,
  double* v_add);

double dotp_gd(const f90_domain* dom,
  const double v1[3],
  const double v2[3]);

double dotp_gd_add2(const f90_domain* dom,
  const double v1[3],
  double* v2_add);

double dotp_gd_add1(const f90_domain* dom,
  double* v1_add,
  const double v2[3]);

double dotp_gd_add12(const f90_domain* dom,
  double* v1_add,
  double* v2_add);

double square_gd(const f90_domain* dom,
  const double v[3]);

double square_gd_add(const f90_domain* dom,
  double* v_add);

f90_f_enumerator_pointer angstroem_units(void);

f90_f_enumerator_pointer periodic_bc(void);

f90_f_enumerator_pointer free_bc(void);

f90_f_enumerator_pointer atomic_units(void);

f90_f_enumerator_pointer double_precision_enum(void);

f90_f_enumerator_pointer nanometer_units(void);

#endif
