/* ====================================================================
 * 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 <fcntl.h>
#ifdef WIN32
#include <io.h>
#else
#include <unistd.h>
#endif /* WIN32 */
#include <sys/stat.h>
#include <errno.h>
#include <glib.h>
#if defined(WIN32) || defined(__CEGCC__)
#include <windows.h>
#endif
#include <zlib.h>

#include "ftd_file.h"

/**
 * De directory waar de cache en config bestanden in opgeslagen staan
 */
static const char *cache_dir = NULL;

/**
 * De directory waar de FTD4Linux data bestanden in opgeslagen staan
 */
static const char *data_dir = NULL;

/**
 * De directory waar de FTD4Linux HTTP plugin in opgeslagen staat
 */
static const char *lib_dir = NULL;

/**
 * Stelt de directory in waar de cache en config bestanden in opgeslagen staan
 *
 * @arg     dir         De directory.
 */
void
ftd_file_set_cache_dir(const char *dir)
{
    ftd_file_free_cache_dir();

    if (dir) {
        cache_dir = g_strdup(dir);
    } else {
#if defined(__CEGCC__)
        cache_dir = g_strdup_printf("/My Documents/OpenFTD");
#else
#if defined(WIN32)
        cache_dir = g_strdup_printf("%s\\OpenFTD", g_get_user_config_dir());
#else
        cache_dir = g_strdup_printf("%s/.openftd", g_get_home_dir());
#endif
#endif
    }

    if (ftd_file_init_cache_dir() != 0) {
        g_error("ftd_file_set_cache_dir(): Aanmaken van %s is mislukt.", cache_dir);
    }
}

/**
 * Geeft de lokale kopie van de cache en config bestanden in opgeslagen staan vrij
 */
void
ftd_file_free_cache_dir(void)
{
    if (cache_dir) {
        g_free((gpointer) cache_dir);
        cache_dir = NULL;
    }
}

/**
 * Maakt een nieuwe directory voor de cache en configuratie bestanden van FTD4linux aan.
 *
 * @return  0 indien dit succesvol verliep.
 */
