/*
 * autocompleters.c
 * vim: expandtab:ts=4:sts=4:sw=4
 *
 * Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
 *
 * SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
 */

#include "config.h"

#include <string.h>

#include <glib.h>

#include "tools/autocomplete.h"
#include "command/cmd_ac.h"

static GHashTable* plugin_to_acs;
static GHashTable* plugin_to_filepath_acs;

static void
_free_autocompleters(GHashTable* key_to_ac)
{
    g_hash_table_destroy(key_to_ac);
}

static void
_free_filepath_autocompleters(GHashTable* prefixes)
{
    g_hash_table_destroy(prefixes);
}

void
autocompleters_init(void)
{
    plugin_to_acs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)_free_autocompleters);
    plugin_to_filepath_acs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)_free_filepath_autocompleters);
}

void
autocompleters_add(const char* const plugin_name, const char* key, char** items)
{
    GHashTable* key_to_ac = g_hash_table_lookup(plugin_to_acs, plugin_name);
    if (key_to_ac) {
        if (g_hash_table_contains(key_to_ac, key)) {
            Autocomplete existing_ac = g_hash_table_lookup(key_to_ac, key);
            autocomplete_add_all(existing_ac, items);
        } else {
            Autocomplete new_ac = autocomplete_new();
            autocomplete_add_all(new_ac, items);
            g_hash_table_insert(key_to_ac, g_strdup(key), new_ac);
        }
    } else {
        key_to_ac = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)autocomplete_free);
        Autocomplete new_ac = autocomplete_new();
        autocomplete_add_all(new_ac, items);
        g_hash_table_insert(key_to_ac, g_strdup(key), new_ac);
        g_hash_table_insert(plugin_to_acs, g_strdup(plugin_name), key_to_ac);
    }
}

void
autocompleters_remove(const char* const plugin_name, const char* key, char** items)
{
    GHashTable* key_to_ac = g_hash_table_lookup(plugin_to_acs, plugin_name);
    if (!key_to_ac) {
        return;
    }

    if (!g_hash_table_contains(key_to_ac, key)) {
        return;
    }

    Autocomplete ac = g_hash_table_lookup(key_to_ac, key);
    autocomplete_remove_all(ac, items);
}

void
autocompleters_clear(const char* const plugin_name, const char* key)
{
    GHashTable* key_to_ac = g_hash_table_lookup(plugin_to_acs, plugin_name);
    if (!key_to_ac) {
        return;
    }

    if (!g_hash_table_contains(key_to_ac, key)) {
        return;
    }

    Autocomplete ac = g_hash_table_lookup(key_to_ac, key);
    autocomplete_clear(ac);
}

void
autocompleters_filepath_add(const char* const plugin_name, const char* prefix)
{
    GHashTable* prefixes = g_hash_table_lookup(plugin_to_filepath_acs, plugin_name);
    if (prefixes) {
        g_hash_table_add(prefixes, g_strdup(prefix));
    } else {
        prefixes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
        g_hash_table_add(prefixes, g_strdup(prefix));
        g_hash_table_insert(plugin_to_filepath_acs, g_strdup(plugin_name), prefixes);
    }
}

char*
autocompleters_complete(const char* const input, gboolean previous)
{
    char* result = NULL;

    GList* ac_hashes = g_hash_table_get_values(plugin_to_acs);
    GList* curr_hash = ac_hashes;
    while (curr_hash) {
        GHashTable* key_to_ac = curr_hash->data;

        GList* keys = g_hash_table_get_keys(key_to_ac);
        GList* curr = keys;
        while (curr) {
            result = autocomplete_param_with_ac(input, curr->data, g_hash_table_lookup(key_to_ac, curr->data), TRUE, previous);
            if (result) {
                g_list_free(ac_hashes);
                g_list_free(keys);
                return result;
            }
            curr = g_list_next(curr);
        }
        g_list_free(keys);

        curr_hash = g_list_next(curr_hash);
    }
    g_list_free(ac_hashes);

    GList* filepath_hashes = g_hash_table_get_values(plugin_to_filepath_acs);
    curr_hash = filepath_hashes;
    while (curr_hash) {
        GHashTable* prefixes_hash = curr_hash->data;
        GList* prefixes = g_hash_table_get_keys(prefixes_hash);
        GList* curr_prefix = prefixes;
        while (curr_prefix) {
            char* prefix = curr_prefix->data;
            if (g_str_has_prefix(input, prefix)) {
                result = cmd_ac_complete_filepath(input, prefix, previous);
                if (result) {
                    g_list_free(filepath_hashes);
                    g_list_free(prefixes);
                    return result;
                }
            }

            curr_prefix = g_list_next(curr_prefix);
        }
        g_list_free(prefixes);

        curr_hash = g_list_next(curr_hash);
    }
    g_list_free(filepath_hashes);

    return NULL;
}

void
autocompleters_reset(void)
{
    GList* ac_hashes = g_hash_table_get_values(plugin_to_acs);
    GList* curr_hash = ac_hashes;
    while (curr_hash) {
        GList* acs = g_hash_table_get_values(curr_hash->data);
        GList* curr = acs;
        while (curr) {
            autocomplete_reset(curr->data);
            curr = g_list_next(curr);
        }

        g_list_free(acs);
        curr_hash = g_list_next(curr_hash);
    }

    g_list_free(ac_hashes);
}

void
autocompleters_destroy(void)
{
    g_hash_table_destroy(plugin_to_acs);
    g_hash_table_destroy(plugin_to_filepath_acs);
    plugin_to_acs = NULL;
    plugin_to_filepath_acs = NULL;
}
