 /* ====================================================================
 * 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 <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <zlib.h>

#include "ftd4linux.h"
#include "ftd_app_core.h"
#include "ftd_config.h"
#include "ftd_events.h"
#include "ftd_groups.h"

/**
/**
 * Haalt de update-bestanden van FTD-server binnen.
 * Let op: deze functie haalt alleen de namen van de bestanden binnen, niet de bestanden zelf.
 *
 * @arg     list_avail_updates  Deze lijst met ::FtdString pointers naar de namen van de update-bestanden wordt aangevuld.
 * @arg     lastCheck           Timestamp van de laatste keer dat er een succesvolle update is gedaan.
 * @arg     newLastCheck        Hierin komt de timestamp van de 'nieuwe' laatste succesvolle update in te staan.
 * @arg     errmsg              Een eventueel foutbericht van de server wordt hier in geplaatst.
 *
 * @return  Een fout-code die aangeeft of het binnenhalen van de bestandsnamen succesvol was.
 */
FtdErrorCode
ftd_events_fetch_get_available(FtdList **list_avail_updates, time_t lastCheck, time_t *newLastCheck, char **errmsg)
{
    FtdList         *params = NULL;
    char            *strLastCheck;
    FtdErrorCode     retval;
    FtdString       *uitvoer;

    strLastCheck = g_strdup_printf("%d", (int) lastCheck);

    params = ftd_list_append(params, ftd_http_parameter_new("lc", strLastCheck));
    g_free(strLastCheck);

    retval = ftd_http_curl_execute(FTD_URL_EVENTS, params, TRUE, &uitvoer, TRUE, NULL, TRUE);
    ftd_http_parameter_free_all(params);

    if (retval != ERROR_NONE) {
        if (retval == ERROR_MSG) {
            *errmsg = g_strdup(ftd_string_value(uitvoer));
        } else {
            *errmsg = NULL;
        }

        return retval;
    }

    /* Ontleedt de XML data om zo de individuele update files te achterhalen */
    retval = ftd_events_list_parse (uitvoer, newLastCheck, list_avail_updates, errmsg);
    ftd_string_free (uitvoer);

    return retval;
}

/**
 * Haalt alle updates binnen die binnen gehaald kunnen of mogen worden.
 *
 * @arg     file_callback   Een callback om de voortgang bij te houden van het downloaden.
 * @arg     parse_callback  Een callback om de voortgang bij te houden van het parsen van de gegevens.
 * @arg     status_callback Een callback om de algemene voortgang bij te houden.
 * @arg     errmsg          Een eventueel foutbericht van de FTd server komt hier in te staan.
 *
 * @return  Een fout-code die aangeeft of het updaten succesvol was of niet.
 */
