# distutils: language=c++

from libcpp cimport bool
from libcpp.memory cimport unique_ptr
from libcpp.string cimport string

cimport resolve_enum as benum
from bout_options cimport Options, OptionsReader, OptionsIO, writeDefaultOutputFile

cdef extern from "boutexception_helper.hxx":
     cdef void raise_bout_py_error()

{% for field in fields %}

cdef extern from "bout/{{ field.header }}.hxx":
    cppclass {{ field.field_type }}:
        {{ field.field_type }}(Mesh * mesh);
        {{ field.field_type }}(const {{ field.field_type }} &)
        double & operator()(int, int, int)
        int getNx()
        int getNy()
        int getNz()
        bool isAllocated()
        void setLocation(benum.CELL_LOC)
        benum.CELL_LOC getLocation()
        Mesh* getMesh()
{% for boundaryMethod in field.boundaries %}
        void {{ boundaryMethod }}(const string bndry)
        void {{ boundaryMethod }}()
        void {{ boundaryMethod }}(double t)
{% endfor %}
{% for fun in "sqrt", "exp", "log", "sin", "cos", "abs" %}
    {{ field.field_type }} {{ fun }}({{ field.field_type }})
{% endfor %}
    double max({{ field.field_type }})
    double min({{ field.field_type }})
    {{ field.field_type }} pow({{ field.field_type }},double)
    {{ field.field_type }} & ddt({{ field.field_type }})
{% endfor %}
{% for vec in vecs %}
cdef extern from "bout/{{ vec.header }}.hxx":
     cppclass {{ vec }}:
         {{ vec }}()
         {{ vec }}(const {{ vec }} &)
         {{ vec.field }} x
         {{ vec.field }} y
         {{ vec.field }} z
{% endfor %}

cdef extern from "bout/mesh.hxx":
    cppclass Mesh:
        Mesh()
        @staticmethod
        Mesh * create(Options * option)
        void load()
        void communicate(FieldGroup&)
        int getNXPE()
        int getNYPE()
        int getXProcIndex()
        int getYProcIndex()
        int xstart
        int ystart
        int LocalNx
        int LocalNy
        Coordinates * getCoordinates()

cdef extern from "bout/coordinates.hxx":
    cppclass Coordinates:
        Coordinates()
        {{ metric_field }} dx, dy, dz
        {{ metric_field }} J
        {{ metric_field }} Bxy
        {{ metric_field }} g11, g22, g33, g12, g13, g23
        {{ metric_field }} g_11, g_22, g_33, g_12, g_13, g_23
        {{ metric_field }} G1_11, G1_22, G1_33, G1_12, G1_13, G1_23
        {{ metric_field }} G2_11, G2_22, G2_33, G2_12, G2_13, G2_23
        {{ metric_field }} G3_11, G3_22, G3_33, G3_12, G3_13, G3_23
        {{ metric_field }} G1, G2, G3
        {{ metric_field }} ShiftTorsion
        {{ metric_field }} IntShiftTorsion
        int geometry()
        int calcCovariant()
        int calcContravariant()
        int jacobian()

cdef extern from "bout/fieldgroup.hxx":
    cppclass FieldGroup:
        FieldGroup()
        void add(Field3D&)
cdef extern from "bout/invert_laplace.hxx":
    cppclass Laplacian:
        @staticmethod
        unique_ptr[Laplacian] create()
        @staticmethod
        unique_ptr[Laplacian] create(Options *)
        Field3D solve(Field3D,Field3D)
        void setCoefA(Field3D)
        void setCoefC(Field3D)
        void setCoefC1(Field3D)
        void setCoefC2(Field3D)
        void setCoefD(Field3D)
        void setCoefEx(Field3D)
        void setCoefEy(Field3D)
        void setCoefEz(Field3D)

cdef extern from "bout/difops.hxx":
    Field3D Div_par(Field3D, benum.CELL_LOC, string)
    Field3D Grad_par(Field3D, benum.CELL_LOC, string)
    Field3D Laplace(Field3D)
    Field3D Vpar_Grad_par(Field3D, Field3D, benum.CELL_LOC, string)
    Field3D bracket(Field3D,Field3D, benum.BRACKET_METHOD, benum.CELL_LOC)
    Field3D Delp2(Field3D)

cdef extern from "bout/derivs.hxx":
{% for d in "XYZ" %}
{% for fun in "DDd", "D2Dd2" %}{# VDDd FDDd #}
{% set DD = fun.replace('d', d) %}
    Field3D {{ DD }}(Field3D, benum.CELL_LOC, string, string) except +raise_bout_py_error
{% endfor %}
{% for fun in "VDDd", "FDDd" %}
{% set DD = fun.replace('d', d) %}
    Field3D {{ DD }}(Field3D, Field3D, benum.CELL_LOC, string, string) except +raise_bout_py_error
{% endfor %}
{% endfor %}
    Field3D D2DXDY(Field3D, benum.CELL_LOC, string, string) except +raise_bout_py_error
    Field3D D2DXDZ(Field3D, benum.CELL_LOC, string, string) except +raise_bout_py_error
    Field3D D2DYDZ(Field3D, benum.CELL_LOC, string, string) except +raise_bout_py_error

cdef extern from "bout/interpolation.hxx":
    Field3D interp_to(Field3D, benum.CELL_LOC)

cdef extern from "bout/field_factory.hxx":
    cppclass FieldFactory:
        FieldFactory(Mesh*,Options*)
        Field3D create3D(string bla, Options * o, Mesh * m,benum.CELL_LOC loc, double t)

cdef extern from "bout/solver.hxx":
    cppclass Solver:
        @staticmethod
        Solver * create()
        void setModel(PhysicsModel *)
        void add(Field3D, char * name)
        void solve()

cdef extern from "bout/physicsmodel.hxx":
    cppclass PhysicsModel:
        int rhs(double t)
ctypedef void (*Method)(void *param, void *user_data)
cdef extern from "helper.h":
    cppclass PythonModel(PhysicsModel):
        int rhs(double t) except +raise_bout_py_error
        void pyinit()
        void free()
        void solve() except +raise_bout_py_error
        Solver * getSolver()
        void set_rhs_func(PythonModelCallback*)
        void set_init_func(PythonModelCallback*)
    cppclass PythonModelCallback:
        PythonModelCallback(Method method, void * user_data)
        void cy_execute(void * parameter)
    void throw_BoutException(string)

cdef extern from "bout/output.hxx":
    cppclass ConditionalOutput:
        void write(const char * str)
    ConditionalOutput output_info

cdef extern from "bout/vecops.hxx":
    Vector3D Grad(const Field3D& f, benum.CELL_LOC, string)
    Vector3D Grad_perp(const Field3D& f, benum.CELL_LOC, string)

cdef extern from "bout/vector3d.hxx":
    Vector3D cross(Vector3D, Vector3D)
