#!/usr/bin/env python

from f2py import crackfortran
import os, yaml
from optparse import OptionParser

def toCArg(name, aDef, args = [], csts = [], db = {}):
  try:
    return CNumericArg(name, aDef, args, csts)
  except ValueError:
    pass
  try:
    return CNumericPtrArg(name, aDef, db)
  except ValueError:
    pass
  try:
    return CStringArg(name, aDef, args, csts)
  except ValueError:
    pass
  try:
    return CTypeArg(name, aDef, db)
  except ValueError:
    pass
  raise ValueError('Unknown Fortran type %s.' % aDef['typespec'])

def toCReturnArg(name, aDef, args, csts, db = {}):
  try:
    return CNumericReturnArg(name, aDef, args, csts)
  except ValueError:
    pass
  try:
    return CStringReturnArg(name, aDef, args, csts)
  except ValueError:
    pass
  try:
    return CTypeReturnArg(name, aDef, db)
  except ValueError:
    pass
  raise ValueError('Unknown Fortran type %s.' % aDef['typespec'])

def toNumericType(aDef):
  if (aDef['typespec'] == 'logical'):
    kind = aDef.get('kindselector', {'kind': None})['kind']
    if kind is None:
      return 'bool'
    else:
      raise NotImplementedError('Logic type kind = ' + kind)
  elif (aDef['typespec'] == 'integer'):
    kind = aDef.get('kindselector', {'kind': 4})['kind']
    if kind in ['8', ]:
      return 'long'
    elif kind in ['f_long', ]:
      return 'size_t'
    elif kind in ['f_address', ]:
      return 'void*'
    else:
      return 'int'
  elif (aDef['typespec'] == 'real'):
    kind = aDef.get('kindselector', {'kind': 4})['kind']
    if kind in ['dp', 'gp', 'wp', 'f_double', '8']:
      return 'double'
    else:
      return 'float'
  elif (aDef['typespec'] == 'double precision'):
    return 'double'
  elif (aDef['typespec'] == 'complex'):
    raise NotImplementedError('Complex type')
  elif (aDef['typespec'] == 'double complex'):
    raise NotImplementedError('Double complex type')
  raise ValueError

def toNumericPtrType(aDef):
  return '%s_%dd_pointer' % (toNumericType(aDef), len(aDef['dimension']))

def toBindArg(name, aDef, memberOf):
  try:
    return BindNumericArg(name, aDef, memberOf)
  except ValueError:
    pass
  try:
    return BindNumericPtrArg(name, aDef, memberOf)
  except ValueError:
    pass
  try:
    return BindStringArg(name, aDef, memberOf)
  except ValueError:
    pass
  try:
    return BindTypeArg(name, aDef, memberOf)
  except ValueError:
    pass
  raise ValueError('Unknown Fortran type %s.' % aDef['typespec'])

def toBindReturnArg(name, aDef, memberOf):
  try:
    return BindReturnNumericArg(name, aDef, memberOf)
  except ValueError:
    pass
  try:
    return BindReturnStringArg(name, aDef, memberOf)
  except ValueError:
    pass
  try:
    return BindReturnTypeArg(name, aDef, memberOf)
  except ValueError:
    pass
  raise ValueError('Unknown Fortran type %s.' % aDef['typespec'])

def isFixedSizeArray(tt):
  try:
    map(int, tt['dimension'])
    return True
  except ValueError:
    return False

def isFixedSizeCharacter(tt, csts = {}):
  if 'charselector' not in tt:
    return 'dimension' not in tt
  size = tt['charselector'].get('*', (tt['charselector']['len']) if 'len' in tt['charselector'] else '')
  try:
    if size in csts:
      return True
    size = int(size)
    return True
  except ValueError:
    return False

def toCamelCase(lbl):
  comp = lbl.split('_')
  return ''.join(c.title() if c[0].islower() else c for c in comp)

class BindArg(object):
  def __init__(self, name, aDef, memberOf):
    self.block = aDef
    self.name = name
    self._memberOf = memberOf
    self._classname = None
    self.index = aDef['index'] if 'index' in aDef else None
    self._cbefore = None
    self._cafter = None
    self._cppbefore = None
    self._cppafter = None
    self._fafter = None

  def dims(self):
    if 'dimension' in self.block:
      return len(self.block['dimension'])
    if 'character' in self.block['typespec']:
      return 1
    return 0

  def ftype(self):
    return self._ftype

  def before(self, useCType):
    return self._cbefore if useCType else self._cppbefore

  def after(self, useCType):
    return self._cafter if useCType else self._cppafter

  def fafter(self):
    return self._fafter

  def toDecl(self, useCType):
    prefix = "%s::" % self._classname if self._classname is not None and not useCType else ''
    if 'in' in self.block.get('intent', ['inout', ]):
      prefix = 'const ' + prefix
    return prefix + self._ctype + ('*' if not self.block.get('byValue', False) else '')

  def toCall(self, useCType = True):
    index = '[%d]' % self.index if self.index is not None else ''
    call = (self._ccall if useCType else self._cppcall) + index
    if 'optionalDepends' in self.block:
      call = '%s ? %s : NULL' % (self.block['optionalDepends'], call)
    return call

  def toFDecl(self):
    if self.index is not None:
      return '%s_%d' % (self.name, self.index)
    else:
      return self.name

  def toFDefinition(self):
    if 'byValue' in self.block:
      return ''
    out = self._ftype
    for a in self.block.get('attrspec', []):
      out += ', %s' % a
    if 'optionalDepends' in self.block:
      out += ', optional'
    if 'intent' in self.block:
      out += ', intent(%s)' % self.block['intent'][0]
    if self.index is not None:
      out += ' :: %s_%d\n' % (self.name, self.index)
    else:
      out += ' :: %s\n' % self.name
    return out

class BindReturnArg(BindArg):
  def __init__(self, name, aDef, memberOf = None):
    super(BindReturnArg, self).__init__(name, aDef, memberOf)
    self._ccall = '&' + self.name    
    self._cppcall = '&' + self.name    

  def toReturnType(self, useCType):
    return self.name

  def toInit(self):
    out = ''
    if 'pointer' in self.block.get('attrspec', []):
      out += '  nullify(%s)\n' % self.name
      out += '  allocate(%s)\n' % self.name
    return out

