__all__ = ['PythonCAPIType', 'PyTypeInterface']

from wrapper_base import *
from parser.api import CHAR_BIT, Module, declaration_type_spec, \
     TypeDecl, TypeStmt, Subroutine, Function, Integer, Real,\
     DoublePrecision, Complex, DoubleComplex, Logical, Character, \
     Byte

class PyTypeInterface:

    def __init__(self, typedecl):
        if isinstance(typedecl, TypeStmt):
            typedecl = typedecl.get_type_decl(typedecl.name)
        self._typedecl = typedecl
        if isinstance(typedecl, TypeDecl):
            self.name = name = typedecl.name
            tname = 'f2py_type_%s_' % (name)
        else:
            if isinstance(typedecl,(Integer,Byte)):
                tname = 'npy_int'
            elif isinstance(typedecl,(Real, DoublePrecision)):
                tname = 'npy_float'
            elif isinstance(typedecl,(Complex, DoubleComplex)):
                tname = 'npy_complex'
            elif isinstance(typedecl,Logical):
                tname = 'f2py_bool'
            elif isinstance(typedecl,Character):
                tname = 'f2py_string'
            else:
                raise NotImplementedError,`typedecl.__class__`
        bitsize = typedecl.get_bit_size()
        self.ctype = ctype = '%s%s' % (tname,bitsize)
        self.bits = bitsize
        self.bytes = bitsize / CHAR_BIT

        if isinstance(typedecl, TypeDecl):
            self.otype = '%sObject' % (ctype)
            self.ftype = 'TYPE(%s)' % (name)
        return
    def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self._typedecl)
    def __str__(self):
        s = []
        for k,v in self.__dict__.items():
            if k.startswith('_'): continue
            s.append('%s=%s' % (k,v))
        return 'PyTypeInterface(%s)' % (', '.join(s))

class PythonCAPIType(WrapperBase):
    """
    Fortran type hooks.
    """
    def __init__(self, parent, typedecl):
        WrapperBase.__init__(self)
        if isinstance(typedecl, tuple(declaration_type_spec)):
            if isinstance(typedecl, TypeStmt):
                type_decl = typedecl.get_type_decl(typedecl.name)
                assert type_decl is not None,"%s %s" % (typedecl,typedecl.name)
                PythonCAPIDerivedType(parent, type_decl)
            else:
                PythonCAPIIntrinsicType(parent, typedecl)
        elif isinstance(typedecl, TypeDecl):
            PythonCAPIDerivedType(parent, typedecl)
        else:
            raise NotImplementedError,`self.__class__,typedecl.__class__`
        return

