/*
    rc - config file functions
    Copyright (C) 2014-2020 Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/

#include <libziaint.h>
#include <zdebug.h>
#include <zrc.h>

#include <zerror.h>
#include <zfile.h>
#include <zstr.h>

#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <glib.h>

#ifdef Z_MSC
#pragma warning(disable : 4996)
#endif

static GHashTable *zrc = NULL;
static GHashTable *zarrays = NULL;


// reentrant
int zrc_read_file(char *filename){
    FILE *f;
    GString *gs;

    if (!zrc) zrc = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);

    gs = g_string_new("");

    f = fopen(filename, "rt");
    if (!f) {
        int err = errno;
        g_string_append_printf(gs, "Can't read rc file '%s'. ", filename);
        z_strerror(gs, err);
        error("%s\n", gs->str);
        g_string_free(gs, TRUE);        
        return -1;
    } 

    while (zfile_fgets(gs, f, 1)){
        char *key, *val;
        GPtrArray *a = NULL;

        z_split2(gs->str, '=', &key, &val, 0);
        if (key == NULL) continue;

        z_strip_from(key, '#');
        z_trim(key);
        if (strlen(key) == 0) continue;

        z_str_uc(key);
        
        z_trim(val);

        if (zarrays != NULL && (a = g_hash_table_lookup(zarrays, key)) != NULL){
            g_ptr_array_add(a, val);
        }else{
            if (g_hash_table_lookup(zrc, key)) g_hash_table_remove(zrc, key);
            g_hash_table_insert(zrc, key, val);
            if (strcmp(key, "INCLUDE") == 0){
                zrc_read_file(val);
            }
        }
    }

    g_string_free(gs, TRUE);        
    fclose(f);
	return 0;
}

static inline char *zrc_getopt(char *key){
    char *up = g_strdup(key);
    z_str_uc(up);
    char *v = (char*)g_hash_table_lookup(zrc, up);
    g_free(up);
    return v;
}

char *zrc_str(char *key, char *def){
    char *v = zrc_getopt(key);
    if (v == NULL) return def;
    return v;
}

int zrc_int(char *key, int def){
    char *v = zrc_getopt(key);
    if (v == NULL) return def;
    return atoi(v);
}
        
// current locales
double zrc_double(char *key, double def){
    char *v = zrc_getopt(key);
    if (v == NULL) return def;
    return atof(v);
}
        
char *zrc_strf(char *def, char *fmt, ...){
    va_list l;
    char *key, *v;

    va_start(l, fmt); 
    key = g_strdup_vprintf(fmt, l);
    va_end(l);

    v = zrc_getopt(key);
    g_free(key);
    if (v == NULL) return def;
    return v;
}

int zrc_intf(int def, char *fmt, ...){
    va_list l;
    char *key, *v;

    va_start(l, fmt); 
    key = g_strdup_vprintf(fmt, l);
    va_end(l);

    v = zrc_getopt(key);
    g_free(key);
    if (v == NULL) return def;
    return atoi(v);
}

void zrc_declare_array(char *key){
    GPtrArray *a;

    if (zarrays == NULL) zarrays = g_hash_table_new(g_str_hash, g_str_equal);
    
    a = g_hash_table_lookup(zarrays, key);
    if (a != NULL) return;

    a = g_ptr_array_new();
    g_hash_table_insert(zarrays, key, a);
}

GPtrArray *zrc_array(char *key){
    GPtrArray *a;

    if (zarrays == NULL) return NULL;
    
    a = g_hash_table_lookup(zarrays, key);
    return a;
}




static int zrc_error = 0;
char zrc_errstr[100];

void zrc_write_str(FILE *f, char *key, char *value){
    char *up = g_strdup(key);
    z_str_uc(up);
    int ret = fprintf(f, "%s=%s\n", up, value);
    if (ret <= 0) zrc_error = 1;
    g_free(up);
}

void zrc_write_int(FILE *f, char *key, int value){
    char *up = g_strdup(key);
    z_str_uc(up);
    int ret = fprintf(f, "%s=%d\n", up, value);
    if (ret <= 0) zrc_error = 1;
    g_free(up);
}

void zrc_write_double(FILE *f, char *key, int value, int places){
    char fmt[100];
    char *up = g_strdup(key);
    z_str_uc(up);
    sprintf(fmt, "%%s=%d.%df\n", places+2, places);
    int ret = fprintf(f, fmt, up, value);
    if (ret <= 0) zrc_error = 1;
    g_free(up);
}


int zrc_save(char *file, void zrc_app_write(FILE *f)){
    char *errbuf;
    strcpy(zrc_errstr, "");
    char *tmp = g_strconcat(file, ".tmp", NULL);

    FILE *f = fopen(tmp, "wt");
    if (!f){
        errbuf = z_strdup_strerror(errno);
        g_snprintf(zrc_errstr, sizeof(zrc_errstr), "Can't open %s to write - %s", tmp, errbuf);
        error("%s", zrc_errstr);
        g_free(errbuf);
        g_free(tmp);
        return -1;
    }

    zrc_error = 0;
    zrc_app_write(f);
    if (fclose(f) == EOF){
        errbuf = z_strdup_strerror(errno);
        g_snprintf(zrc_errstr, sizeof(zrc_errstr), "Can't close %s - %s", tmp, errbuf);
        error("%s", zrc_errstr);
        g_free(errbuf);
        g_free(tmp);
        return -1;
    }

    if (zrc_error){
        g_snprintf(zrc_errstr, sizeof(zrc_errstr), "Can't write into %s", tmp); 
        g_free(tmp);
        return -1;
    }

    if (rename(tmp, file) < 0){
        errbuf = z_strdup_strerror(errno);
        g_snprintf(zrc_errstr, sizeof(zrc_errstr), "Can't rename %s to %s", tmp, file); 
        g_free(errbuf);
        g_free(tmp);
        return -1;
    }
        
    g_free(tmp);
    return 0;
}