FtdErrorCode
ftd_events_fetch(FtdEventFileCallback file_callback, FtdEventParseCallback parse_callback, FtdEventStatusCallback status_callback, char **errmsg, gpointer data)
{
    int              retval;
    FtdList         *listUpdateFiles = NULL;
    FtdList         *eventlistNieuw = NULL;
    FtdString       *uitvoer;
    int              origPrevCheck;
    time_t           newLastCheck;
    FtdErrorCode     parseRetval;
    FtdList         *list;
    int              file_teller;

    g_assert (core);
    g_assert (core->config);

    /* Ruim (te) oude FTD postings op */
    ftd_eventlist_cleanup(core->eventlist);

    if (file_callback) {
        file_callback(0, 0);
    }

    if (!core->config->recursiveUpdate) {
        retval = ftd_events_fetch_get_available(&listUpdateFiles, core->config->lastCheck, &newLastCheck, errmsg);

        if (retval != ERROR_NONE) {
            return retval;
        }
    } else {
        time_t lastCheck;
        int size_of_list = 0;
        int prev_size_of_list = -1;

        newLastCheck = core->config->lastCheck;

        while (size_of_list > prev_size_of_list) {     // Ga net zo lang door totdat we geen nieuwe updates meer hebben
            lastCheck = newLastCheck;
            retval = ftd_events_fetch_get_available(&listUpdateFiles, lastCheck, &newLastCheck, errmsg);
            if (retval != ERROR_NONE) {
                ftd_eventlist_load(core->eventlist, NULL);
                return retval;
            }

            prev_size_of_list = size_of_list;
            size_of_list = ftd_list_length(listUpdateFiles);
        }
    }

    // Er kunnen nieuwe groepen zojuist toegevoegd zijn aan de lijst
    ftd_group_save();

    list = listUpdateFiles;
    file_teller = 0;

    ftd_categories_reset_counters();

    while (list) {
        FtdList *tmplist = NULL;
        char *url = g_strdup_printf("%s%s.gz", FTD_DUMPS_DIR, (char *) ftd_list_value(list));
        char *filename = NULL;
        gzFile gz_fp;
        unsigned char buf[1024];
        int len;
        FtdString *decompressedData;

        file_teller++;

        if (file_callback) {
            file_callback(ftd_list_length(listUpdateFiles), file_teller);
        }

        retval = ftd_http_curl_execute(url, NULL, TRUE, NULL, TRUE, &filename, TRUE);
        g_free(url);

        if (retval != ERROR_NONE) {
            return retval;
        }

        // Decodeer de gzip data
        gz_fp = gzopen(filename, "rb");

        decompressedData = ftd_string_new("");
        memset(buf, 0, sizeof(buf));
        while ((len = gzread(gz_fp, buf, sizeof(buf) - 1)) > 0) {
            decompressedData = ftd_string_append(decompressedData, buf);
            memset(buf, 0, sizeof(buf));
        }

        gzclose(gz_fp);
        unlink(filename);
        g_free(filename);

        if (len == -1) {
            // Hiermee kunnen we de GUI signaleren dat er tijdens
            // het lezen van de server iets misgegaan is
            return ERROR_UNKNOWN;
        }

        parseRetval = ftd_events_parse (decompressedData, NULL, &tmplist, errmsg, FALSE);
        ftd_string_free(decompressedData);
        uitvoer = NULL;

        if (parseRetval != ERROR_NONE) {
            return parseRetval;
        }

        eventlistNieuw = g_slist_concat(eventlistNieuw, tmplist);

        g_free(ftd_list_value(list));
        list = ftd_list_next(list);
    }

    ftd_list_free(listUpdateFiles);

    if (eventlistNieuw != NULL) {    /* De postlist kan leeg zijn (dus geen nieuwe postings of status
                                      * veranderingen sinds laatste update) */
        if (core->config->lastCheck != 0) {
            ftd_eventlist_load(core->eventlist, NULL);
        }

        /* Draai de eventlistNieuw om zodat de meest nieuwe events als eerste behandeld worden */
        eventlistNieuw = ftd_list_reverse(eventlistNieuw);

        /* Bewaar alle nieuw opgehaalde postings direct */
        ftd_eventlist_merge(core->eventlist, eventlistNieuw, parse_callback, data);

        if (status_callback) {
            status_callback(data);
        }

        ftd_eventlist_save(core->eventlist);
    } else {
        /* Geen nieuwe FTD Postings kunnen achterhalen.. lees de oude
         * postings in en return ERROR_NEW */
        return ERROR_NEW;
    }

    /* Updaten geslaagd, pas in de config file de lastCheck en prevCheck waarden aan */
    origPrevCheck = core->config->prevCheck;
    core->config->prevCheck = core->config->lastCheck;
    core->config->lastCheck = newLastCheck;
    ftd_config_save();

    /* Bereid de lijst voor die we moeten tonen op het scherm */
    /* Laad gegevens uit de posts cache, sorteer deze en bewaar het als HTML_LIST_FILE */
    ftd_eventlist_load(core->eventlist, NULL);

    if (status_callback) {
        status_callback(data);
    }

    return ERROR_NONE;
}