int
ftd_file_init_cache_dir (void)
{
    struct stat st;
#ifndef WIN32
    FILE *fp;
    char *tmpfile;
#endif /* WIN32 */

    g_assert(cache_dir);

    /* Kijk eerst of deze map reeds bestaat alvorens een nieuwe file erin proberen aan te maken */
    if (stat(cache_dir, &st) == -1)
    {
        /* Map bestaat niet, maak deze eerst aan */
#if defined(WIN32) && !defined(__CEGCC__)
        if (CreateDirectory(cache_dir, NULL) == 0) {
#else
        if (mkdir (cache_dir, 0700) != 0) {
#endif
            /* Aanmaken map mislukt, kappen */
            g_warning("ftd_file_init_cache_dir(): mkdir() mislukt: %s\n", strerror(errno));
            return -2;
        }
    } else {
        /* Controleer of cache_dir DAADWERKELIJK een map is */
        if (!S_ISDIR(st.st_mode)) {
            g_warning("ftd_file_init_cache_dir(): map %s bestaat maar is geen directory\n", cache_dir);
            return -3;
        }

/* WIN32 heeft niets te maken met schrijfrechten */
#if !defined(WIN32) && !defined(__CEGCC__)
        /* Controleer of we genoeg rechten hebben om te mogen schrijven */
        tmpfile = g_strdup_printf("%s/test.dat", cache_dir);
        fp = fopen(tmpfile, "w");
        if (fp == NULL) {
            g_warning("ftd_file_init_cache_dir(): de map %s heeft niet voldoende rechten om in te schrijven!\n", cache_dir);
            g_free(tmpfile);
            return -4;
        }
        fclose(fp);

        /* Nu heeft fopen een bestand aangemaakt van 0 bytes..dit was alleen maar een test dus verwijderen */
        unlink(tmpfile);
        g_free(tmpfile);
#endif /* WIN32 */
    }

    return 0;
}

/**
 * Stelt de directory in waar de gegevens van FTD4linux zich bevinden.
 *
 * @arg     data_dir        De genoemde directory.
 */
void
ftd_file_set_data_dir(const char *ftd_data_dir)
{
    ftd_file_free_data_dir();

    if (ftd_data_dir) {
        data_dir = g_strdup(ftd_data_dir);
    } else {
#if defined(WIN32) || defined(__CEGCC__)
        //data_dir = g_strdup("../share/ftd4linux");
        char *dir = g_path_get_dirname(__argv[0]);
        data_dir = g_build_path("/", dir, "../share/openftd", NULL);
        g_free(dir);
#else
        data_dir = g_strdup(FTD_DATA_DIR);
#endif
    }
}

/**
 * Geeft de lokale kopie van de directory waar de de gegevens van FTD4linux zich bevinden vrij
 */
void
ftd_file_free_data_dir(void)
{
    if (data_dir) {
        g_free((gpointer) data_dir);
        data_dir = NULL;
    }
}

/**
 * Geeft het volledige pad naar een bestand in de gebruikers-data-directory terug.
 *
 * @arg     filename        De naam van het bestand dat gezocht wordt.
 *
 * @return  Een nieuwe string met daarin het volledige pad naar het gezochte bestand.
 */
char *
ftd_file_parse_data_filename(const char *filename)
{
    char *volledigeFilename;

    g_assert(data_dir);

    if( filename ) {
        volledigeFilename = g_strdup_printf("%s/%s", data_dir, filename);
    } else {
        volledigeFilename = g_strdup( data_dir );
    }

#ifdef WIN32
    return g_strdelimit(volledigeFilename, "/" , '\\');
#else
    return volledigeFilename;
#endif
}

/**
 * Geeft het volledige pad naar een bestand in de gebruikers-data-directory terug.
 * Deze directory bevat de gegevens zoals een events-cache, instellingen en andere gebruikers-afhankelijke dingen.
 *
 * @arg     filename        De naam van het bestand dat gezocht wordt.
 * @arg     gzip            Indien TRUE zal de term '.gz' toegevoegd worden aan de return value
 *
 * @return  Een nieuwe string met daarin het volledige pad naar het gezochte bestand.
 */
char *
ftd_file_parse_cache_filename(const char *filename, gboolean gzip)
{
    char *volledigeFilename;

    g_assert(cache_dir);

    if (filename) {
#ifdef WIN32
        if (gzip) {
            volledigeFilename = g_strdup_printf("%s\\%s.gz", cache_dir, filename);
        } else {
            volledigeFilename = g_strdup_printf("%s\\%s", cache_dir, filename);
        }
#else
        if (gzip) {
            volledigeFilename = g_strdup_printf("%s/%s.gz", cache_dir, filename);
        } else {
            volledigeFilename = g_strdup_printf("%s/%s", cache_dir, filename);
        }
#endif
    } else {
        volledigeFilename = g_strdup(cache_dir);
    }

#ifdef WIN32
    return g_strdelimit(volledigeFilename, "/" , '\\');
#else
    return volledigeFilename;
#endif
}

/**
 * Geeft het volledige pad naar een bestand dat zich in de globale directory met de data van FTD4linux bevindt.
 *
 * @arg     filename        De naam van het gezochte bestand.
 *
 * @return  Een nieuwe string met het volledige pad naar het bestand.
 */
char *
ftd_file_parse_data_dir(const char *filename)
{
    char *res;

    g_assert(data_dir);

    if (filename) {
        res = g_strdup_printf("%s%s", data_dir, filename);
    } else {
        res = g_strdup(data_dir);
    }

#ifdef WIN32
    return g_strdelimit(res, "/" , '\\');
#else
    return res;
#endif
}

/**
 * Stelt de directory in waar de gegevens van FTD4linux zich bevinden.
 *
 * @arg     data_dir        De genoemde directory.
 */
void
ftd_file_set_lib_dir(const char *ftd_lib_dir)
{
    ftd_file_free_lib_dir();

    if (ftd_lib_dir) {
        lib_dir = g_strdup(ftd_lib_dir);
    } else {
#ifdef WIN32
        lib_dir = g_strdup("../lib");
#else
        lib_dir = g_strdup(LIBDIR);
#endif
    }
}

/**
 * Geeft de lokale kopie van de directory waar de de gegevens van FTD4linux zich bevinden vrij
 */
void
ftd_file_free_lib_dir(void)
{
    if (lib_dir) {
        g_free((gpointer) lib_dir);
        lib_dir = NULL;
    }
}

char *
ftd_file_parse_lib_dir(const char *filename)
{
    char *res;

    g_assert(lib_dir);

    if (filename) {
        res = g_strdup_printf("%s%s", lib_dir, filename);
    } else {
        res = g_strdup(lib_dir);
    }

#ifdef WIN32
    return g_strdelimit(res, "/" , '\\');
#else
    return res;
#endif
}

/**
 * Laad een bestand in en geeft de inhoud als string terug.
 *
 * @arg     filename        Het volledig pad naar het te laden bestand.
 * @arg     gzip            Indien TRUE, dan zal gzip compressie worden toegepast
 *                          (en de extensie aangevuld worden met .gz)
 *
 * @return  Een nieuwe string met de volledige inhoud van de gezochte file, of NULL indien er een fout optrad.
 */
FtdString *
ftd_file_load(const char *filename, gboolean gzip)
{
    FILE *fp;
    FtdString *inhoud;
    char *volledigeFilename;

    g_assert (filename);

    if (gzip) {
        gzFile gz_fp;
        char buf[32767];

        // Kijk eerst of het bestand reeds bestaat zonder gzip extensie
        volledigeFilename = ftd_file_parse_cache_filename(filename, FALSE);
        fp = fopen(volledigeFilename, "r");

        if (fp != NULL) {
            // Lees voor deze keer het bestand zonder gzip extensie in en verwijder
	    // deze na gebruik zodat in het vervolg de gzip variant geladen worden
            inhoud = ftd_string_new("");
            while (!feof(fp)) {
                if (fgets(buf, sizeof(buf), fp)) {
                    inhoud = ftd_string_append(inhoud, buf);
                }
            }
            fclose(fp);
            unlink(volledigeFilename);
            g_free(volledigeFilename);
            return inhoud;
        }

        g_free(volledigeFilename);

        volledigeFilename = ftd_file_parse_cache_filename(filename, TRUE);
        gz_fp = gzopen(volledigeFilename, "rb");

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

        gzclose(gz_fp);
        g_free(volledigeFilename);

        return inhoud;
    } else {
        volledigeFilename = ftd_file_parse_cache_filename(filename, FALSE);
        fp = fopen (volledigeFilename, "r");
        g_free (volledigeFilename);
        if (fp == NULL) {
            return NULL;
        }

        /* Lees ALLE data uit de file en bewaar deze in een buffer */
        inhoud = ftd_string_new("");
        while (!feof(fp)) {
            char buf[32767];
            if (fgets(buf, sizeof(buf), fp)) {
                inhoud = ftd_string_append(inhoud, buf);
            }
        }
    }

    fclose (fp);

    return inhoud;
}

/**
 * Slaat een string op als bestand.
 *
 * @arg     filename        Het volledig pad naar het op te slaan bestand.
 * @arg     tekst           De volledige tekst die in de file terecht dient te komen.
 * @arg     gzip            Indien TRUE, dan zal gzip compressie worden toegepast
 *                          (en de extensie aangevuld worden met .gz)
 *
 * @return  0 indien dit succesvol was.
 */
int ftd_file_save(const char *filename, FtdString *tekst, gboolean gzip)
{
    FILE *fp;
    char *volledigeFilename;
    gzFile gz_fp;

    g_assert (filename);
    g_assert (tekst);

    volledigeFilename = ftd_file_parse_cache_filename(filename, gzip);

    if (gzip) {
        gz_fp = gzopen(volledigeFilename, "wb");
        gzwrite(gz_fp, ftd_string_value(tekst), ftd_string_length(tekst));
        gzclose(gz_fp);

        g_free(volledigeFilename);
    } else {
        fp = fopen (volledigeFilename, "w");
        if (fp == NULL) {
            g_free(volledigeFilename);

            return -1;
        }

        g_free (volledigeFilename);

        /* Schrijf de inhoud van de tekst buffer in de fp */
        fprintf(fp, "%s\n", ftd_string_value(tekst));
        fflush (fp);
        fclose (fp);
    }

    return 0;
}

/**
 * Kopieert de ene file naar de andere.
 *
 * @arg     src         Het volledig pad van de file die gekopieerd moet worden.
 * @arg     dest        Het volledig pas van de file waarheen dit gekopieerd moet worden.
 *
 * @return  False indien het kopieren succesvol was.
 */
gboolean
ftd_file_copy(char *src, char *dest)
{
    int fp_in, fp_out;
    char buf[32767];
    int len = 1;

    OPEN(fp_in, src, O_RDONLY);
    if (fp_in == -1) {
        return TRUE;
    }

#ifdef WIN32
    OPEN(fp_out, dest, O_WRONLY | O_CREAT | O_TRUNC);
#else
    fp_out = open(dest, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
#endif
    if (fp_out == -1) {
        close(fp_in);
        return TRUE;
    }

    while((len = read(fp_in, buf, sizeof(buf))) > 0) {
        write(fp_out, buf, len);
    }

    close(fp_in);
    close(fp_out);

    return FALSE;
}