class BindTypeArg(BindArg):
  def __init__(self, name, aDef, memberOf = None):
    super(BindTypeArg, self).__init__(name, aDef, memberOf)

    if aDef['typespec'] != 'type':
      raise ValueError

    if 'dimension' in aDef:
      raise NotImplementedError('Cannot bind array of types.')

    pt = '_pointer' if 'pointer' in aDef.get('attrspec', []) else ''
    self._ctype = 'f90_' + aDef['typename'] + pt
    self._classname = toCamelCase(aDef['typename']) if aDef['typename'] != 'dictionary' else None

    self._ccall = self.name
    if 'pointer' in aDef.get('attrspec', []) and 'optional' not in aDef.get('attrspec', []):
      self._ccall = '&' + self.name
    self._cppcall = self.name if self._memberOf is None else '*this'
    if 'optional' in aDef.get('attrspec', []):
      const = 'const ' if 'in' in aDef.get('intent', ['inout']) else ''
      ns = (self._classname + '::') if self._classname is not None else ''
      cast = '(' + const + ns + self._ctype + '*)'
      self._cppcall = self.name + ' ? ' + cast + '*' + self.name + ' : NULL'
    self._ftype = 'type(%s)' % aDef['typename']

class BindReturnTypeArg(BindReturnArg, BindTypeArg):
  def __init__(self, name, aDef, memberOf = None):
    tt = aDef.copy()
    if 'attrspec' in tt and 'pointer' not in tt['attrspec']:
      tt['attrspec'].append('pointer')
    elif 'attrspec' not in tt:
      tt['attrspec'] = ['pointer', ]
      
    super(BindReturnTypeArg, self).__init__(name, tt, memberOf)

    # Cannot return a type, so work on pointers instead
    self._ctype = 'f90_' + aDef['typename'] + '_pointer'
    self._cppcall = '&%s' % self.name if memberOf is None else '*this'

    # Declare local variables for the return.
    self._cbefore = '%s %s' % (self._ctype, name)
    self._cppbefore = '%s::%s %s' % (self._classname, self._ctype, name) if memberOf is None else None

  def toReturnType(self, useCType):
    return self.name if useCType else '%s(%s)' % (self._classname, self.name)

class BindStringArg(BindArg):
  def __init__(self, name, aDef, memberOf = None):
    super(BindStringArg, self).__init__(name, aDef, memberOf)

    if aDef['typespec'] != 'character':
      raise ValueError

    self._ctype = 'char'

    ln = aDef.get('charselector', {}).get('*', aDef.get('charselector', {}).get('len', '1'))
    self._ccall = name
    if 'dimension' not in self.block and isFixedSizeCharacter(aDef) and ln == '1' and 'optional' not in aDef.get('attrspec', []):
      self._ccall = '&' + self.name
    if isFixedSizeCharacter(aDef) and 'optional' in aDef.get('attrspec', []):
      self._ccall = '%s ? *%s : NULL' % (name, name)
    self._cppcall = self._ccall
    if 'dimension' in aDef:
      self._ftype = 'character, dimension(%s_len)' % name
      self._cbefore = 'size_t %s_chk_len, %s_len = %s_chk_len = %s ? strlen(%s) : 0' % (name, name, name, name, name)
    elif ln == '1':
      self._ftype = 'character'
      self._cbefore = 'size_t %s_chk_len = 1' % name
    elif isFixedSizeCharacter(aDef):
      self._ftype = 'character(len = %s)' % ln
      self._cbefore = 'size_t %s_chk_len = %s' % (name, ln)
    elif '*' in ln or '(' in ln:
      self._ftype = 'character(len = %s_len)' % name
      if 'in' in aDef.get('intent', ['inout', ]):
        self._cbefore = 'size_t %s_chk_len, %s_len = %s_chk_len = %s ? strlen(%s) : 0' % (name, name, name, name, name)
      else:
        self._cbefore = 'size_t %s_chk_len = %s_len' % (name, name)
    else:
      self._ftype = 'character(len = %s)' % ln
      self._cbefore = 'size_t %s_chk_len = %s ? strlen(%s) : 0' % (name, name, name)
      if 'in' in aDef.get('intent', ['inout', ]):
        self._cbefore += ';\n  int %s = %s ? strlen(%s) : 0' % (ln, name, name)
    self._cppbefore = self._cbefore
    
class BindReturnStringArg(BindReturnArg, BindStringArg):
  def __init__(self, name, aDef, memberOf = None):
    super(BindReturnStringArg, self).__init__(name, aDef, memberOf)

    self._cbefore = 'char %s' % name
    self._cppbefore = self._cbefore

class BindNumericArg(BindArg):
  def __init__(self, name, aDef, memberOf = None):
    super(BindNumericArg, self).__init__(name, aDef, memberOf)

    self._ctype = toNumericType(aDef)

    if 'pointer' in aDef.get('attrspec', []):
      raise ValueError

    self._ccall = self.name
    if 'dimension' not in aDef and not self.name.endswith('_add') and ('in' in aDef.get('intent', ['inout']) or 'index' in aDef) and 'optional' not in aDef.get('attrspec', []) and not aDef.get('byValue', False):
      self._ccall = '&' + self.name
    elif 'dimension' in aDef and isFixedSizeArray(aDef):
      for d in aDef['dimension'][:-1]:
        self._ccall += '[0]'
      if 'optional' in aDef.get('attrspec', []):
        self._ccall = '%s ? *%s : NULL' % (name, self._ccall)
    self._cppcall = self._ccall
    if 'dimension' not in aDef and not self.name.endswith('_add') and 'in' not in aDef.get('intent', ['inout']) and 'optional' not in aDef.get('attrspec', []) and not aDef.get('byValue', False):
      self._cppcall = '&' + self.name

    if self._ctype == 'bool':
      self._ctype = 'int'
      self._cbefore = 'int %s_conv' % name
      if 'in' in aDef.get('intent', []):
        if 'optional' in aDef.get('attrspec', []):
          self._cbefore += ' = %s ? *%s : 0' % (name, name)
        else:
          self._cbefore += ' = %s' % name
      self._cppbefore = self._cbefore
      if 'optional' in aDef.get('attrspec', []):
        self._ccall = ' %s ? &%s_conv : NULL' % (name, name)
      else:
        self._ccall = '&%s_conv' % name
      self._cppcall = self._ccall
      if 'in' not in aDef.get('intent', []):
        self._cafter = 'if (%s) *%s = %s_conv' % (name, name, name)
        if 'optional' in aDef.get('attrspec', []):
          self._cppafter = self._cafter
        else:
          self._cppafter = '%s = %s_conv' % (name, name)

    self._ftype = aDef['typespec']
    if 'kindselector' in aDef:
      self._ftype += '(kind = %s)' % aDef['kindselector']['kind']
    if 'dimension' in aDef:
      self._ftype += ', dimension('
      self._ftype += aDef['dimension'][0] if aDef['dimension'][0] != ':' else ('%s_dim_0' % name)
      for i, d in enumerate(aDef['dimension'][1:]):
        self._ftype += ', &\n '
        self._ftype += d if d != ':' else ('%s_dim_%d' % (name, i + 1))
      self._ftype += ')'
    
