#include <leek-extension.h>

#include <lauxlib.h>
#include <lua.h>

#include <stddef.h>
#include <stdlib.h>
#include <string.h>

/**
 * The folder functions leave one value on the Lua stack. This cleans that up on
 * the next call. Leaving the value on the Lua stack allows use-after-return, so
 * no copies are necessary.
 */
static void leek_extension_lua_clean(lua_State* lua_state) {
    lua_pop(lua_state, 1);
}

void* leek_extension_folder_initialize(uint8_t const* name,
                                       size_t  name_length,
                                       uint8_t const** error_message,
                                       size_t* error_message_length) {
    lua_State* lua_state = luaL_newstate();
    if (lua_state == NULL) goto error;

    int load_status = luaL_loadbuffer(lua_state, name, name_length, "");
    if (load_status != 0) goto error;

    int initialize_status = lua_pcall(lua_state, 0, 0, 0);
    if (initialize_status != 0) goto error;

    lua_pushnil(lua_state);
    return lua_state;

 error:
    *error_message = "Could not initialize Lua folder";
    *error_message_length = strlen(*error_message);
    if (lua_state != NULL) lua_close(lua_state);
    return NULL;
}

int leek_extension_folder_step(void*   accumulator,
                               uint8_t const* entry,
                               size_t  entry_length,
                               uint8_t const** error_message,
                               size_t* error_message_length) {
    lua_State* lua_state = accumulator;
    leek_extension_lua_clean(lua_state);

    lua_getglobal(lua_state, "step");
    lua_pushlstring(lua_state, entry, entry_length);
    int status = lua_pcall(lua_state, 1, 0, 0);
    if (status != 0) goto error;

    lua_pushnil(lua_state);
    return 0;

 error:
    *error_message = lua_tolstring(lua_state, -1, error_message_length);
    return 1;
}

int leek_extension_folder_finalize(void*   accumulator,
                                   uint8_t const** value,
                                   size_t* value_length,
                                   uint8_t const** error_message,
                                   size_t* error_message_length) {
    lua_State* lua_state = accumulator;
    leek_extension_lua_clean(lua_state);

    lua_getglobal(lua_state, "finalize");
    int status = lua_pcall(lua_state, 0, 1, 0);
    if (status != 0) goto error;

    *value = lua_tolstring(lua_state, -1, value_length);
    if (*value == NULL) goto error;

    return 0;

 error:
    *error_message = lua_tolstring(lua_state, -1, error_message_length);
    return 1;
}

void leek_extension_folder_free(void* accumulator) {
    lua_close(accumulator);
}
