#!/usr/bin/env python3

import sys
import cffi

builder = cffi.FFI()

with open(sys.argv[1]) as f:
    output = []
    eat_until_endif = 0
    for line in f:
        if line.startswith('#define'):
            continue
        elif line.endswith('ZOITECHAT_PLUGIN_H\n'):
            continue
        elif 'time.h' in line:
            output.append('typedef int... time_t;')
        elif line.startswith('#if'):
            eat_until_endif += 1
        elif line.startswith('#endif'):
            eat_until_endif -= 1
        elif eat_until_endif and '_zoitechat_context' not in line:
            continue
        else:
            output.append(line)
    builder.cdef(''.join(output))

builder.embedding_api('''
extern "Python" int _on_py_command(char **, char **, void *);
extern "Python" int _on_load_command(char **, char **, void *);
extern "Python" int _on_unload_command(char **, char **, void *);
extern "Python" int _on_reload_command(char **, char **, void *);
extern "Python" int _on_say_command(char **, char **, void *);

extern "Python" int _on_command_hook(char **, char **, void *);
extern "Python" int _on_print_hook(char **, void *);
extern "Python" int _on_print_attrs_hook(char **, zoitechat_event_attrs *, void *);
extern "Python" int _on_server_hook(char **, char **, void *);
extern "Python" int _on_server_attrs_hook(char **, char **, zoitechat_event_attrs *, void *);
extern "Python" int _on_timer_hook(void *);

extern "Python" int _on_plugin_init(char **, char **, char **, char *, char *);
extern "Python" int _on_plugin_deinit(void);

static zoitechat_plugin *ph;
''')

builder.set_source('_zoitechat_embedded', '''
/* Python's header defines these.. */
#undef HAVE_MEMRCHR
#undef HAVE_STRINGS_H

#include "config.h"
#include "zoitechat-plugin.h"

#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#endif

static zoitechat_plugin *ph;
CFFI_DLLEXPORT int _on_plugin_init(char **, char **, char **, char *, char *);
CFFI_DLLEXPORT int _on_plugin_deinit(void);

int zoitechat_plugin_init(zoitechat_plugin *plugin_handle,
                        char **name_out, char **description_out,
                        char **version_out, char *arg)
{
#ifdef _WIN32
    /* CPython cannot be removed from a process once Py_Initialize has run.
       If the embedded interpreter fails to start (e.g. _cffi_backend is
       missing), the host responds to our 0 return by g_module_close()ing
       this plugin, which would also drop the last reference to pythonXY.dll
       and unload it mid-flight; the leftover process-global state then
       crashes the process later, e.g. when a server connection spawns the
       next thread.  Pin this module so FreeLibrary can never unload it. */
    HMODULE self_handle;
    GetModuleHandleExW (GET_MODULE_HANDLE_EX_FLAG_PIN |
                        GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
                        (LPCWSTR)&zoitechat_plugin_init, &self_handle);
#endif

    if (ph != NULL)
    {
        puts ("Python plugin already loaded\\n");
        return 0; /* Prevent loading twice */
    }

    ph = plugin_handle;
    return _on_plugin_init(name_out, description_out, version_out, arg, ZOITECHATLIBDIR);
}

int zoitechat_plugin_deinit(void)
{
    int ret = _on_plugin_deinit();
    ph = NULL;
    return ret;
}
''')

with open(sys.argv[2]) as f:
    builder.embedding_init_code(f.read())

builder.emit_c_code(sys.argv[3])
