#!/usr/bin/env python

import argparse
import jinja2
import re
from yaml import load

split_re = re.compile(r'^(?P<type>.*?)\s*(?P<name>\w+)$')
env = jinja2.Environment(
    trim_blocks=True,
    lstrip_blocks=True,
    loader=jinja2.FileSystemLoader('template'),
)

def args(args, add_type=True):
    return ', '.join(
        '{} {}'.format(arg['type'], arg['name']) if add_type else arg['name']
        for arg in args
    )

f = '0.2f'
printf_lookup = {
    'GLbitfield': 'd',
    'GLboolean': 'd',
    'GLbyte': 'c',
    'GLubyte': 'c',
    'GLchar': 'c',
    'GLdouble': '0.2f',
    'GLenum': 'u',
    'GLfloat': '0.2f',
    'GLint': 'd',
    'GLintptr': 'd',
    'GLintptrARB': 'd',
    'GLshort': 'd',
    'GLsizei': 'd',
    'GLsizeiptr': 'd',
    'GLsizeiptrARB': 'd',
    'GLuint': 'u',
    'GLushort': 'u',
    'GLvoid': 'p',
}

def printf(args):
    if isinstance(args, dict):
        args = (args,)

    types = []
    for arg in args:
        typ = arg['type']
        if '*' in typ:
            t = 'p'
        else:
            t = printf_lookup.get(typ, 'p')

        types.append(t)

    return ', '.join('%' + t for t in types)

def unconst(s):
    split = s.split(' ')
    while 'const' in split:
        split.remove('const')
    return ' '.join(split)

env.filters['args'] = args
env.filters['printf'] = printf
env.filters['unconst'] = unconst

def split_arg(arg):
    match = split_re.match(arg)
    if match:
        return match.groupdict()
    else:
        return {'type': 'unknown', 'name': arg}

def gen(files, template, guard_name, headers,
        deep=False, cats=(), ifdef=None, ifndef=None):
    funcs = {}
    formats = []
    unique_formats = set()
    for data in files:
        if deep and not isinstance(data.values()[0], list):
            functions = []
            for cat, f in data.items():
                if not cats or cat in cats:
                    functions.extend(f.items())
        else:
            functions = data.items()

        for name, args in sorted(functions):
            props = {}
            if args:
                ret = args.pop(0)
            else:
                ret = 'void'

            args = [split_arg(arg) for arg in args if not arg == 'void']
            if any(arg.get('type') == 'unknown' for arg in args):
                continue

            if args:
                args[0]['first'] = True
                args[-1]['last'] = True

            for i, arg in enumerate(args):
                arg['index'] = i

            types = '_'.join(
                arg['type'].replace(' ', '_').replace('*', '__GENPT__')
                for arg in [{'type': ret}] + args)

            props.update({
                'return': ret,
                'name': name,
                'args': args,
                'types': types,
                'void': ret == 'void',
            })
            if not types in unique_formats:
                unique_formats.add(types)
                formats.append(props)

            funcs[name] = props

    context = {
        'functions': [i[1] for i in sorted(funcs.items())],
        'formats': formats,
        'headers': headers,
        'name': guard_name,
        'ifdef': ifdef,
        'ifndef': ifndef,
    }

    t = env.get_template(template)
    return t.render(**context).rstrip('\n')

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Generate code with yml/jinja.')
    parser.add_argument('yaml', help='spec files')
    parser.add_argument('template', help='jinja template to load')
    parser.add_argument('name', help='header guard name')
    parser.add_argument('headers', nargs='*', help='headers to include')
    parser.add_argument('--deep', help='nested definitions', action='store_true')
    parser.add_argument('--cats', help='deep category filter')
    parser.add_argument('--ifdef', help='wrap with ifdef')
    parser.add_argument('--ifndef', help='wrap with ifndef')

    args = parser.parse_args()

    files = []
    for name in args.yaml.split(','):
        with open(name) as f:
            data = load(f)
            if data:
                files.append(data)

    if args.cats:
        cats = args.cats.split(',')
    else:
        cats = None
    print gen(files, args.template, args.name,
              args.headers, args.deep, cats,
              args.ifdef, args.ifndef)

@@ -468,16 +468,19 @@
 tfield':
+   
  'd',%0A  
@@ -493,16 +493,20 @@
 oolean':
+    
  'd',%0A  
@@ -512,24 +512,31 @@
    'GLbyte':
+       
  'c',%0A    'G
@@ -543,16 +543,22 @@
 Lubyte':
+      
  'c',%0A  
@@ -568,16 +568,23 @@
 GLchar':
+       
  'c',%0A  
@@ -596,16 +596,21 @@
 double':
+     
  '0.2f',
@@ -624,18 +624,30 @@
 Lenum': 
-'u
+       '0x%2504X
 ',%0A    '
@@ -655,16 +655,22 @@
 Lfloat':
+      
  '0.2f',
@@ -682,16 +682,24 @@
 'GLint':
+        
  'd',%0A  
@@ -707,24 +707,29 @@
  'GLintptr':
+     
  'd',%0A    'G
@@ -732,32 +732,34 @@
   'GLintptrARB':
+  
  'd',%0A    'GLsho
@@ -762,16 +762,22 @@
 Lshort':
+      
  'd',%0A  
@@ -788,16 +788,22 @@
 Lsizei':
+      
  'd',%0A  
@@ -817,16 +817,19 @@
 zeiptr':
+   
  'd',%0A  
@@ -865,16 +865,23 @@
 GLuint':
+       
  'u',%0A  
@@ -893,16 +893,21 @@
 ushort':
+     
  'u',%0A  
@@ -917,16 +917,23 @@
 GLvoid':
+       
  'p',%0A%7D%0A
@@ -1169,16 +1169,64 @@
 p, 'p')%0A
+        if not '%25' in t:%0A            t = '%25' + t
 %0A       
@@ -1268,25 +1268,8 @@
 oin(
-'%25' + t for t in 
 type

improve spec printf types