#! /usr/bin/env python

import os
import sys
from optparse import OptionParser
import re

def main():
    parser = OptionParser(usage="[options] filename")
    parser.add_option("-v", "--verbose",
            action="store_true", dest="verbose",
            default=False, help="Be verbose")

    options, args = parser.parse_args()
    if len(args) == 1:
        arg, = args
        parse_file(arg, options.verbose)
        return
    elif len(args) == 0:
        pass
    else:
        print "Too many arguments"
        sys.exit(1)
    parser.print_help()

def parse_file(filename, verbose=False):
    code = open(filename).read()
    name, ext = os.path.splitext(filename)
    basename = os.path.basename(name)
    pxd = """\
# This file is automatically generated by fparser from '%s'.
# Do not edit by hand (rerun fparser instead).

from libcpp cimport bool

cdef extern from "%s":

""" % (filename, basename + ".h")
    h = """\
// This file is automatically generated by fparser from '%(filename)s'.
// Do not edit by hand (rerun fparser instead).

#ifndef %(protection)s
#define %(protection)s

#include <stdbool.h>

#if defined (__cplusplus)
extern "C" {
#endif

""" % {
        "protection": "_" + basename + "_h",
        "filename": filename
    }
    for subroutine in find_subroutines(code):
        pxd += "    " + get_c_signature(subroutine) + "\n"
        h += get_c_signature(subroutine) + ";\n"

    h += """\

#if defined (__cplusplus)
}
#endif

#endif
"""

    pxd_filename = name + ".pxd"
    h_filename = name + ".h"
    open(pxd_filename, "w").write(pxd)
    open(h_filename, "w").write(h)
    if verbose:
        print "'%s' created." % pxd_filename
        print "'%s' created." % h_filename

def find_subroutines(code):
    s2 = 0
    while True:
        s1 = code.find("subroutine", s2)
        if s1 == -1:
            break
        s2 = code.find("end subroutine", s1) + 14
        yield code[s1:s2]

def get_c_signature(code):
    assert code[:10] == "subroutine"
    name = code[11:code.find("(")]
    params = get_vars(code[12 + len(name):code.find(")")])
    params = [get_param_type(param, code) + param for param in params]
    return "void " + name + "(" + ", ".join(params) + ")"

def get_vars(code):
    return [x.strip(" &").strip() for x in code.split(",")]

def get_param_type(param, code):
    for line in code.split("\n"):
        if line.find("intent") != -1:
            args = get_vars(line[line.find("::")+2:])
            args = [just_name(x) for x in args]
            args = [x.lower() for x in args]
            if param.lower() in args:
                if line.find("c_int") != -1:
                    return "int *"
                elif line.find("c_double") != -1:
                    return "double *"
                elif line.find("c_bool") != -1:
                    return "bool *"
                else:
                    print line
                    print param
                    raise Exception("Unkown type")
    print code
    print param
    raise Exception("Param not found")

def just_name(arg):
    i = arg.find("(")
    if i != -1:
        return arg[:i]
    return arg

if __name__ == "__main__":
    main()