class BindReturnNumericArg(BindReturnArg, BindNumericArg):
  def __init__(self, name, aDef, memberOf = None):
    super(BindReturnNumericArg, self).__init__(name, aDef, memberOf)

    self._cbefore = '%s %s' % (self._ctype, name)
    self._cppbefore = self._cbefore
    self._cafter = None
    self._cppafter = None
    
class BindNumericPtrArg(BindArg):
  def __init__(self, name, aDef, memberOf = None):
    super(BindNumericPtrArg, self).__init__(name, aDef, memberOf)

    ctype = toNumericType(aDef)
    ln = len(aDef['dimension'])
    self._ctype = 'f90_%s' % toNumericPtrType(aDef)
    self._cpptype = toCamelCase(toNumericPtrType(aDef))
    self._classname = self._cpptype

    self._cbefore = "size_t %s_len[%d]" % (name, ln)
    self._cppbefore = '%s::%s %s_ptr; ' % (self._classname, self._ctype, name) + self._cbefore

    if 'optional' in aDef.get('attrspec', []):
      self._ccall = '%s ? &%s->ptr : NULL' % (name, name)
    else:
      self._ccall = '&%s.ptr' % name
    self._cppcall = "&%s_ptr" % self.name
    if 'optional' in aDef.get('attrspec', []):
      self._cppcall = '%s ? %s : NULL' % (name, self._cppcall)

    if 'optional' in aDef.get('attrspec', []):
      self._cafter = 'if (%s) {' % name
      for i, dim in enumerate(aDef['dimension']):
        dims = '[%d]' % i if ln > 1 else ''
        self._cafter += '; %s->len%s = %s_len[%d]' % (name, dims, name, i)
      self._cafter += ';}'
    else:
      self._cafter = ''
      for i, dim in enumerate(aDef['dimension']):
        dims = '[%d]' % i if ln > 1 else ''
        self._cafter += '; %s.len%s = %s_len[%d]' % (name, dims, name, i)
    dims = '[0]' if ln == 1 else ''
    self._cppafter = '%s = %s(%s_ptr, %s_len%s)' % (name, self._cpptype, name, name, dims)
    if 'optional' in aDef.get('attrspec', []):
      self._cppafter = 'if (%s) *%s' % (name, self._cppafter)

    self._ftype = aDef['typespec']
    if 'kindselector' in aDef:
      self._ftype += '(kind = %s)' % aDef['kindselector']['kind']
    self._ftype += ', dimension(:'
    for d in aDef['dimension'][1:]:
      self._ftype += ', :'
    self._ftype += ')'
    self._fafter = ''
    buf = ''
    if 'optional' in aDef.get('attrspec', []):
      self._fafter += 'if (present(%s)) then' % name
      buf = '  '
    for i, d in enumerate(aDef['dimension']):
      self._fafter += '\n  %s%s_len_%d = size(%s, %d)' % (buf, name, i, name, i + 1)
    if 'optional' in aDef.get('attrspec', []):
      self._fafter += '\n  end if'
    
class CArg(object):
  def __init__(self, name, aDef):
    if 'external' in aDef.get('attrspec', []):
      raise NotImplementedError('external argument')

    self.block = aDef.copy()
    # Strangeness from crackfrotran
    if '=' in aDef and aDef['='].startswith('shape'):
      self.block['attrspec'].remove('optional')
    self.name = name
    self._dims = ''
    self._size = 0
    self.depends = []
    self.shadows = []

  def ctype(self):
    return self._ctype

  def size(self):
    return self._size

  def toDecl(self, useCType = True, declareOptional = False):
    optional = 'optional' in self.block.get('attrspec', [])
    default = self.block.get('default', '')
    lbl = ('(*' + self.name + ')') if optional else self.name
    value = ''
    if declareOptional:
      if 'default' in self.block:
        value = ' = ' + self.block['default']
      elif optional:
        value = ' = nullptr'
    return self.toType(useCType) + ' ' + lbl + self._dims + value

  def toType(self, useCType = True):
    return (self._ctype if useCType else self._cpptype)

class CReturnArg(object):
  def __init__(self, aDef, **kw):
    super(CReturnArg, self).__init__(kw.pop('name'), aDef, **kw)
    if 'dimension' in aDef and not isFixedSizeArray(aDef):
      raise NotImplementedError('dynamic array as a return value.')
    self.isReturnable = 'dimension' not in aDef

class CTypeArg(CArg):
  def __init__(self, name, aDef, db, **kw):
    super(CTypeArg, self).__init__(name, aDef, **kw)

    if aDef['typespec'] != 'type':
      raise ValueError

    if 'dimension' in aDef and len(aDef['dimension']) > 1:
      raise NotImplementedError('Cannot bind multi-dimensional array of types.')
    if 'dimension' in aDef and not isFixedSizeArray(aDef):
      raise NotImplementedError('Cannot bind dynamic array of types.')

    const = 'const ' if 'in' in aDef.get('intent', ['inout', ]) else ''
    if 'pointer' in aDef.get('attrspec', []):
      self._ctype = 'f90_' + aDef['typename'] + '_pointer'
      if 'optional' in aDef.get('attrspec', []):
        self._ctype = const + self._ctype
    else:
      self._ctype = const + 'f90_' + aDef['typename'] + '*'

    self._cpptype = toCamelCase(aDef['typename'])
    ns = '::'.join(db[self._cpptype]['namespaces']) if self._cpptype in db else ''
    if len(ns) > 0:
      ns += '::'
    self._cpptype = const + ns + self._cpptype
    if 'optional' not in aDef.get('attrspec', []) and 'dimension' not in aDef:
      self._cpptype += '&'

    if 'dimension' in aDef:
      self._dims = '[%s]' % aDef['dimension'][0]

class CTypeReturnArg(CReturnArg, CTypeArg):
  def __init__(self, name, aDef, db, **kw):
    super(CTypeReturnArg, self).__init__(name = name, aDef = aDef, db = db)

    if 'dimension' in aDef:
      raise NotImplementedError('Cannot return an array of types.')

    aDef['attrspec'] = aDef.get('attrspec', [])
    if 'pointer' not in aDef['attrspec']:
      aDef['attrspec'].append('pointer')
    self._ctype = 'f90_' + aDef['typename'] + '_pointer'
    self._cpptype = toCamelCase(aDef['typename'])
    ns = '::'.join(db[self._cpptype]['namespaces']) if self._cpptype in db else ''
    if len(ns) > 0:
      ns += '::'
    self._cpptype = ns + self._cpptype

