import re


FUNC_RE = re.compile(r'^(?P<rtype>[^(]+ \**)(?P<name>[^(]+)\((?P<args>.*)\);$')
ARG_RE = re.compile(r'^(?P<type>.* \**)(?P<name>[^ *]+)$')


def splitfunc(func):
    return FUNC_RE.match(func).groupdict()


def mktypedef(funcparts):
    if funcparts['args'].endswith('...'):
        return ''
    return 'typedef %(rtype)s(*t_%(name)s_cb)(%(args)s);' % funcparts


def mkcargs(args):
    if args == 'void':
        return ''
    return ', '.join(ARG_RE.match(arg.strip()).groupdict()['name']
                     for arg in args.split(','))


def mkhookfunc(funcparts):
    parts = funcparts.copy()
    parts['ret'] = 'return ' if parts['rtype'] != 'void ' else ''
    if parts['args'].endswith('...'):
        # Special case variadics, because we can't do anything with them.
        return ('%(rtype)s%(name)s(%(args)s) '
                '{ %(ret)s 0; }') % parts
    parts['cargs'] = mkcargs(parts['args'])
    return ('%(rtype)s%(name)s(%(args)s) '
            '{ %(ret)s%(name)s_cb(%(cargs)s); }') % parts


def mkpyfunc(funcparts):
    parts = funcparts.copy()
    if parts['args'].endswith('...'):
        # Special case variadics, because we can't do anything with them.
        return '    # Variadic function %(name)s ignored.\n' % parts
    parts['cargs'] = mkcargs(parts['args'])
    if parts['cargs']:
        parts['cargs'] = ', ' + parts['cargs']
    return ('    def %(name)s(self%(cargs)s):\n'
            '        raise NotImplementedError("%(name)s")\n') % parts


def mkcbvar(funcparts):
    if funcparts['args'].endswith('...'):
        return ''
    return 't_%(name)s_cb %(name)s_cb;' % funcparts


def mksetup(splitfuncs, with_body=True):
    front = 'void setup_callbacks('
    argsep = ',\n' + (' ' * len(front))
    args = argsep.join('t_%(name)s_cb %(name)s' % sf for sf in splitfuncs
                       if not sf['args'].endswith('...'))

    body = '\n    '.join('%(name)s_cb = %(name)s;' % sf
                         for sf in splitfuncs
                         if not sf['args'].endswith('...'))
    return '%s%s) {\n    %s\n}' % (front, args, body)


def mkcallsetupdecl(splitfuncs, with_body=True):
    front = 'void call_setup_callbacks('
    argsep = ',\n' + (' ' * len(front))
    args = argsep.join('t_%(name)s_cb %(name)s' % sf for sf in splitfuncs
                       if not sf['args'].endswith('...'))

    return '%s%s);' % (front, args)


def mksetuptypedef(splitfuncs, with_body=True):
    front = 'typedef void (*setup_callbacks_t)('
    argsep = ',\n' + (' ' * len(front))
    args = argsep.join('t_%(name)s_cb' % sf for sf in splitfuncs
                       if not sf['args'].endswith('...'))
    return '%s%s);' % (front, args)


def mkcallsetup(splitfuncs, with_body=True):
    front = 'void call_setup_callbacks('
    argsep = ',\n' + (' ' * len(front))
    args = argsep.join('t_%(name)s_cb %(name)s' % sf for sf in splitfuncs
                       if not sf['args'].endswith('...'))

    sfront = '    setup_callbacks_func('
    argsep = ',\n' + (' ' * len(sfront))
    sargs = argsep.join('%(name)s' % sf for sf in splitfuncs
                        if not sf['args'].endswith('...'))
    return '%s%s) {\n%s%s);\n}' % (front, args, sfront, sargs)


def mkpysetup(splitfuncs):
    front = ('    def setup_callbacks(self):\n'
             '        self._callbacks = [\n            ')
    back = ('\n        ]\n')
    argsep = '\n            '
    body = argsep.join("self._mkcallback('%(name)s')," % sf
                       for sf in splitfuncs
                       if not sf['args'].endswith('...'))

    return "%s%s%s" % (front, body, back)


funcs = [line.strip() for line in open('api_functions.h').readlines()
         if line.strip().endswith(';')]
splitfuncs = [splitfunc(func) for func in funcs]


typedefs = '\n'.join(mktypedef(sf) for sf in splitfuncs)
cbvars = '\n'.join(mkcbvar(sf) for sf in splitfuncs)
hookfuncs = '\n'.join(mkhookfunc(sf) for sf in splitfuncs)
setuptypedef = mksetuptypedef(splitfuncs)
callsetup = mkcallsetup(splitfuncs)
callsetupdecl = mkcallsetupdecl(splitfuncs)
setupfunc = mksetup(splitfuncs)
pyfuncs = '\n'.join(mkpyfunc(sf) for sf in splitfuncs)
pysetupfunc = mkpysetup(splitfuncs)


with open('claypellet_harness.h', 'w') as fo:
    fo.write(
        "/***************************************************************\n"
        " * This file is automatically generated.\n"
        " * It gets included in a call to `FFI.cdef()', so it needs to be\n"
        " * parseable by that.\n"
        " ***************************************************************/\n")
    fo.write('\n\n')
    fo.write(typedefs)
    fo.write('\n\n')
    fo.write(setuptypedef)
    fo.write('\n\n')
    fo.write(callsetupdecl)
    fo.write('\n\n')
    fo.write(open('claypellet_extra.h').read())

with open('claypellet_harness_gen.c', 'w') as fo:
    fo.write(
        "/***************************************************************\n"
        " * This file is automatically generated.\n"
        " ***************************************************************/\n")
    fo.write('\n\n')
    fo.write(callsetup)
    fo.write('\n\n')

with open('claypellet.h', 'w') as fo:
    fo.write(
        "/***************************************************************\n"
        " * This file is automatically generated.\n"
        " ***************************************************************/\n")
    fo.write('\n\n')
    fo.write(typedefs)
    fo.write('\n\n')
    fo.write(open('claypellet_extra.h').read())

with open('claypellet.c', 'w') as fo:
    fo.write(
        "/***************************************************************\n"
        " * This file is automatically generated.\n"
        " ***************************************************************/\n"
        "\n"
        '#include "pebble_os.h"\n'
        '#include "pebble_app.h"\n'
        '#include "pebble_fonts.h"\n'
        "\n"
        "// Undefine some macros so our typedefs don't get confused.\n"
        "#undef GPoint(x, y)\n"
        "#undef GSize(w, h)\n"
        "#undef GRect(x, y, w, h)\n"
        "\n"
        '#include "claypellet.h"\n'
        "\n")
    fo.write('\n\n')
    fo.write(cbvars)
    fo.write('\n\n')
    fo.write(setupfunc)
    fo.write('\n\n')
    fo.write(hookfuncs)
    fo.write('\n\n')

with open('harness_hooks.py', 'w') as fo:
    fo.write(
        "#################################################################\n"
        "# This file is automatically generated.\n"
        "#################################################################\n"
        "\n\n"
        "class PebbleHarnessBase(object):\n")
    fo.write(pysetupfunc)
    fo.write('\n')
    fo.write(pyfuncs)

print "done"
