/* Include file for C universe-related functions.
 *
 * Written by Konrad Hinsen
 */

#ifndef MMTK_UNIVERSE_H

#include "MMTK/core.h"

#ifdef WITH_THREAD
#include "pythread.h"
#endif

/* Type definitions */

typedef void distance_fn(vector3 d, vector3 r1, vector3 r2, double *data);
typedef void correction_fn(vector3 *x, int natoms, double *data);
typedef double volume_fn(double scale_factor, double *data);
typedef void box_fn(vector3 *x, vector3 *b, int n, double *data, int to_box);
typedef void bounding_box_fn(vector3 *box1, vector3 *box2, vector3 *x,
			     int n, double *data);


/* Universe specification object structure */

typedef struct {
  PyObject_HEAD
  PyArrayObject *geometry;
  double *geometry_data;
  distance_fn *distance_function;
  correction_fn *correction_function;
  volume_fn *volume_function;
  box_fn *box_function;
  box_fn *trajectory_function;
  bounding_box_fn *bounding_box_function;
#ifdef WITH_THREAD
  PyThread_type_lock configuration_change_lock;
  PyThread_type_lock main_state_lock;
  PyThread_type_lock state_wait_lock[MMTK_MAX_THREADS];
  int state_access_type[MMTK_MAX_THREADS];
  int state_access;
  int waiting_threads;
#endif
  int is_periodic;
  int is_orthogonal;
  int geometry_data_length;
} PyUniverseSpecObject;


/* Macro definitions */

/* Distance vector in infinite universe */
#define distance_vector_1(d, r1, r2, data) \
  { \
    d[0] = r2[0]-r1[0]; \
    d[1] = r2[1]-r1[1]; \
    d[2] = r2[2]-r1[2]; \
  }

/* Distance vector in orthorhombic universe */
#define distance_vector_2(d, r1, r2, data) \
  { \
    double xh = 0.5*(data)[0]; \
    double yh = 0.5*(data)[1]; \
    double zh = 0.5*(data)[2]; \
    d[0] = r2[0]-r1[0]; \
    if (d[0] > xh) d[0] -= (data)[0]; \
    if (d[0] <= -xh) d[0] += (data)[0]; \
    d[1] = r2[1]-r1[1]; \
    if (d[1] > yh) d[1] -= (data)[1]; \
    if (d[1] <= -yh) d[1] += (data)[1]; \
    d[2] = r2[2]-r1[2]; \
    if (d[2] > zh) d[2] -= (data)[2]; \
    if (d[2] <= -zh) d[2] += (data)[2]; \
  }

/* Distance vector in parallelepipedic universe */
#define distance_vector_3(d, r1, r2, data) \
  { \
    double dx = (r2)[0]-(r1)[0]; \
    double dy = (r2)[1]-(r1)[1]; \
    double dz = (r2)[2]-(r1)[2]; \
    double dfx = (data)[0+9]*dx + (data)[1+9]*dy + (data)[2+9]*dz; \
    double dfy = (data)[3+9]*dx + (data)[4+9]*dy + (data)[5+9]*dz; \
    double dfz = (data)[6+9]*dx + (data)[7+9]*dy + (data)[8+9]*dz; \
    if (dfx > 0.5) dfx -= 1.; \
    if (dfx <= -0.5) dfx += 1.; \
    if (dfy > 0.5) dfy -= 1.; \
    if (dfy <= -0.5) dfy += 1.; \
    if (dfz > 0.5) dfz -= 1.; \
    if (dfz <= -0.5) dfz += 1.; \
    d[0] = (data)[0]*dfx + (data)[1]*dfy + (data)[2]*dfz; \
    d[1] = (data)[3]*dfx + (data)[4]*dfy + (data)[5]*dfz; \
    d[2] = (data)[6]*dfx + (data)[7]*dfy + (data)[8]*dfz; \
  }

/*
 * C API
 */

/* Type definitions */
#define PyUniverseSpec_Type_NUM 0

/* Universe state lock access */
#define PyUniverseSpec_StateLock_RET int
#define PyUniverseSpec_StateLock_PROTO \
        Py_PROTO((PyUniverseSpecObject *universe, int action))
#define PyUniverseSpec_StateLock_NUM 1

/* Total number of C API pointers */
#define PyUniverse_API_pointers 2


#ifdef _UNIVERSE_MODULE

/* Type object declarations */
extern PyTypeObject PyUniverseSpec_Type;

/* Type check macros */
#define PyUniverseSpec_Check(op) ((op)->ob_type == &PyUniverseSpec_Type)

/* C API function declarations */

extern PyUniverseSpec_StateLock_RET PyUniverseSpec_StateLock \
       PyUniverseSpec_StateLock_PROTO;

#else

/* C API address pointer */ 
static void **PyUniverse_API;

/* Type definitions */
#define PyUniverseSpec_Type *(PyTypeObject *) \
                             PyUniverse_API[PyUniverseSpec_Type_NUM]

/* Type check macros */
#define PyUniverseSpec_Check(op) \
   ((op)->ob_type == (PyTypeObject *)PyUniverse_API[PyUniverseSpec_Type_NUM])

/* C API function declarations */
#define PyUniverseSpec_StateLock \
  (*(PyUniverseSpec_StateLock_RET (*)PyUniverseSpec_StateLock_PROTO) \
   PyUniverse_API[PyUniverseSpec_StateLock_NUM])

/* Import macro */
#define import_MMTK_universe() \
{ \
  PyObject *module = PyImport_ImportModule("MMTK_universe"); \
  if (module != NULL) { \
    PyObject *module_dict = PyModule_GetDict(module); \
    PyObject *c_api_object = PyDict_GetItemString(module_dict, "_C_API"); \
    if (PyCObject_Check(c_api_object)) { \
      PyUniverse_API = (void **)PyCObject_AsVoidPtr(c_api_object); \
    } \
  } \
}

#endif

#define MMTK_UNIVERSE_H
#endif