class PythonCAPIIntrinsicType(WrapperBase):
    """
    Fortran intrinsic type hooks.
    """

    capi_code_template_scalar = '''
static PyObject* pyobj_from_%(ctype)s(%(ctype)s* value) {
  PyObject* obj = PyArrayScalar_New(%(Cls)s);
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  fprintf(stderr,"pyobj_from_%(ctype)s(value=%%"%(CTYPE)s_FMT")\\n",*value);
#endif
  if (obj==NULL) /* TODO: set exception */ return NULL;
  PyArrayScalar_ASSIGN(obj,%(Cls)s,*value);
  return obj;
}

static int pyobj_to_%(ctype)s(PyObject *obj, %(ctype)s* value) {
  int return_value = 0;
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  fprintf(stderr,"pyobj_to_%(ctype)s(type=%%s)\\n",PyString_AS_STRING(PyObject_Repr(PyObject_Type(obj))));
#endif
  if (obj==NULL) ;
  else if (PyArray_IsScalar(obj,%(Cls)s)) {
    *value = PyArrayScalar_VAL(obj,%(Cls)s);
    return_value = 1;
  }
  else if (PySequence_Check(obj)) {
    if (PySequence_Size(obj)==1)
      return_value = pyobj_to_%(ctype)s(PySequence_GetItem(obj,0),value);
  } else {
    PyObject* sc = Py%(Cls)sArrType_Type.tp_new(
      &Py%(Cls)sArrType_Type,Py_BuildValue("(O)",obj),NULL);
    if (sc==NULL) ;
    else if (PyArray_IsScalar(sc, Generic))
      return_value = pyobj_to_%(ctype)s(sc,value);
    else
      return_value = pyobj_to_%(ctype)s(PyArray_ScalarFromObject(sc),value);
  }
  if (!return_value && !PyErr_Occurred()) {
    PyObject* r = PyString_FromString("Failed to convert ");
    PyString_ConcatAndDel(&r, PyObject_Repr(PyObject_Type(obj)));
    PyString_ConcatAndDel(&r, PyString_FromString(" to C %(ctype)s"));
    PyErr_SetObject(PyExc_TypeError,r);
  }
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  if (PyErr_Occurred()) {
    if (return_value)
      fprintf(stderr,"pyobj_to_%(ctype)s:INCONSISTENCY with return_value=%%d and PyErr_Occurred()=%%p\\n",return_value, PyErr_Occurred());
    else
      fprintf(stderr,"pyobj_to_%(ctype)s: PyErr_Occurred()=%%p\\n", PyErr_Occurred());
  } else {
    if (return_value)
      fprintf(stderr,"pyobj_to_%(ctype)s: value=%%"%(CTYPE)s_FMT"\\n", *value);
    else
      fprintf(stderr,"pyobj_to_%(ctype)s:INCONSISTENCY with return_value=%%d and PyErr_Occurred()=%%p\\n",return_value, PyErr_Occurred());
  }
#endif
  return return_value;
}
'''

    capi_code_template_complex_scalar = '''
static PyObject* pyobj_from_%(ctype)s(%(ctype)s* value) {
  PyObject* obj = PyArrayScalar_New(%(Cls)s);
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  fprintf(stderr,"pyobj_from_%(ctype)s(value=(%%"%(FCTYPE)s_FMT",%%"%(FCTYPE)s_FMT"))\\n",value->real, value->imag);
#endif
  if (obj==NULL) /* TODO: set exception */ return NULL;
  PyArrayScalar_ASSIGN(obj,%(Cls)s,*value);
  return obj;
}

static int pyobj_to_%(ctype)s(PyObject *obj, %(ctype)s* value) {
  int return_value = 0;
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  fprintf(stderr,"pyobj_to_%(ctype)s(type=%%s)\\n",PyString_AS_STRING(PyObject_Repr(PyObject_Type(obj))));
#endif
  if (obj==NULL) ;
  else if (PyArray_IsScalar(obj,%(Cls)s)) {
    value->real = PyArrayScalar_VAL(obj,%(Cls)s).real;
    value->imag = PyArrayScalar_VAL(obj,%(Cls)s).imag;
    return_value = 1;
  }
  else if (PySequence_Check(obj)) {
    if (PySequence_Size(obj)==1)
      return_value = pyobj_to_%(ctype)s(PySequence_GetItem(obj,0),value);
    else if (PySequence_Size(obj)==2) {
      return_value = pyobj_to_%(fctype)s(PySequence_GetItem(obj,0),&(value->real))
                     && pyobj_to_%(fctype)s(PySequence_GetItem(obj,1),&(value->imag));
    }
  } else {
    PyObject* sc = Py%(Cls)sArrType_Type.tp_new(
      &Py%(Cls)sArrType_Type,Py_BuildValue("(O)",obj),NULL);
    if (sc==NULL) ;
    else if (PyArray_IsScalar(sc, Generic))
      return_value = pyobj_to_%(ctype)s(sc,value);
    else
      return_value = pyobj_to_%(ctype)s(PyArray_ScalarFromObject(sc),value);
  }
  if (!return_value && !PyErr_Occurred()) {
    PyObject* r = PyString_FromString("Failed to convert ");
    PyString_ConcatAndDel(&r, PyObject_Repr(PyObject_Type(obj)));
    PyString_ConcatAndDel(&r, PyString_FromString(" to C %(ctype)s"));
    PyErr_SetObject(PyExc_TypeError,r);
  }
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  if (PyErr_Occurred()) {
    if (return_value)
      fprintf(stderr,"pyobj_to_%(ctype)s:INCONSISTENCY with return_value=%%d and PyErr_Occurred()=%%p\\n",return_value, PyErr_Occurred());
    else
      fprintf(stderr,"pyobj_to_%(ctype)s: PyErr_Occurred()=%%p\\n", PyErr_Occurred());
  } else {
    if (return_value)
      fprintf(stderr,"pyobj_to_%(ctype)s: value=(%%"%(FCTYPE)s_FMT",%%"%(FCTYPE)s_FMT")\\n",
      value->real, value->imag);
    else
      fprintf(stderr,"pyobj_to_%(ctype)s:INCONSISTENCY with return_value=%%d and PyErr_Occurred()=%%p\\n",return_value, PyErr_Occurred());
  }
#endif
  return return_value;
}
'''

    capi_code_template_logical_scalar = '''
static PyObject* pyobj_from_%(ctype)s(%(ctype)s* value) {
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  fprintf(stderr,"pyobj_from_%(ctype)s(value=%%"%(ICTYPE)s_FMT")\\n",*value);
#endif
  if (*value) {
    PyArrayScalar_RETURN_TRUE;
  } else {
    PyArrayScalar_RETURN_FALSE;
  }
}
static int pyobj_to_%(ctype)s(PyObject *obj, %(ctype)s* value) {
  int return_value = 0;
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  fprintf(stderr,"pyobj_to_%(ctype)s(type=%%s)\\n",PyString_AS_STRING(PyObject_Repr(PyObject_Type(obj))));
#endif
  if (obj==NULL) ;
  else if (PyArray_IsScalar(obj,Bool)) {
    *value = PyArrayScalar_VAL(obj,Bool);
    return_value = 1;
  } else {
    switch (PyObject_IsTrue(obj)) {
      case 0: *value = 0; return_value = 1; break;
      case -1: break;
      default: *value = 1; return_value = 1;
    }
  }
  if (!return_value && !PyErr_Occurred()) {
    PyObject* r = PyString_FromString("Failed to convert ");
    PyString_ConcatAndDel(&r, PyObject_Repr(PyObject_Type(obj)));
    PyString_ConcatAndDel(&r, PyString_FromString(" to C %(ctype)s"));
    PyErr_SetObject(PyExc_TypeError,r);
  }
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  if (PyErr_Occurred()) {
    if (return_value)
      fprintf(stderr,"pyobj_to_%(ctype)s:INCONSISTENCY with return_value=%%d and PyErr_Occurred()=%%p\\n",return_value, PyErr_Occurred());
    else
      fprintf(stderr,"pyobj_to_%(ctype)s: PyErr_Occurred()=%%p\\n", PyErr_Occurred());
  } else {
    if (return_value)
      fprintf(stderr,"pyobj_to_%(ctype)s: value=%%"%(ICTYPE)s_FMT"\\n", *value);
    else
      fprintf(stderr,"pyobj_to_%(ctype)s:INCONSISTENCY with return_value=%%d and PyErr_Occurred()=%%p\\n",return_value, PyErr_Occurred());
  }
#endif
  return return_value;
}
'''
    capi_code_template_string_scalar = '''
static PyObject* pyobj_from_%(ctype)s(%(ctype)s* value) {
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  fprintf(stderr,"pyobj_from_%(ctype)s(value->data=\'%%s\')\\n",value->data);
#endif
  PyArray_Descr* descr = PyArray_DescrNewFromType(NPY_STRING);
  descr->elsize = %(bytes)s;
  PyObject* obj = PyArray_Scalar(value->data, descr, NULL);
  if (obj==NULL) /* TODO: set exception */ return NULL;
  return obj;
}

static int pyobj_to_%(ctype)s(PyObject *obj, %(ctype)s* value) {
  int return_value = 0;
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  fprintf(stderr,"pyobj_to_%(ctype)s(type=%%s)\\n",PyString_AS_STRING(PyObject_Repr(PyObject_Type(obj))));
#endif
  if (PyString_Check(obj)) {
    int s = PyString_GET_SIZE(obj);
    memset(value->data, (int)\' \',%(bytes)s);
    return_value = !! strncpy(value->data,PyString_AS_STRING(obj),%(bytes)s);
    if (return_value && s<%(bytes)s) {
      memset(value->data + s, (int)\' \',%(bytes)s-s);
    }
  } else {
    return_value = pyobj_to_%(ctype)s(PyObject_Str(obj), value);
  }
  if (!return_value && !PyErr_Occurred()) {
    PyObject* r = PyString_FromString("Failed to convert ");
    PyString_ConcatAndDel(&r, PyObject_Repr(PyObject_Type(obj)));
    PyString_ConcatAndDel(&r, PyString_FromString(" to C %(ctype)s"));
    PyErr_SetObject(PyExc_TypeError,r);
  }
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  if (PyErr_Occurred()) {
    if (return_value)
      fprintf(stderr,"pyobj_to_%(ctype)s:INCONSISTENCY with return_value=%%d and PyErr_Occurred()=%%p\\n",return_value, PyErr_Occurred());
    else
      fprintf(stderr,"pyobj_to_%(ctype)s: PyErr_Occurred()=%%p\\n", PyErr_Occurred());
  } else {
    if (return_value)
      fprintf(stderr,"pyobj_to_%(ctype)s: value->data=\'%%s\'\\n", value->data);
    else
      fprintf(stderr,"pyobj_to_%(ctype)s:INCONSISTENCY with return_value=%%d and PyErr_Occurred()=%%p\\n",return_value, PyErr_Occurred());
  }
#endif
  return return_value;
}
'''
    capi_code_template_string0_scalar = '''
static PyObject* pyobj_from_%(ctype)s(%(ctype)s* value) {
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  fprintf(stderr,"pyobj_from_%(ctype)s(value->len=%%d, value->data=\'%%s\')\\n",value->len, value->data);
#endif
  PyArray_Descr* descr = PyArray_DescrNewFromType(NPY_STRING);
  descr->elsize = value->len;
  PyObject* obj = PyArray_Scalar(value->data, descr, NULL);
  if (obj==NULL) /* TODO: set exception */ return NULL;
  return obj;
}

static int pyobj_to_%(ctype)s(PyObject *obj, %(ctype)s* value) {
  int return_value = 0;
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  fprintf(stderr,"pyobj_to_%(ctype)s(type=%%s)\\n",PyString_AS_STRING(PyObject_Repr(PyObject_Type(obj))));
#endif
  if (PyString_Check(obj)) {
    value->len = PyString_GET_SIZE(obj);
    value->data = malloc(value->len*sizeof(char));
    return_value = !! strncpy(value->data,PyString_AS_STRING(obj),value->len);
  } else {
    return_value = pyobj_to_%(ctype)s(PyObject_Str(obj), value);
  }
  if (!return_value && !PyErr_Occurred()) {
    PyObject* r = PyString_FromString("Failed to convert ");
    PyString_ConcatAndDel(&r, PyObject_Repr(PyObject_Type(obj)));
    PyString_ConcatAndDel(&r, PyString_FromString(" to C %(ctype)s"));
    PyErr_SetObject(PyExc_TypeError,r);
  }
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  if (PyErr_Occurred()) {
    if (return_value)
      fprintf(stderr,"pyobj_to_%(ctype)s:INCONSISTENCY with return_value=%%d and PyErr_Occurred()=%%p\\n",return_value, PyErr_Occurred());
    else
      fprintf(stderr,"pyobj_to_%(ctype)s: PyErr_Occurred()=%%p\\n", PyErr_Occurred());
  } else {
    if (return_value)
      fprintf(stderr,"pyobj_to_%(ctype)s: value->len=%%d, value->data=\'%%s\'\\n", value->len, value->data);
    else
      fprintf(stderr,"pyobj_to_%(ctype)s:INCONSISTENCY with return_value=%%d and PyErr_Occurred()=%%p\\n",return_value, PyErr_Occurred());
  }
#endif
  return return_value;
}
'''
    def __init__(self, parent, typedecl):
        WrapperBase.__init__(self)
        self.name = name = typedecl.name
        ti = PyTypeInterface(typedecl)
        self.ctype = ctype = ti.ctype

        defined = parent.defined_types
        if ctype in defined:
            return
        defined.append(ctype)

        self.info('Generating interface for %s: %s' % (typedecl.__class__.__name__, ctype))
        self.parent = parent
        if isinstance(typedecl, (Integer,Byte,Real,DoublePrecision)):
            self.Cls = ctype[4].upper() + ctype[5:]
            self.capi_code_template = self.capi_code_template_scalar
        elif isinstance(typedecl, (Complex,DoubleComplex)):
            self.Cls = ctype[4].upper() + ctype[5:]
            PythonCAPIIntrinsicType(parent, typedecl.get_part_typedecl())
            ti1 = PyTypeInterface(typedecl.get_part_typedecl())
            self.fctype = ti1.ctype
            self.capi_code_template = self.capi_code_template_complex_scalar
        elif isinstance(typedecl, Logical):
            self.ictype = 'npy_int%s' % (typedecl.get_bit_size())
            self.header_template = '#define %(ctype)s %(ictype)s'
            self.capi_code_template = self.capi_code_template_logical_scalar
        elif isinstance(typedecl, Character):
            self.bits = bits = typedecl.get_bit_size()
            if bits:
                self.bytes = bits/CHAR_BIT
                self.header_template = '''
#include <string.h>
typedef struct { char data[%(bytes)s]; } %(ctype)s;
'''
                self.capi_code_template = self.capi_code_template_string_scalar
            else:
                self.header_template = '''
#include <string.h>
typedef struct { char* data; size_t len; } %(ctype)s;
'''
                self.capi_code_template = self.capi_code_template_string0_scalar
        else:
            raise NotImplementedError,`name,ctype`
        parent.apply_templates(self)
        return

