#
# This module is used to generate the header and pseudo-c-extension-module
# body for an extension module c-api library.
#
# See "Extending and Embedding the Python Interpeter",
#         "1.12 Providing a C API for an Extension Module"
#
# The purpose of a c-api pseudo-extension-module is to provide a platform
# independent mechanism for accessing c-api functions which are built in
# a shared library without polluting the global namespace.
#
# Towards this end, the c-api functions are compiled "static" but pointers
# to them are placed in a jump table.
#
# The header defines "proxy-macros" which provide a typechecked means of
# calling the API functions through the jump table.
#
# The header also provides an "import_module" macro which is used to initialize
# a pointer to the API jump table.  So, including the library header generates
# a single pointer of static data in the including file.  Calling the import
# macro fills in the pointer at runtime.
#
# The generated output includes header and an pseudo-c-extension body code.
# It is presumed that the API functions themselves already exist in external
# files which either have global linkage *or* are included directly into
# the pseudo-c-extension as extra_headers.
#
import imp

file, path, descr = imp.find_module("template", ["Lib/codegenerator"])
template = imp.load_module("template", file, path, descr)
# import template   # for doing <name> --> %(name)s transformations

PROTO = """
static <rval> <function> <proto>;
"""

PSEUDO = """
#define <function> (<module>_API ? (*(<rval> (*) <proto> ) <module>_API[ <num> ]) : (*(<rval> (*) <proto> ) <module>_FatalApiError))
"""

HDR_BODY = """
/*   W     W   AAA   RRRR   N   N  III  N   N   GGG   !!!
**   W     W  A   A  R   R  NN  N   I   NN  N  G   G  !!!
**   W  W  W  AAAAA  RRRR   N N N   I   N N N  G       !
**    W W W   A   A  R   R  N  NN   I   N  NN  G  GG
**     W W    A   A  R   R  N   N  III  N   N   GGG   !!!
**
** WARNING: This file is program generated by genapi.py.
**
** DO NOT EDIT THIS FILE! Any changes made to this file will be lost!
*/

#ifndef _<module>
#define _<module>

<headers>

#ifdef __cplusplus
extern "C" {
#endif

/* Header file for <module> */

#if !defined(_<module>_MODULE)

/*
Extensions constructed from seperate compilation units can access the
C-API defined here by defining "<module>_UNIQUE_SYMBOL" to a global
name unique to the extension.  Doing this circumvents the requirement
to import <module> into each compilation unit, but is nevertheless
mildly discouraged as "outside the Python norm" and potentially
leading to problems.  Looking around at "existing Python art", most
extension modules are monolithic C files, and likely for good reason.
*/

#if defined(<module>_UNIQUE_SYMBOL)
#define <module>_API <module>_UNIQUE_SYMBOL
#endif

/* C API address pointer */ 
#if defined(NO_IMPORT) || defined(NO_IMPORT_ARRAY)
extern void **<module>_API;
#else
#if defined(<module>_UNIQUE_SYMBOL)
void **<module>_API;
#else
static void **<module>_API;
#endif
#endif

#define _import_<module>()                                                  \\
      {                                                                     \\
        PyObject *module = PyImport_ImportModule("<qualified_module>");     \\
        if (module != NULL) {                                               \\
          PyObject *module_dict = PyModule_GetDict(module);                 \\
          PyObject *c_api_object =                                          \\
                 PyDict_GetItemString(module_dict, "_C_API");               \\
          if (c_api_object && PyCObject_Check(c_api_object)) {              \\
            <module>_API = (void **)PyCObject_AsVoidPtr(c_api_object);      \\
          } else {                                                          \\
            PyErr_Format(PyExc_ImportError,                                 \\
                         "Can't get API for module '<qualified_module>'");  \\
          }                                                                 \\
        }                                                                   \\
      }
      
#define import_<module>() _import_<module>(); if (PyErr_Occurred()) { PyErr_Print(); Py_FatalError("<qualified_module> failed to import... exiting.\\n"); }
      
#endif


#define <module>_FatalApiError (Py_FatalError("Call to API function without first calling import_<module>() in " __FILE__), NULL)
      

/* Macros defining components of function prototypes */

<macros>

#ifdef _<module>_MODULE
  /* This section is used when compiling <module> */

static PyObject *_Error;
  
<protos>
  
#else
  /* This section is used in modules that use <module> */
<pseudos>
#endif

  /* Total number of C API pointers */
#define <module>_API_pointers <num>

#ifdef __cplusplus
}
#endif

#endif /* !defined(_<module>) */
"""