class CStringArg(CArg):
  def __init__(self, name, aDef, args, csts, **kw):
    aDef = aDef.copy()
    ln = aDef.get('charselector', {}).get('*', aDef.get('charselector', {}).get('len', '1'))
    if ln in csts:
      aDef['charselector']['len'] = csts[ln]
    super(CStringArg, self).__init__(name, aDef, **kw)

    if aDef['typespec'] != 'character':
      raise ValueError

    pt = '*' if 'optional' not in aDef.get('attrspec', []) else ''
    const = 'const ' if 'in' in aDef.get('intent', ['inout', ]) else ''
    if 'dimension' in aDef and ln != '1':
      raise NotImplementedError('Array of strings')
    if 'dimension' in aDef and len(aDef['dimension']) > 1:
      raise NotImplementedError('Multi-dimensional array of characters')

    self._dims = ''
    self.isFixedSize = isFixedSizeCharacter(aDef, csts)
    if self.isFixedSize:
      if ln != '1':
        self._dims = '[%s]' % (ln if ln not in csts else csts[ln])
        self._ctype = const + 'char'
      elif 'dimension' in aDef and isFixedSizeArray(aDef):
        self._dims = '[%s]' % aDef['dimension'][0]
        self._ctype = const + 'char'
      elif 'dimension' in aDef:
        self._ctype = const + 'char' + pt
        self.isFixedSize = False
      else:
        self._ctype = 'char'
        if 'optional' in aDef.get('attrspec', []):
          self._ctype = const + self._ctype
    elif ln in args:
      self._ctype = const + 'char' + pt
      self.depends.append(ln)
      if 'in' in aDef.get('intent', ['inout', ]):
        self.shadows.append(ln)
    else:
      self._ctype = const + 'char' + pt
      if 'in' not in aDef.get('intent', ['inout', ]):
        self.depends.append(name + '_len')
        self._size = 1
    self._cpptype = self._ctype

  def argSize(self, i):
    return self.name + '_len'

class CStringReturnArg(CReturnArg, CStringArg):
  def __init__(self, name, aDef, args, csts):
    super(CStringReturnArg, self).__init__(name = name, aDef = aDef, args = args, csts = csts)

    if not self.isFixedSize:
      raise NotImplementedError('Dynamic string as return value')
    self.isReturnable = self._ctype == 'char' and self._dims == ''

class CNumericArg(CArg):
  def __init__(self, name, aDef, args, csts, **kw):
    super(CNumericArg, self).__init__(name, aDef, **kw)

    self._ctype = toNumericType(aDef)

    if 'pointer' in aDef.get('attrspec', []):
      raise ValueError
    if self._ctype == 'bool' and ('dimension' in aDef or name.endswith('_add')):
      raise NotImplementedError('Cannot convert array of bool')

    const = 'const ' if 'in' in aDef.get('intent', ['inout', ]) else ''
    self._dims = ''
    self._size = 0
    self._cpptype = None
    if 'dimension' in aDef or self.name.endswith('_add'):
      self._ctype = const + self._ctype
      if self.name.endswith('_add'):
        if 'optional' not in aDef.get('attrspec', []):
          self._ctype += '*'
        # Size will be provided already in Fortran API
      elif isFixedSizeArray(aDef):
        for dim in aDef['dimension']:
          self._dims += '[%s]' % dim
      else:
        if 'optional' not in aDef.get('attrspec', []):
          self._ctype += '*'
        if any(dim == ':' for dim in aDef['dimension']):
          self._size = len(aDef['dimension'])
        elif aDef['dimension'][0] == '*' or ('depend' in aDef and len(aDef['depend']) > 0) or any(dim.split('%')[0] in args for dim in aDef['dimension']):
          pass
        else:
          raise NotImplementedError("Unexpected dimension " + ', '.join(aDef['dimension']))
    elif self._ctype == 'void*':
      self._ctype = const + self._ctype
    elif 'optional' in aDef.get('attrspec', []):
      self._ctype = const + self._ctype
    elif 'in' not in aDef.get('intent', ['inout', ]):
      self._cpptype = self._ctype + '&'
      self._ctype += '*'
    if self._cpptype is None:
      self._cpptype = self._ctype

  def argSize(self, i):
    return self.name + '_dim_%d' % i

class CNumericReturnArg(CReturnArg, CNumericArg):
  def __init__(self, name, aDef, args, csts):
    super(CNumericReturnArg, self).__init__(name = name, aDef = aDef, args = args, csts = csts)

    if self._size > 0:
      raise NotImplementedError('Dynamic arrays as return value')
    self.isReturnable = 'dimension' not in self.block
    if self._ctype.endswith('*'):
      self._ctype = self._ctype[:-1]
      self._cpptype = self._cpptype[:-1]

class CNumericPtrArg(CArg):
  def __init__(self, name, aDef, db, **kw):
    super(CNumericPtrArg, self).__init__(name, aDef, **kw)

    ctype = toNumericType(aDef)

    if 'pointer' not in aDef.get('attrspec', []):
      raise ValueError
    if 'dimension' not in aDef:
      raise NotImplementedError('Pointer on a ' + aDef['typespec'])
    if any(dim != ":" for dim in aDef['dimension']):
      raise NotImplementedError('Pointer on a fixed size array')
    if ctype == 'bool':
      raise NotImplementedError('Cannot convert array of bool')

    self._dims = ''
    self._size = 0
    self._ctype = toNumericPtrType(aDef)
    self._cpptype = toCamelCase(self._ctype)
    ns = '::'.join(db[self._cpptype]['namespaces']) if self._cpptype in db else ''
    if len(ns) > 0:
      ns += '::'
    self._cpptype = ns + self._cpptype

  def outDims(self):
    return len(self.block['dimension'])

