/* ====================================================================
 * FTD4Linux Software License, Version 1.0
 *
 * Copyright (c) 2000-2004 The FTD4Linux Development Team.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        FTD4Linux Development Team (http://www.ftd4linux.2y.net/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "FTD" and "FTD4Linux" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact alguno@ftd4linux.2y.net.
 *
 * 5. Products derived from this software may not be called "FTD4Linux",
 *    nor may "FTD4Linux" appear in their name, without prior written
 *    permission of the FTD4Linux Development Team.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE FTD4LINUX DEVELOPMENT TEAM OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <glib.h>
#include "ftd4linux.h"
#include "ftd_app_core.h"
#include "ftd_groups.h"
#include "ftd_file.h"

/**
 * Maakt een nieuwe nieuwsgroep aan.
 *
 * @return  De nieuwe nieuwsgroep.
 */
FtdGroup *
ftd_group_new()
{
    FtdGroup *group;

    group = g_malloc0(sizeof(FtdGroup));

    return group;
}

/**
 * Geeft een nieuwsgroep vrij.
 *
 * @arg     group       De vrij te geven nieuwsgroep.
 */
void
ftd_group_free(FtdGroup *group)
{
    g_assert(group);
    ftd_string_free(group->groupName);
    g_free(group);
}

/**
 * Geeft alle geladen nieuwsgroepen vrij.
 * Deze functie wordt aangeroepen vanuit ::ftd_app_core_cleanup.
 */
void
ftd_group_free_all(void)
{
    FtdList *list;
    g_assert(core);

    list = core->groups;
    while (list) {
        FtdGroup *group = ftd_list_value(list);
        ftd_group_free(group);
        list = ftd_list_next(list);
    }

    ftd_list_free(core->groups);
    core->groups = NULL;
}

/**
 * Haalt de nieuwsgroepen op vanaf de FTD server.
 *
 * @arg     errmsg      Een eventuele foutmelding van de FTD servers komt hier in te staan.
 *
 * @return  Een fout-code die aangeeft of het ophalen van de groepen succesvol was.
 */
FtdErrorCode
ftd_group_fetch(char **errmsg)
{
    FtdErrorCode retval;
    FtdString *uitvoer;

    g_assert(core);

    retval = ftd_http_curl_execute(FTD_URL_GROUPS, NULL, TRUE, &uitvoer, TRUE, NULL, TRUE);
    if (retval != ERROR_NONE) {
        return retval;
    }

    if (core->groups) {
        ftd_group_free_all();
    }

    core->groups = ftd_group_parse(uitvoer);

    ftd_group_save();

    ftd_string_free(uitvoer);

    if (!core->groups) {
        return ERROR_PARSE;
    }

    return ERROR_NONE;
}

/**
 * Leest de gegevens van een lijst nieuwsgroepen uit een XML gecodeerde string.
 *
 * @arg     buffer      De XML gecodeerde string waarin de gegevens staan.
 *
 * @return  Een nieuwe lijst met nieuwsgroepen.
 */
FtdGroupList *
ftd_group_parse(FtdString *buffer)
{
    xmlDocPtr        doc;
    xmlNodePtr       cur;
    FtdGroupList    *list = NULL;
    FtdGroup        *group = NULL;

    g_assert(core);
    g_assert(buffer);

    doc = xmlParseMemory(ftd_string_value(buffer), ftd_string_length(buffer));
    cur = xmlDocGetRootElement(doc);
    if (cur == NULL) {
        fprintf(stderr,"ftd_group_parse(): empty document!\n");
        xmlFreeDoc(doc);
        return NULL;
    }

    if (strcasecmp(cur->name, "Groups")) {
        fprintf(stderr,"ftd_group_parse(): document of the wrong type, root node != Groups");
        xmlFreeDoc(doc);
        return NULL;
    }

    cur = cur->xmlChildrenNode;
    if (!cur) {
        // Een warning is niet nodig hier
        xmlFreeDoc(doc);
        return NULL;
    }

    while (cur) {
        if (!strcasecmp(cur->name, "Group")) {
            group = ftd_group_parse_group(doc, cur->xmlChildrenNode);
            group->groupId = ftd_xml_parse_int_attr(doc, cur, "ID");
            if(ftd_group_by_id(group->groupId) == NULL) {
                list = ftd_list_append(list, group);
            } else {
                ftd_group_free(group);
            }
        }
        cur = cur->next;
    }
    xmlFreeDoc(doc);

    return list;
}

/**
 * Leest de gegevens van een nieuwsgroep uit een XML boom.
 *
 * @arg     doc         Het XML document waar de gegevens in staan.
 * @arg     cur         Een pointer naar het element in het document waar de nieuwsgroep te vinden is.
 *
 * @return  Een nieuwe nieuwsgroep met de gevonden gegevens.
 */
FtdGroup *
ftd_group_parse_group(xmlDocPtr doc, xmlNodePtr cur)
{
    FtdGroup *group;

    group = ftd_group_new();

    while(cur != NULL) {
        if (!strcasecmp(cur->name, "Name")) {
            group->groupName = ftd_xml_parse_string(doc, cur);
        }
        cur = cur->next;
    }

    return group;
}