def genhdr(module, qualified_module, file, header_includes):
    functions = open(file).readlines()
    headers = mkhdrs(header_includes)
    macros, protos, pseudos = "", "", ""
    num = 0
    fps = []
    for l in functions:
        rval, function, proto = l.rstrip().split("@")
        rval = rval.rstrip()
        function = function.rstrip()
        fname = module + "_" + function
        protos += PROTO % locals()
        pseudos += PSEUDO % locals()
        fps.append(function)
        num += 1
    hdr = open(module +".h","w")
    print >>hdr, HDR_BODY % locals()
    return fps

MOD_BODY = """
/*   W     W   AAA   RRRR   N   N  III  N   N   GGG   !!!
**   W     W  A   A  R   R  NN  N   I   NN  N  G   G  !!!
**   W  W  W  AAAAA  RRRR   N N N   I   N N N  G       !
**    W W W   A   A  R   R  N  NN   I   N  NN  G  GG
**     W W    A   A  R   R  N   N  III  N   N   GGG   !!!
**
** WARNING: This file is program generated by genapi.py.
**
** DO NOT EDIT THIS FILE! Any changes made to this file will be lost!
*/
#include <Python.h>

#define _<module>_MODULE

<body_code>

static PyObject *_Error;

void *<module>_API[] = {
<fps>
};

#if (!defined(METHOD_TABLE_EXISTS))
static PyMethodDef _<module>Methods[] = {
    {NULL,      NULL}        /* Sentinel */
};
#endif

/* platform independent*/
#ifdef MS_WIN32
__declspec(dllexport)
#endif

/* boiler plate API init */
void init<module>(void)
{
    PyObject *m = Py_InitModule("<module>", _<module>Methods);
    PyObject *c_api_object;

    _Error = PyErr_NewException("<qualified_module>.error", NULL, NULL);

    /* Create a CObject containing the API pointer array's address */
    c_api_object = PyCObject_FromVoidPtr((void *)<module>_API, NULL);

    if (c_api_object != NULL) {
      /* Create a name for this object in the module's namespace */
      PyObject *d = PyModule_GetDict(m);

      PyDict_SetItemString(d, "_C_API", c_api_object);
      PyDict_SetItemString(d, "error", _Error);
      Py_DECREF(c_api_object);
    } else {
        return;
    }
    ADD_VERSION(m);
    <module>_init();  /* module customized init */
}

"""

import re

def mkhdrs(hdr_list):
    headers = map(lambda x: '#include "%s"' % x, hdr_list)
    headers = "\n".join(headers)
    return headers

def bodystring(files):
    s = ""
    for filename in files:
        # Add the whole file but the emacs Local Variables section.
        # This assumes the Local Variables are in a single C-style comment,
        # each line starting with " *" (as opposed to each line beeing it's
        # own comment).
        s += re.compile("/\*.?\n.?\*.*Local Variables:.*\*/", re.DOTALL).sub("", open(filename).read()+"\n\n")
    return s

def genmod(module, qualified_module, fps, body_files):
    body_code = bodystring(body_files)
    fps  = "\t(void*)" + ",\n\t(void*)".join(fps)
    mod = open(module +".c","w")
    print >>mod, MOD_BODY % locals()

def main(module, functions, header_includes, body_files,
         qualified_module, extra_dependencies=[]):
    print "generating new API module '%s' .c  & .h" % module
    fps = genhdr(module, qualified_module, functions, header_includes)
    genmod(module, qualified_module, fps, body_files)

template.sugar_dict(globals())