class BindType(object):
  def __init__(self, aDef = {'name': 'null'}):
    self.block = aDef
    self.name = aDef['name']
    self.classname = toCamelCase(self.name)

  def addEmptyConstructor(self):
    return {'block': 'function', 'name': 'f90_%s_empty' % self.name, \
            'args': [], \
            'result': 'self', \
            'vars': {'self': \
                       {'typespec': 'type', 'typename': self.name, 'attrspec': ['pointer', ]}}, \
            'fbody': ''
            }

  def addDefaultConstructor(self):
    members = self.block['vars'].copy()
    # Quick and dirty fix a bug in crackfortran for => null() association
    members.pop('null', None)
    args = sorted(members.keys(), key = lambda a: a if 'optional' in members[a].get('attrspec', []) else '_' + a)
    members['self'] = {'typespec': 'type', 'typename': self.name, 'attrspec': ['pointer', ]}
    fbody = ''
    for a in args:
      if 'pointer' in members[a].get('attrspec', []) and 'dimension' in members[a]:
        members[a]['attrspec'].remove('pointer')
      if 'pointer' not in members[a].get('attrspec', []):
        members[a]["intent"] = ["in", ]
      assign = "=>" if 'pointer' in members[a].get('attrspec', []) else "="
      if 'optional' in members[a].get('attrspec', []):
        fbody += '  if (present(%s)) out_self%%%s %s %s\n' % (a, a, assign, a)
      else:
        fbody += '  out_self%%%s %s %s\n' % (a, assign, a)
    return {'block': 'function', 'name': 'f90_%s_type_new' % self.name, \
            'args': args, \
            'result': 'self', \
            'vars': members, \
            'fbody': fbody \
            }

  def addDestructor(self):
    return {'block': 'subroutine', 'name': 'f90_%s_free' % self.name, \
            'args': ['self', ], \
            'vars': {'self': \
                       {'typespec': 'type', 'typename': self.name, 'attrspec': ['pointer', ]}}, \
            'fbody': '  deallocate(self)\n  nullify(self)\n'
            }

  def addCopyConstructor(self):
    return {'block': 'function', 'name': 'f90_%s_copy_constructor' % self.name, \
            'args': ['other',], \
            'result': 'self', \
            'vars': {'self': \
                       {'typespec': 'type', 'typename': self.name, 'attrspec': ['pointer', ]}, \
                     'other': \
                       {'typespec': 'type', 'typename': self.name, 'intent': ['in', ]}, \
                       }, \
            'fbody': '  out_self = other\n'
            }

  def isNull(self):
    return self.name == 'null'

  def toDecl(self):
    return 'F_DEFINE_TYPE(%s);\n' % self.name

  def toConversions(self, out):
    out.write('  operator f90_%s*() { return F_TYPE(%s); };\n' % (self.name, 'm' + self.classname))
    out.write('  operator const f90_%s*() const { return F_TYPE(%s); };\n' % (self.name, 'm' + self.classname))
    out.write('  operator f90_%s_pointer*() { return &%s; };\n' % (self.name, 'm' + self.classname))
    out.write('  %s(f90_%s_pointer other) : %s(other) {};\n' % (self.classname, self.name, 'm' + self.classname))
    return out

  def match(self, tt):
    return tt['typespec'] == 'type' and tt['typename'] == self.name

