// Tutorial: Static Native Modules from C (StbImage)
//
// This tutorial demonstrates the initialization sequence used by hosts which
// statically link additional daScript modules:
//
//   1. Register the built-in modules.
//   2. Register application modules through unmangled C entry points.
//   3. Finalize module dependencies.
//
// It also uses the explicit-length C API throughout. This maps directly to
// languages such as D, where strings are pointer/length slices and need not be
// null-terminated.

#include <stdio.h>

#include "daScript/daScriptC.h"

// These functions are generated by REGISTER_MODULE_IN_NAMESPACE and exported
// with C linkage. A D host can declare the same functions as `extern(C)`.
extern das_module * register_Module_Raster(void);
extern das_module * register_Module_StbImage(void);

static const char SCRIPT_NAME[] = "stbimage_from_c.das";
static const char BOOST_MODULE[] = "stbimage/stbimage_boost";
static const char MAIN_FUNCTION[] = "main";

// make_image exercises the StbImage high-level module. Image.resize calls the
// native raster resize implementation, so the script verifies both statically
// registered modules rather than merely checking that their names exist.
static const char SCRIPT_SOURCE[] =
    "options gen2\n"
    "require stbimage/stbimage_boost\n"
    "\n"
    "[export]\n"
    "def main : int {\n"
    "    var image = make_image(2, 2, 4)\n"
    "    var resized <- image.resize(1, 1)\n"
    "    return resized.width * 100 + resized.height * 10 + resized.channels\n"
    "}\n";

static void print_errors(das_program * program, das_text_writer * output) {
    int count = das_program_err_count(program);
    for (int index = 0; index != count; ++index) {
        das_error_output(das_program_get_error(program, index), output);
    }
}

int main(void) {
    int exit_code = 1;
    das_text_writer * output = NULL;
    das_module_group * modules = NULL;
    das_file_access * files = NULL;
    das_program * program = NULL;
    das_context * context = NULL;

    das_initialize_modules();
    if (!register_Module_Raster() || !register_Module_StbImage()) {
        fprintf(stderr, "failed to register the static StbImage modules\n");
        goto shutdown;
    }
    das_initialize_finalize();

    output = das_text_make_printer();
    modules = das_modulegroup_make();
    files = das_fileaccess_make_default();

    // Make the high-level .das wrapper visible to this FileAccess. The native
    // Raster and StbImage implementations were registered above.
    if (!das_fileaccess_introduce_native_module_n(
            files, BOOST_MODULE, sizeof(BOOST_MODULE) - 1)) {
        fprintf(stderr, "stbimage_boost.das was not found\n");
        goto cleanup;
    }

    das_fileaccess_introduce_file_n(
        files,
        SCRIPT_NAME, sizeof(SCRIPT_NAME) - 1,
        SCRIPT_SOURCE, sizeof(SCRIPT_SOURCE) - 1,
        0);

    program = das_program_compile_n(
        SCRIPT_NAME, sizeof(SCRIPT_NAME) - 1,
        files, output, modules);
    if (das_program_err_count(program)) {
        print_errors(program, output);
        goto cleanup;
    }

    context = das_context_make(das_program_context_stack_size(program));
    if (!das_program_simulate(program, context, output)) {
        fprintf(stderr, "simulation failed\n");
        goto cleanup;
    }

    {
        das_function * main_function = das_context_find_function_n(
            context, MAIN_FUNCTION, sizeof(MAIN_FUNCTION) - 1);
        vec4f result;
        const char * exception;
        int value;

        if (!main_function) {
            fprintf(stderr, "exported main function was not found\n");
            goto cleanup;
        }

        result = das_context_eval_with_catch(context, main_function, NULL);
        exception = das_context_get_exception(context);
        if (exception) {
            fprintf(stderr, "script exception: %s\n", exception);
            goto cleanup;
        }

        value = das_argument_int(result);
        if (value != 114) {
            fprintf(stderr, "unexpected StbImage result: %d\n", value);
            goto cleanup;
        }
        printf("static StbImage integration succeeded: %d\n", value);
    }

    exit_code = 0;

cleanup:
    if (context) das_context_release(context);
    if (program) das_program_release(program);
    if (files) das_fileaccess_release(files);
    if (modules) das_modulegroup_release(modules);
    if (output) das_text_release(output);
shutdown:
    das_shutdown();
    return exit_code;
}