/**
 * Zoekt de nieuwsgroep op bij een nieuwsgroep-id.
 * Geef het resultaat niet vrij!
 *
 * @arg     id          Het nummer van de nieuwsgroep.
 *
 * @return  De nieuwsgroep behorend bij het gegeven nummer.
 *
 * @see     ::FtdGroup.id
 */
FtdGroup *
ftd_group_by_id(int id)
{
    FtdGroupList *list;

    g_assert(core);

    list = core->groups;

    while (list) {
        FtdGroup *group = ftd_list_value(list);
        if (group->groupId == id) {
            return group;
        }
        list = ftd_list_next(list);
    }

    return NULL;
}

/**
 * Een lijst met alle nummers van de toegestane nieuwsgroepen.
 */
int *groupIds = NULL;
/**
 * De lengte van de lijst met alle nummer van de toegestande nieuwsgroepen.
 */
int groupIdLen = 0;

/**
 * Stelt de globale groepenlijst in.
 * Vergeet niet om ook weer ::ftd_group_unset_group_id_list aan te roepen.
 *
 * @arg     grouplist           De lijst met ::FtdGroup pointers naar alle toegestane nieuwsgroepen.
 */
void
ftd_group_set_group_id_list(FtdList *grouplist)
{
    FtdGroupList *list = grouplist;

    g_assert(groupIds == NULL);
    g_assert(grouplist);

    groupIds = g_malloc(sizeof(int) * ftd_list_length(list));
    groupIdLen = 0;

    while (list) {
        FtdString *groupId = ftd_list_value(list);
        g_assert(groupId);
        groupIds[groupIdLen] = atoi(ftd_string_value(groupId));
        groupIdLen++;
        list = ftd_list_next(list);
    }
}

/**
 * Geeft de globale groepenlijst vrij.
 */
void
ftd_group_unset_group_id_list(void)
{
    g_assert(groupIds);

    g_free(groupIds);
    groupIdLen = 0;
    groupIds = NULL;
}

/**
 * Kijkt of een nieuwsgroep-nummer in de globale groepenlijst voorkomt.
 *
 * @pre     ftd_group_set_group_id_list is aangeroepen.
 *
 * @arg     id      Het gezochte nieuwsgroep-nummer.
 *
 * @return  True indien het gezochte nummer in de gloable groepenlijst voor komt.
 */
gboolean
ftd_group_id_is_in_list(int id)
{
    int i;

    g_assert(groupIds);

    for (i = 0; i < groupIdLen; i++) {
        if (groupIds[i] == id) {
            return TRUE;
        }
    }

    return FALSE;
}

/**
 * Slaat de lijst met alle nieuwsgroepen op.
 */
int
ftd_group_save(void)
{
    char *filename;
    xmlDocPtr doc;
    xmlNodePtr rootNode, groupNode;
    FtdList *list;
    FtdString *str;
    int retval;

    g_assert(core);

    doc = xmlNewDoc(BAD_CAST "1.0");
    rootNode = xmlNewNode(NULL, BAD_CAST "Groups");
    xmlDocSetRootElement(doc, rootNode);

    list = core->groups;

    while (list) {
        FtdGroup *group = ftd_list_value(list);

        groupNode = xmlNewNode(NULL, BAD_CAST "Group"); /* <Group ID="1"> */
        str = ftd_string_integer(group->groupId);
        xmlNewProp(groupNode, "ID", ftd_string_value(str));
        xmlAddChild(rootNode, groupNode);
        ftd_string_free(str);
        xmlNewTextChild(groupNode, NULL, "Name", ftd_string_value(group->groupName));

        list = ftd_list_next(list);
    }

    filename = ftd_file_parse_cache_filename(GROUPS_FILE, TRUE);
    xmlSetCompressMode(9);
    retval = xmlSaveFormatFile(filename, doc, 1);
    g_free(filename);

    xmlFreeDoc(doc);
    //xmlCleanupParser();

    return retval;
}

/**
 * Laadt de lijst met alle nieuwsgroepen.
 */
void
ftd_group_load(void)
{
    FtdString *data;

    data = ftd_file_load(GROUPS_FILE, TRUE);
    if (!data) {
        return;
    }

    if (core->groups) {
        ftd_group_free_all();
    }

    core->groups = ftd_group_parse(data);
    ftd_string_free(data);
}

/**
 * Geeft de lijst met alle nieuwsgroepen terug.
 * Geef het resultaat niet vrij!
 *
 * @return  De lijst met ::FtdGroup pointers naar alle nieuwsgroepen.
 */
const FtdList *
ftd_groups_get(void)
{
    return core->groups;
}

/**
 * Geeft aan of de lijst met nieuwsgroepen geladen is.
 *
 * @return  True indien de lijst geladen is.
 */
gboolean
ftd_group_list_ready(void)
{
    g_assert(core);

    return (core->groups != NULL);
}