class BindFunc(object):
  def __init__(self, block, generic = None, types = [], csts = [], db = {}):
    self.block = block
    self.memberOf = None
    self.constructorOf = None
    self.destructorOf = None
    self.generic = generic if generic is not None else block['name']
    self.fbody = block.get('fbody', None)
    self.args = []
    self.retArg = None
    self.isValid = False
    try:
      self.__analyseBlock(types, csts)
      self.isValid = True
    except NotImplementedError as e:
      print '%s error:' % block['name'], e

  def __analyseBlock(self, types, csts):
    # List of C / Fortran arguments
    for a in self.block['args']:
      arg = toCArg(a, self.block['vars'][a], self.block['args'], csts, db)
      self.args.append(arg)
      # Add a size argument of every assume size array
      for i in range(arg.size()):
        tt = {'typespec': 'integer', 'intent': ['in', ], 'kindselector': {'kind': 'f_long'}}
        if 'optional' in arg.block.get('attrspec', []):
          tt['default'] = '0'
        self.args.append(toCArg(arg.argSize(i), tt))
    shadows = {k for a in self.args for k in a.shadows}
    if len(shadows) > 0:
      self.args = [a for a in self.args if a.name not in shadows]
    if len(self.args) > 0:
      self.memberOf = next((t for t in types if self.args[0].block['typespec'] == 'type' and self.args[0].block['typename'] == t.name), None)
      self.destructorOf = self.memberOf if self.memberOf is not None and len(self.args) == 1 and self.retArg is None and 'inout' in self.args[0].block.get('intent', ['inout', ]) and self.block['name'].endswith('_free') else None
    # Return argument if any
    retName = self.block.get('result', self.block['name']) if self.block['block'] == 'function' else None
    if retName is not None:
      self.retArg = toCReturnArg(retName, self.block['vars'][retName], self.block['args'], csts, db)
      if not self.retArg.isReturnable:
        # Convert returned values as out argument.
        tt = self.block['vars'][retName].copy()
        tt['intent'] = ['out', ]
        self.args.insert(0 if self.memberOf is None else 1, toCReturnArg('out_%s' % retName, tt, self.block['args'], csts))
        self.retArg = None
    if self.retArg is not None and not self.generic.startswith('operator('):
      self.constructorOf = next((t for t in types if self.retArg.block['typespec'] == 'type' and self.retArg.block['typename'] == t.name), None)
      if self.constructorOf is not None:
        self.memberOf = self.constructorOf
    if self.constructorOf is not None:
      self.generic = self.constructorOf.classname
    
    # List of argument names that are Fortran character
    strArgs = [a for a in self.args if isinstance(a, CStringArg)]
    # List of C / Fortran arguments for the binding routine
    self.bindArgs = []
    self.expandedArgs = []
    isMember = self.constructorOf is None and self.memberOf is not None and len(self.args) > 0 and self.memberOf.match(self.args[0].block)
    if self.retArg is not None:
      # Transform retArg into an out argument of the bind subroutine
      self.bindArgs.append(toBindReturnArg('out_%s' % self.retArg.name, self.retArg.block, self.memberOf if not isMember else None))
    st = 0
    if self.constructorOf is None and self.memberOf is not None and len(self.args) > 0 and self.memberOf.match(self.args[0].block):
      self.bindArgs.append(toBindArg(self.args[0].name, self.args[0].block, self.memberOf))
      st = 1
    for a in self.args[st:]:
      if isinstance(a, CTypeArg) and 'dimension' in a.block:
        self.expandedArgs.append(a)
        tt = a.block.copy()
        tt.pop('dimension')
        # Expanded fixed-size type array into references on type
        for i in range(int(a.block['dimension'][0])):
          tt['index'] = i
          self.bindArgs.append(toBindArg(a.name, tt, None))
      else:
        self.bindArgs.append(toBindArg(a.name, a.block, None))
        if isinstance(a, CStringArg) and not a.isFixedSize:
          # Append string length
          if len(a.shadows) > 0:
            for d in a.shadows:
              self.bindArgs.append(toBindArg(d, self.block['vars'][d], None))
          if len(a.depends) == 0:
            self.bindArgs.append(toBindArg('%s_len' % a.name, {'typespec': 'integer', 'intent': ['in', ], 'kindselector': {'kind': 'f_long'}}, None))
        elif isinstance(a, CNumericPtrArg):
          # Append allocated dimenions
          for i in range(a.outDims()):
            optDep = a.name if 'optional' in a.block.get('attrspec', []) else None
            self.bindArgs.append(toBindArg('%s_len' % a.name, {'typespec': 'integer', 'intent': ['out', ], 'kindselector': {'kind': 'f_long'}, 'optionalDepends': optDep, 'index': i}, None))
    for a in strArgs:
      # GFortran is adding a by-value size for bounds-check of strings
      self.bindArgs.append(toBindArg('%s_chk_len' % a.name, {'typespec': 'integer', 'kindselector': {'kind': 'f_long'}, 'byValue': True}, None))
    if len(strArgs) > 0 or any(a.size() > 0 for a in self.args):
      if 'use' not in self.block:
        self.block['use'] = {}
      self.block['use']['f_precisions'] = {'map': {'f_long': 'f_long'}}

  def __name(self, useCType, stripList = None):
    if not useCType and self.constructorOf is not None:
      return self.constructorOf.classname
    elif not useCType and self.destructorOf is not None:
      return '~' + self.destructorOf.classname
    elif not useCType:
      if self.generic.startswith('operator('):
        op = self.generic[9:].split(')')[0].strip()
        if any([a.block['typespec'] == 'type' for a in self.args]) and not op.startswith('.') and op != '//' and op != '**':
          if op == '/=':
            out = 'operator !='
          else:
            out = 'operator ' + op
        else:
          out = self.block['name']
      else:
        out = self.generic
    else:
      out = self.block['name']
    if useCType:
      return out
    for prefix in ([self.memberOf.name] + stripList if self.memberOf is not None else stripList):
      if out.lower().startswith(prefix.lower() + '_'):
        return out[len(prefix) + 1:]
    return out

  def __toDecl(self, out, useCType, useClassname = False, declareOptional = False, stripList = []):
    if useCType or (self.constructorOf is None and self.destructorOf is None):
      out.write(self.retArg.toType(useCType) if self.retArg is not None else 'void')
      out.write(' ')
    if useClassname:
      if self.memberOf is not None:
        out.write(self.memberOf.classname + '::')
      else:
        for ns in stripList:
          out.write(ns + '::')
    out.write(self.__name(useCType, stripList) + '(')
    st = 0 if useCType or self.memberOf is None or self.constructorOf is not None else 1
    out.write(self.args[st].toDecl(useCType, declareOptional) if len(self.args) > st else 'void')
    for a in self.args[st + 1:]:
      out.write(",\n  ")
      if not useCType:
        out.write('  ')
      out.write(a.toDecl(useCType, declareOptional))
    out.write(')')
    if not useCType and self.memberOf is not None and self.constructorOf is None and 'in' in self.args[0].block.get('intent', []):
      out.write(' const')

  def __toFCFunc(self):
    return 'FC_FUNC%s(bind_%s, BIND_%s)' % ("_" if "_" in self.block['name'] else "", self.block['name'].lower(), self.block['name'].upper())

  def __toBindCDecl(self, out, useCType):
    out.write('(')
    if len(self.bindArgs) > 0:
      out.write(self.bindArgs[0].toDecl(useCType))
    else:
      out.write('void')
    for a in self.bindArgs[1:]:
      out.write(',\n  ')
      out.write(a.toDecl(useCType))
    out.write(')')

  def __callBindCFunc(self, out, useCType):
    out.write('  %s\n' % self.__toFCFunc())
    out.write('    (')
    if len(self.bindArgs) > 0:
      out.write(self.bindArgs[0].toCall(useCType))
    for a in self.bindArgs[1:]:
      out.write(', ')
      out.write(a.toCall(useCType))
    out.write(');\n')
    
  def toHeader(self, out):
    if not self.isValid:
      out.write('/* ')
    self.__toDecl(out, True)
    out.write(';')
    if not self.isValid:
      out.write(' */')
    out.write('\n\n')

  def toClass(self, out, useClassname = False, namespaces = []):
    if not self.isValid:
      out.write('/* ')
    if not useClassname and self.memberOf is not None:
      out.write('  ')
    self.__toDecl(out, False, useClassname, True, stripList = namespaces)
    out.write(';')
    if not self.isValid:
      out.write(' */')
    out.write('\n\n')

  def toBindCDecl(self, out, useCType = True):
    if not self.isValid:
      return
    out.write('void %s' % self.__toFCFunc())
    self.__toBindCDecl(out, useCType)
    out.write(';\n')

  def toImpl(self, out, useCType = True, namespaces = []):
    if not self.isValid:
      return
    self.__toDecl(out, useCType, not useCType, stripList = namespaces)
    out.write('\n{\n')

    # Add some local variables, if needed
    for b in [a.before(useCType) for a in self.bindArgs if a.before(useCType) is not None]:
      out.write('  ' + b + ';\n')
    self.__callBindCFunc(out, useCType)
    for a in [a.after(useCType) for a in self.bindArgs if a.after(useCType) is not None]:
      out.write('  ' + a + ';\n')
    if len(self.bindArgs) > 0 and isinstance(self.bindArgs[0], BindReturnArg):
      if useCType or self.constructorOf is None:
        out.write('  return %s;\n' % self.bindArgs[0].toReturnType(useCType))
    out.write('}\n\n')

  def toBind(self, out, m, uses):
    if not self.isValid:
      return
    out.write('subroutine bind_%s( &\n    ' % self.block['name'])
    if len(self.bindArgs) > 0:
      out.write(self.bindArgs[0].toFDecl())
    for a in self.bindArgs[1:]:
      if 'byValue' not in a.block:
        out.write(', &\n    ')
        out.write(a.toFDecl())
    out.write(')\n')
    uses_ = uses.copy()
    for k, v in self.block.get('use', {}).items():
      if 'map' in v and k in uses_ and 'map' in uses_[k]:
        uses_[k]['map'].update(v['map'])
      elif k in uses_:
        uses_[k].update(v)
      else:
        uses_[k] = v
    for (u, a) in uses_.items():
      out.write('  use %s' % u)
      if 'map' in a:
        out.write(', ')
        if a.get('only', 0) == 1:
          out.write('only: ')
        it = a['map'].items()
        if len(it) > 0:
          out.write('%s' % it[0][0])
          if it[0][0] != it[0][1]:
            out.write('=>%s' % it[0][1])
        for fr, to in it[1:]:
          out.write(', &\n    %s' % fr)
          if fr != to:
            out.write('=>%s' % to)
      out.write('\n')
    out.write('  use %s\n' % m)
    out.write('  implicit none\n')
    for a in sorted([a for a in self.bindArgs if 'byValue' not in a.block], key = BindArg.dims):
      out.write('  ' + a.toFDefinition())
    for a in self.expandedArgs:
      out.write('  type(%s), dimension(%s) :: %s\n' % (a.block['typename'], a.block['dimension'][0], a.name))
    out.write('\n')
    for a in [a for a in self.bindArgs if a.index is not None and a.name in self.block['args'] \
                and ('in' in a.block.get('intent', ['inout', ]) or \
                       'inout' in a.block.get('intent', ['inout', ]))]:
      out.write('  %s(%d) = %s_%d\n' % (a.name, a.index + 1, a.name, a.index))
    if len(self.bindArgs) > 0 and isinstance(self.bindArgs[0], BindReturnArg) and 'pointer' in self.bindArgs[0].block.get('attrspec', []):
      out.write(self.bindArgs[0].toInit())

    fname = self.generic if self.constructorOf is None else self.block['name']
    if self.fbody is None and fname.startswith('operator') and len(self.block['args']) == 2 and 'result' in self.block:
      op = fname[9:].split(')')[0].strip()
      out.write('  %s = (%s %s %s)\n' % (self.bindArgs[0].name, self.block['args'][0], op, self.block['args'][1]))
    elif self.fbody is None and fname.startswith('assignment'):
      for a in self.args:
        if 'out' in a.block['intent'] or 'inout' in a.block['intent']:
          varTo = a.name
        if 'in' in a.block['intent'] and '_dim_' not in a.name:
          varFrom = a.name
      out.write('  %s = %s\n' % (varTo, varFrom))
    elif self.fbody is None:
      if len(self.bindArgs) > 0 and isinstance(self.bindArgs[0], BindReturnArg):
        out.write('  %s = %s( &\n' % (self.bindArgs[0].name, fname))
      elif len(self.args) > 0 and isinstance(self.args[0], CReturnArg):
        out.write('  %s = %s( &\n' % (self.args[0].name, fname))
      elif self.memberOf is not None and len(self.args) > 1 and isinstance(self.args[1], CReturnArg):
        out.write('  %s = %s( &\n' % (self.args[1].name, fname))
      else:
        out.write('  call %s( &\n' % fname)
      for a in self.block['args'][:-1]:
        out.write('    %s, &\n' % a)
      if len(self.block['args']) > 0:
        out.write('    %s' % self.block['args'][-1])
      out.write(')\n')
    else:
      out.write(self.fbody)
    for a in [a for a in self.bindArgs if a.index is not None and a.name in self.block['args'] \
                and ('out' in a.block.get('intent', ['inout', ]) or \
                       'inout' in a.block.get('intent', ['inout', ]))]:
      out.write('  %s_%d = %s(%d)\n' % (a.name, a.index, a.name, a.index + 1))
    for f in [a.fafter() for a in self.bindArgs if a.fafter() is not None]:
      out.write('  %s\n' % f)
    out.write('end subroutine bind_%s\n\n' % self.block['name'])

  def nArgs(self, ignoreOptional = True):
    if ignoreOptional:
      return len([a for a in self.args if 'optional' not in a.block.get('attrspec', [])])
    else:
      return len(self.args)

  def match(self, func):
    if not self.isValid or not func.isValid:
      return False
    if self.nArgs() != func.nArgs():
      return False
    if self.generic != func.generic:
      return False
    for a1, a2 in zip(self.args, func.args):
      if a1.ctype() != a2.ctype():
        return False
    return True
    
