/*
    Copyright (C) 2011  ABRT team
    Copyright (C) 2010  RedHat Inc

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "event_config.h"
#include "workflow.h"
#include "internal_libreport.h"

struct workflow
{
    config_item_info_t *info;
    int priority; // direct correlation: higher number -> higher priority

    GList *events; //list of event_option_t
};


GHashTable *g_workflow_list;

workflow_t *new_workflow(const char *name)
{
    workflow_t *w = xzalloc(sizeof(*w));
    w->info = new_config_info(name);
    return w;
}

void free_workflow(workflow_t *w)
{
    if (!w)
        return;

    free_config_info(w->info);
    g_list_free_full(w->events, (GDestroyNotify)free_event_config);
    free(w);
}

void free_workflow_list(GHashTable **wl)
{
    if (*wl != NULL)
    {
        g_hash_table_destroy(*wl);
        *wl = NULL;
    }
}

workflow_t *get_workflow(const char *name)
{
    if (!g_workflow_list)
        return NULL;
    /* @@ FIXME: SYMLINKS@!!!
    if (g_event_config_symlinks)
    {
        char *link = g_hash_table_lookup(g_event_config_symlinks, name);
        if (link)
            name = link;
    }
    */
    return g_hash_table_lookup(g_workflow_list, name);
}

static gint file_obj_cmp(file_obj_t *file, const char *filename)
{
    gint cmp = strcmp(file->filename, filename);
    return cmp;
}

static void load_workflow_config(const char *name,
                           GList *available_wfs,
                           GHashTable *wf_list)
{
    GList *wf_file = g_list_find_custom(available_wfs, name, (GCompareFunc)file_obj_cmp);
    if (wf_file)
    {
        file_obj_t *file = (file_obj_t *)wf_file->data;
        workflow_t *workflow = new_workflow(file->filename);
        load_workflow_description_from_file(workflow, file->fullpath);
        log_info("Adding '%s' to workflows\n", file->filename);
        g_hash_table_insert(wf_list, xstrdup(file->filename), workflow);
    }
}

GHashTable *load_workflow_config_data_from_list(GList *wf_names,
                                                const char *path)
{
    GList *wfs = wf_names;
    GHashTable *wf_list = g_hash_table_new_full(
                         g_str_hash,
                         g_str_equal,
                         g_free,
                         (GDestroyNotify) free_workflow
        );

    if (path == NULL)
        path = WORKFLOWS_DIR;

    GList *workflow_files = get_file_list(path, "xml");
    while(wfs)
    {
        load_workflow_config((const char *)wfs->data, workflow_files, wf_list);
        wfs = g_list_next(wfs);
    }
    free_file_list(workflow_files);

    return wf_list;
}

GHashTable *load_workflow_config_data(const char *path)
{
    if (g_workflow_list)
        return g_workflow_list;

    if (g_workflow_list == NULL)
    {
        g_workflow_list = g_hash_table_new_full(
                                        g_str_hash,
                                        g_str_equal,
                                        g_free,
                                        (GDestroyNotify) free_workflow
        );
    }

    if (path == NULL)
        path = WORKFLOWS_DIR;

    GList *workflow_files = get_file_list(path, "xml");
    while (workflow_files)
    {
        file_obj_t *file = (file_obj_t *)workflow_files->data;

        workflow_t *workflow = get_workflow(file->filename);
        bool nw_workflow = (!workflow);
        if (nw_workflow)
            workflow = new_workflow(file->filename);

        load_workflow_description_from_file(workflow, file->fullpath);

        if (nw_workflow)
            g_hash_table_replace(g_workflow_list, xstrdup(wf_get_name(workflow)), workflow);

        free_file_obj(file);
        workflow_files = g_list_delete_link(workflow_files, workflow_files);
    }

    return g_workflow_list;
}

config_item_info_t *workflow_get_config_info(workflow_t *w)
{
    return w->info;
}

GList *wf_get_event_list(workflow_t *w)
{
    return w->events;
}

GList *wf_get_event_names(workflow_t *w)
{
    GList *wf_event_list = wf_get_event_list(w);
    GList *event_names = NULL;
    while(wf_event_list)
    {
        event_names = g_list_append(event_names, xstrdup(ec_get_name(wf_event_list->data)));
        wf_event_list = g_list_next(wf_event_list);
    }

    return event_names;
}

const char *wf_get_name(workflow_t *w)
{
    return ci_get_name(workflow_get_config_info(w));
}

const char *wf_get_screen_name(workflow_t *w)
{
    return ci_get_screen_name(workflow_get_config_info(w));
}

const char *wf_get_description(workflow_t *w)
{
    return ci_get_description(workflow_get_config_info(w));
}

const char *wf_get_long_desc(workflow_t *w)
{
    return ci_get_long_desc(workflow_get_config_info(w));
}

int wf_get_priority(workflow_t *w)
{
    return w->priority;
}

void wf_set_screen_name(workflow_t *w, const char* screen_name)
{
    ci_set_screen_name(workflow_get_config_info(w), screen_name);
}

void wf_set_description(workflow_t *w, const char* description)
{
    ci_set_description(workflow_get_config_info(w), description);
}

void wf_set_long_desc(workflow_t *w, const char* long_desc)
{
    ci_set_long_desc(workflow_get_config_info(w), long_desc);
}

void wf_add_event(workflow_t *w, event_config_t *ec)
{
    w->events = g_list_append(w->events, ec);
    log_info("added to ev list: '%s'", ec_get_screen_name(ec));
}

void wf_set_priority(workflow_t *w, int priority)
{
    w->priority = priority;
}

/*
 * Returns a negative integer if the first value comes before the second, 0 if
 * they are equal, or a positive integer if the first value comes after the
 * second.
 */
int wf_priority_compare(const workflow_t *first, const workflow_t *second)
{
    return second->priority - first->priority;
}