class PythonCAPIDerivedType(WrapperBase):
    """
    Fortran 90 derived type hooks.
    """

    header_template_wrapper = '''\
#define %(otype)s_Check(obj) \\
    PyObject_TypeCheck((PyObject*)obj, &%(otype)sType)
#define %(init_func)s_f \\
    F_FUNC(%(init_func)s,%(INIT_FUNC)s)
'''

    typedef_template_wrapper = '''\
typedef void * %(ctype)s;
typedef struct {
  PyObject_HEAD
  %(ptrstruct_list)s
  %(ctype)s data;
} %(otype)s;
typedef void (*%(init_func)s_c_functype)(%(init_func_c_ctype_arg_clist)s);
'''

    typedef_template_importer = '''\
typedef void * %(ctype)s;
typedef struct {
  PyObject_HEAD
  %(ptrstruct_list)s
  %(ctype)s data;
} %(otype)s;
typedef int (*pyobj_to_%(ctype)s_inplace_functype)(PyObject*, %(otype)s** );
typedef int (*pyobj_to_%(ctype)s_functype)(PyObject*, %(otype)s* );
typedef PyObject* (*pyobj_from_%(ctype)s_functype)(%(ctype)s*);
#define %(otype)sType (*(PyTypeObject *)PyArray_API[0])
#define pyobj_from_%(ctype)s ((pyobj_from_%(ctype)s_functype)PyArray_API[1])
#define pyobj_to_%(ctype)s_inplace ((pyobj_to_%(ctype)s_inplace_functype)PyArray_API[2])
'''

    extern_template_wrapper = '''\
static PyTypeObject %(otype)sType;
extern void %(init_func)s_f(%(init_func)s_c_functype, void*, %(ctype)s);
'''

    objdecl_template_wrapper = '''\
static PyMethodDef %(otype)s_methods[] = {
    %(type_method_list)s
    {NULL}  /* Sentinel */
};

static PyGetSetDef %(otype)s_getseters[] = {
    %(type_getseters_list)s
    {NULL}  /* Sentinel */
};

static PyTypeObject %(otype)sType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "%(modulename)s.%(name)s",                /*tp_name*/
    sizeof(%(otype)s),    /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)%(otype)s_dealloc, /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    %(otype)s_repr,            /*tp_repr*/
    0,                         /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /*tp_flags*/
    "Fortran derived type %(name)s objects",        /* tp_doc */
    0,                         /* tp_traverse */
    0,                         /* tp_clear */
    0,                         /* tp_richcompare */
    0,                         /* tp_weaklistoffset */
    0,                         /* tp_iter */
    0,                         /* tp_iternext */
    %(otype)s_methods,          /* tp_methods */
    0 /*%(otype)s_members*/,    /* tp_members */
    %(otype)s_getseters,       /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    (initproc)%(otype)s_init,      /* tp_init */
    0,                         /* tp_alloc */
    %(otype)s_new,                 /* tp_new */
};

void *F2PY_%(otype)s_API[] = {
  (void *) &%(otype)sType,
  (void *) pyobj_from_%(ctype)s,
  (void *) pyobj_to_%(ctype)s_inplace
};
'''

    objdecl_template_importer = '''\
static void **F2PY_%(otype)s_API;
'''
    module_init_template_wrapper = '''\
if (PyType_Ready(&%(otype)sType) < 0) goto capi_err;
PyModule_AddObject(f2py_module, "%(name)s", (PyObject *)&%(otype)sType);
{
  PyObject* c_api = PyCObject_FromVoidPtr((void *)F2PY_%(otype)s_API, NULL);
  PyModule_AddObject(f2py_module, "_%(NAME)s_API", c_api);
  if (PyErr_Occurred()) goto capi_err;
}
'''
    module_init_template_importer = '''\
{
  PyObject *c_api = NULL;
  PyObject *wrappermodule = PyImport_ImportModule("%(wrappermodulename)s");
  if (wrappermodule == NULL) goto capi_%(name)s_err;
  c_api = PyObject_GetAttrString(wrappermodule, "_%(NAME)s_API");
  if (c_api == NULL) {Py_DECREF(wrappermodule); goto capi_%(name)s_err;}
  if (PyCObject_Check(c_api)) {
      F2PY_%(otype)s_API = (void **)PyCObject_AsVoidPtr(c_api);
  }
  Py_DECREF(c_api);
  Py_DECREF(wrappermodule);
  if (F2PY_%(otype)s_API != NULL) goto capi_%(name)s_ok;
capi_%(name)s_err:
  PyErr_Print();
  PyErr_SetString(PyExc_ImportError, "%(wrappermodulename)s failed to import");
  return;
capi_%(name)s_ok:
  c_api = PyCObject_FromVoidPtr((void *)F2PY_%(otype)s_API, NULL);
  PyModule_AddObject(f2py_module, "_%(NAME)s_API", c_api);
  if (PyErr_Occurred()) goto capi_err;
}
'''

    c_code_template_wrapper = '''\
static void %(init_func)s_c(
               %(init_func_c_arg_clist)s) {
  %(init_func_c_body_list)s
}
'''

    capi_code_template_wrapper = '''\
static void %(otype)s_dealloc(%(otype)s* self) {
  if (self->data)
    PyMem_Free(self->data);
  self->ob_type->tp_free((PyObject*)self);
}

static int pyobj_to_%(ctype)s_inplace(PyObject *obj,
                                      %(otype)s** value_ptr) {
  int return_value = 0;
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  fprintf(stderr,"pyobj_to_%(ctype)s(type=%%s)\\n",PyString_AS_STRING(PyObject_Repr(PyObject_Type(obj))));
#endif
  if (%(otype)s_Check(obj)) {
    *value_ptr = (%(otype)s*)obj;
    return_value = 1;
  }
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  fprintf(stderr,"pyobj_to_%(ctype)s: return_value=%%d, PyErr_Occurred()=%%p\\n", return_value, PyErr_Occurred());
#endif
  return return_value;
}

static int pyobj_to_%(ctype)s(PyObject *obj,
                                   %(ctype)s* value_ptr) {
  int return_value = 0;
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  fprintf(stderr,"pyobj_to_%(ctype)s(type=%%s)\\n",PyString_AS_STRING(PyObject_Repr(PyObject_Type(obj))));
#endif
  if (%(otype)s_Check(obj)) {
    if (!memcpy(value_ptr,((%(otype)s *)obj)->data, %(bytes)s)) {
      PyErr_SetString(PyExc_MemoryError,
         "failed to copy %(name)s instance memory to %(ctype)s object.");
    } else {
      return_value = 1;
    }
  }
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  fprintf(stderr,"pyobj_to_%(ctype)s: return_value=%%d, PyErr_Occurred()=%%p\\n", return_value, PyErr_Occurred());
#endif
  return return_value;
}

static PyObject* pyobj_from_%(ctype)s(%(ctype)s* value_ptr) {
  %(otype)s* obj = (%(otype)s*)(%(otype)sType.tp_alloc(&%(otype)sType, 0));
  if (obj == NULL)
    return NULL;
  obj->data = PyMem_Malloc(%(bytes)s);
  if (obj->data == NULL) {
    Py_DECREF(obj);
    return PyErr_NoMemory();
  }
  if (value_ptr) {
    if (!memcpy(obj->data, value_ptr, %(bytes)s)) {
      PyErr_SetString(PyExc_MemoryError,
         "failed to copy %(ctype)s object memory to %(name)s instance.");
    }
  }
  %(init_func)s_f(%(init_func)s_c, obj, obj->data);
  return (PyObject*)obj;
}

static PyObject * %(otype)s_new(PyTypeObject *type,
                                PyObject *args, PyObject *kwds)
{
  return pyobj_from_%(ctype)s(NULL);
}

static int %(otype)s_init(%(otype)s *self,
                          PyObject *capi_args, PyObject *capi_kwds)
{
   int return_value = 0;
#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  fprintf(stderr,"%(otype)s_init()\\n");
#endif
   if (!PyArg_ParseTuple(capi_args,"%(attr_format_elist)s"
                                   %(attr_init_clist)s))
      return_value = -1;

#if defined(F2PY_DEBUG_PYOBJ_TOFROM)
  fprintf(stderr,"%(otype)s_init: return_value=%%d, PyErr_Occurred()=%%p\\n", return_value, PyErr_Occurred());
#endif
   return return_value;
}

static PyObject * %(otype)s_as_tuple(%(otype)s * self) {
  return Py_BuildValue("%(as_tuple_format_elist)s"
                        %(as_tuple_arg_clist)s);
}

static PyObject * %(otype)s_repr(PyObject * self) {
  PyObject* r = PyString_FromString("%(name)s(");
  PyString_ConcatAndDel(&r, PyObject_Repr(%(otype)s_as_tuple((%(otype)s*)self)));
  PyString_ConcatAndDel(&r, PyString_FromString(")"));
  return r;
}

%(getset_func_list)s
'''

    fortran_code_template_wrapper = '''\
      subroutine %(init_func)s(init_func_c, self, obj)
      %(use_stmt_list)s
      %(type_decl_list)s
      external init_func_c
!     self is %(otype)s
      external self
      %(ftype)s obj
      call init_func_c(%(init_func_f_arg_clist)s)
      end
'''

    #module_method_template = ''''''

    _defined = []
    def __init__(self, parent, typedecl):
        WrapperBase.__init__(self)
        ti = PyTypeInterface(typedecl)
        self.ctype = ctype = ti.ctype
        defined = parent.defined_types
        if ctype in defined:
            return
        defined.append(ctype)



        implement_wrappers = True
        if isinstance(typedecl.parent,Module) and typedecl.parent.name!=parent.modulename:
            implement_wrappers = False
            self.info('Using api for %s.%s: %s' % (parent.modulename, typedecl.name, ctype))
            self.wrappermodulename = typedecl.parent.name
        else:
            self.info('Generating interface for %s.%s: %s' % (parent.modulename, typedecl.name, ctype))

        parent.isf90 = True
        self.parent = parent
        self.name = name = typedecl.name
        self.otype = otype = ti.otype
        self.ctype = ctype = ti.ctype
        self.ctype_ptrs = self.ctype + '_ptrs'
        self.ftype = ti.ftype
        self.bytes = bytes = ti.bytes

        if not implement_wrappers:
            self.typedef_template = self.typedef_template_importer
            self.objdecl_template = self.objdecl_template_importer
            self.module_init_template = self.module_init_template_importer
        else:
            self.header_template = self.header_template_wrapper
            self.typedef_template = self.typedef_template_wrapper
            self.extern_template = self.extern_template_wrapper
            self.objdecl_template = self.objdecl_template_wrapper
            self.module_init_template = self.module_init_template_wrapper
            self.c_code_template = self.c_code_template_wrapper
            self.capi_code_template = self.capi_code_template_wrapper
            self.fortran_code_template = self.fortran_code_template_wrapper
            WrapperCPPMacro(parent, 'F_FUNC')

        self.init_func_f_arg_list = ['self']
        self.init_func_c_arg_list = ['%s *self' % (otype)]
        self.init_func_c_ctype_arg_list = ['%s *' % (otype)]
        self.init_func_c_body_list = []
        self.ptrstruct_list = []
        self.attr_decl_list = []
        self.attr_format_list = []
        self.attr_init_list = []
        self.as_tuple_format_list = []
        self.as_tuple_arg_list = []
        self.getset_func_list = []
        self.type_getseters_list = []
        for n in typedecl.a.component_names:
            v = typedecl.a.components[n]
            t = v.get_typedecl()
            ti1 = PyTypeInterface(t)
            PythonCAPIType(parent, t)
            ct = ti1.ctype
            parent.add(t)
            self.ptrstruct_list.append('%s* %s_ptr;' % (ct, n))
            self.init_func_f_arg_list.append('obj %% %s' % (n))
            self.init_func_c_arg_list.append('\n%s * %s_ptr' % (ct, n))
            self.init_func_c_ctype_arg_list.append('\n%s *' % (ct))
            self.init_func_c_body_list.append('''\
if (!((void*)%(n)s_ptr >= self->data
      && (void*)%(n)s_ptr < self->data + %(bytes)s ))
  fprintf(stderr,"INCONSISTENCY IN %(name)s WRAPPER: "
                 "self->data=%%p <= %(n)s_ptr=%%p < self->data+%(bytes)s=%%p\\n",
                 self->data, %(n)s_ptr, self->data + %(bytes)s);
self->%(n)s_ptr = %(n)s_ptr;
''' % (locals()))
            self.attr_format_list.append('O&')
            self.attr_init_list.append('\npyobj_to_%s, self->%s_ptr' % (ct,n))
            self.as_tuple_format_list.append('O&')
            self.as_tuple_arg_list.append('\npyobj_from_%s, self->%s_ptr' % (ct, n))
            self.getset_func_list.append('''\
static PyObject * %(otype)s_get_%(n)s(%(otype)s *self,
                                      void *closure) {
  return pyobj_from_%(ct)s(self->%(n)s_ptr);
}
static int %(otype)s_set_%(n)s(%(otype)s *self,
                               PyObject *value, void *closure)
{
  if (value == NULL) {
    PyErr_SetString(PyExc_TypeError,
                    "Cannot delete %(name)s attribute %(n)s");
    return -1;
  }
  if (pyobj_to_%(ct)s(value, self->%(n)s_ptr))
    return 0;
  return -1;
}
''' % (locals()))
            self.type_getseters_list.append('{"%(n)s",(getter)%(otype)s_get_%(n)s, (setter)%(otype)s_set_%(n)s,\n "component %(n)s",NULL},' % (locals()))
        if self.attr_init_list: self.attr_init_list.insert(0,'')
        if self.as_tuple_arg_list: self.as_tuple_arg_list.insert(0,'')
        self.init_func = self.ctype + '_init'

        self.type_method_list = []
        self.type_method_list.append('{"as_tuple",(PyCFunction)%(otype)s_as_tuple,METH_NOARGS,\n "Return %(name)s components as tuple."},' % (self.__dict__))

        self.use_stmt_list = []
        self.type_decl_list = []
        if isinstance(typedecl.parent, Module):
            self.use_stmt_list.append('use %s' % (typedecl.parent.name))
        elif isinstance(typedecl.parent, (Subroutine, Function)):
            self.type_decl_list.append(typedecl.asfix())
        else:
            raise NotImplementedError,'types declared in '+typedecl.parent.__class__.__name__
        parent.apply_templates(self)
        return