class BindModule(object):
  def __init__(self, tt, namespace = None, db = {}):
    self.block = tt
    
    if tt['block'] != 'module':
      raise ValueError('Not a module file')

    self.namespaces = map(toCamelCase, namespace.split('::')) if namespace is not None else []
    self.name = tt['name']
    self.classname = toCamelCase(self.name)
    self.header = self.name.lower() + '.h'
    self.cimpl = self.name.lower() + '_c.c'
    self.cppimpl = self.name.lower() + '_cpp.cpp'
    self.fimpl = self.name.lower() + '_bind.f90'

    self.types = [BindType(b) for b in self.__publicBlocks('type')]
    self.csts = {k: b['='] for k, b in self.block['vars'].items() if 'typespec' in b and b['typespec'] == 'integer' and '=' in b}
    self.__db = {}
    for t, aDef in db.items():
      tt = aDef.copy()
      if 'namespaces' in tt:
        for ns in self.namespaces:
          if len(tt['namespaces']) > 0 and ns == tt['namespaces'][0]:
            tt['namespaces'].pop()
          else:
            break
      self.__db[t] = tt

    self.funcs = [BindFunc(b, types = self.types, csts = self.csts, db = self.__db) for b in self.__publicBlocks('function')] \
      + [BindFunc(b, types = self.types, csts = self.csts, db = self.__db) for b in self.__publicBlocks('subroutine')] \
      + [BindFunc(b, i, types = self.types, csts = self.csts, db = self.__db) for (i, b) in self.__publicInterfaces()]

    for k, b in self.__publicEnums():
      aDef = {'block': 'function', 'name': k, \
                'args': [], \
                'result': 'self', \
                'vars': {'self': b.copy()}}
      aDef['vars']['self']['attrspec'] = ['pointer', ]
      aDef['fbody'] = '  out_self = %s\n' % k
      self.funcs.append(BindFunc(aDef, types = self.types, csts = self.csts))

    for t in self.types:
      try:
        next(f for f in self.funcs if f.constructorOf == t and len(f.args) == 0)
      except StopIteration:
        self.funcs.insert(0, BindFunc(t.addEmptyConstructor(), types = [t, ]))
      try:
        next(f for f in self.funcs if f.destructorOf == t)
      except StopIteration:
        self.funcs.insert(0, BindFunc(t.addDestructor(), types = [t, ]))
      self.funcs.insert(0, BindFunc(t.addDefaultConstructor(), types = [t, ], csts = self.csts))
      self.funcs.insert(0, BindFunc(t.addCopyConstructor(), types = [t, ]))
    
  def __publicBlocks(self, kind):
    interfaces = []
    if kind != 'interface':
      # Some funny people like to name some routines like the interface itself
      interfaces = [i['name'] for i in self.__publicBlocks('interface')]
    return [b for b in self.block['body'] if b['block'] == kind and b['name'] in self.block['vars'] and 'public' in self.block['vars'][b['name']]['attrspec'] and b['name'] not in interfaces]

  def __publicInterfaces(self):
    return [(b['name'], i) for b in self.__publicBlocks('interface') for i in self.block['body'] if i['name'] in b['implementedby'] and (i['block'] == 'function' or i['block'] == 'subroutine')]

  def __publicEnums(self):
    return [(k, b) for k, b in self.block['vars'].items() if 'typename' in b and b['typename'] == 'f_enumerator' and 'public' in b['attrspec']]

  def __publicUsedTypes(self):
    local = [b['name'] for b in self.__publicBlocks('type')]
    return {v['typename'] for t in self.__publicBlocks('type') for v in t['vars'].values() if v['typespec'] == 'type' and v['typename'] not in local} \
      | {v['typename'] for f in self.__publicBlocks('function') for a in f['args'] for k, v in f['vars'].items() if k == a and v['typespec'] == 'type' and v['typename'] not in local} \
      | {v['typename'] for f in self.__publicBlocks('subroutine') for a in f['args'] for k, v in f['vars'].items() if k == a and v['typespec'] == 'type' and v['typename'] not in local} \
      | {v['typename'] for i, f in self.__publicInterfaces() for a in f['args'] for k, v in f['vars'].items() if k == a and v.get('typespec', []) == 'type' and v['typename'] not in local} \
      | {toNumericPtrType(v) for f in self.__publicBlocks('subroutine') for a in f['args'] for k, v in f['vars'].items() if k == a and (v['typespec'] == 'integer' or v['typespec'] == 'real') and 'pointer' in v.get('attrspec', []) and 'dimension' in v}

  def __filterDup(self, funcs, drops = None):
    outf = []
    for f in funcs:
      if not any([o.match(f) for o in outf]):
        outf.append(f)
      else:
        m = next((o for o in outf if o.match(f)))
        if f.nArgs(ignoreOptional = False) > m.nArgs(ignoreOptional = False):
          outf.append(f)
          outf.remove(m)
          if drops is not None:
            drops.append(m)
        elif drops is not None:
            drops.append(f)
    return outf

  def toIncludes(self, out, useCType):
    out.write('#include "futile_cst.h"\n')
    if useCType:
      out.write('#include <stdbool.h>\n')
    for t in self.__publicUsedTypes():
      tname = t if useCType else toCamelCase(t)
      if tname in self.__db:
        out.write('#include "%s"\n' % self.__db[tname]['file'])
      else:
        out.write('#include "%s"\n' % ('%s.h' % t if useCType else tname))
    out.write('\n')

  def toHeader(self):
    with open(self.header, 'w') as header:
      header.write('#ifndef %s_H\n' % self.name.upper())
      header.write('#define %s_H\n' % self.name.upper())
      header.write('\n')

      self.toIncludes(header, True)

      for b in self.types:
        header.write(b.toDecl())
      header.write('\n')

      for b in self.funcs:
        b.toHeader(header)

      header.write('#endif\n')

  def toClass(self):
    with open(self.classname, 'w') as header:
      header.write('#ifndef %s_H\n' % self.name.upper())
      header.write('#define %s_H\n' % self.name.upper())
      header.write('\n')

      self.toIncludes(header, False)

      for n in self.namespaces:
        header.write('namespace %s {\n\n' % n)

      for b in self.types:
        header.write('class %s\n{\n' % b.classname)

        header.write('public:\n\n')
        header.write('  ' + b.toDecl())
        b.toConversions(header)
        header.write('\n')

        drops = []
        for f in self.__filterDup([f for f in self.funcs if f.memberOf == b], drops):
          f.toClass(header, namespaces = self.namespaces)
        for f in drops:
          print f.block['name'], ": dropping"

        header.write('private:\n')
        header.write('  f90_%s_pointer m%s;\n' % (b.name, b.classname))
        header.write('\n')

        header.write('};\n\n')

      drops = []
      for f in self.__filterDup([f for f in self.funcs if f.memberOf is None], drops):
        f.toClass(header, namespaces = self.namespaces)
      for f in drops:
        print f.block['name'], ": dropping"

      for n in self.namespaces:
        header.write('\n}\n')

      header.write('#endif\n')

  def toCImpl(self):
    with open(self.cimpl, 'w') as cimpl:
      cimpl.write('#include "%s"\n' % self.header)
      cimpl.write('#include <config.h>\n')
      cimpl.write('#include <string.h>\n\n')

      for b in self.funcs:
        b.toBindCDecl(cimpl)
        b.toImpl(cimpl)

  def toClassImpl(self):
    with open(self.cppimpl, 'w') as cppimpl:
      cppimpl.write('#include "%s"\n' % self.classname)
      cppimpl.write('#include <config.h>\n')
      cppimpl.write('#include <string.h>\n\n')

      for ns in {'::'.join(tt['namespaces']) for tt in self.__db.values() if 'namespaces' in tt and len(tt['namespaces']) > 0}:
        cppimpl.write('using namespace %s;\n\n' % ns)
      
      for i in range(len(self.namespaces)):
        cppimpl.write('using namespace %s;\n\n' % '::'.join(self.namespaces[:i + 1]))

      cppimpl.write('extern "C" {\n')
      for f in self.funcs:
        f.toBindCDecl(cppimpl, False)
      cppimpl.write('}\n\n')

      for f in self.__filterDup([f for f in self.funcs if f.memberOf is None]):
        f.toImpl(cppimpl, False, self.namespaces)

      for b in self.types:
        for f in self.__filterDup([f for f in self.funcs if f.memberOf == b]):
          f.toImpl(cppimpl, False, self.namespaces)

  def toBind(self):
    with open(self.fimpl, 'w') as fimpl:
      for b in self.funcs:
        b.toBind(fimpl, self.name, self.block.get('use', {}))

  def toExport(self):
    tp = {}
    for t in self.types:
      tp[t.name] = {'file': self.header}
      tp[t.classname] = {'file': self.classname, 'namespaces': self.namespaces}
    return tp

def cli():
  parser = OptionParser("usage: fbinder.py [options] file")
  parser.add_option("-n", "--namespace", dest = "namespace", type = "string", default = None,
                    help="provide a namespace for C++ export (like Futile::Yaml).")
  parser.add_option("-i", "--include-paths", dest = "incs", type = "string", default = None,
                    help="a path to a YAML file with include definitions.")
  return parser

(options, args) = cli().parse_args()

db = {}
db['dictionary'] = {'file': 'dict.h'}
db['int_1d_pointer'] = {'file': 'dynamic_memory.h'}
db['double_2d_pointer'] = {'file': 'dynamic_memory.h'}
db['Dictionary'] = {'file': 'Dict', 'namespaces': ['Futile', ]}
db['Int1dPointer'] = {'file': 'DynamicMemory', 'namespaces': ['Futile', ]}
db['Double2dPointer'] = {'file': 'DynamicMemory', 'namespaces': ['Futile', ]}
if options.incs is not None:
  for p in options.incs.split(':'):
    if os.path.isfile(p):
      db.update(yaml.load(open(p, 'r')))
if os.path.isfile('exports'):
  db.update(yaml.load(open('exports', 'r')))

module = BindModule(crackfortran.crackfortran(args[0])[0], options.namespace, db)

module.toHeader()
module.toCImpl()
module.toClass()
module.toClassImpl()
module.toBind()

db = yaml.load(open('exports', 'r')) if os.path.isfile('exports') else {}
db.update(module.toExport())
yaml.dump(db, open('exports', 'w'))
