/*
    Tucnak - VHF contest log
    Copyright (C) 2002-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 "header.h"

#include "excdb.h"
#include "dwdb.h"
#include "language2.h"
#include "main.h"
#include "stats.h"
#include "tsdl.h"


//#include <string.h>

struct excdb *excdb;
static gchar *exc_next;
static gchar *cxe_next;

struct excdb *init_exc(void){
    struct excdb *excdb;

	progress(VTEXT(T_INIT_EXCDB));		

    excdb = g_new0(struct excdb, 1);
    
    excdb->exc = g_hash_table_new(g_str_hash, g_str_equal);
    excdb->cxe = g_hash_table_new(g_str_hash, g_str_equal);
    excdb->vexc = g_hash_table_new(g_str_hash, g_str_equal);
    excdb->vexcia = z_ptr_array_new();
    
    exc_next = NULL;
    cxe_next = NULL;
    excdb->latest = 0;
    excdb->excname = g_strdup("");
    
    return excdb;
}

gboolean free_exc_item(gpointer key, gpointer value, gpointer user_data){
    struct exc_item *exci;
    
    g_free(key);
    exci = (struct exc_item *)value;
    if (exci->exc0) g_free(exci->exc0);
    if (exci->exc1) g_free(exci->exc1);
    g_free(value);
    return TRUE;    
}

gboolean free_cxe_item(gpointer key, gpointer value, gpointer user_data){
    struct cxe_item *wci;
    
    g_free(key);
    wci = (struct cxe_item *)value;
    if (wci->call0) g_free(wci->call0);
    if (wci->call1) g_free(wci->call1);
    g_free(value);
    return TRUE;
        
}

gboolean free_vexc_item(gpointer key, gpointer value, gpointer user_data){
    if (key) g_free(key);
    return TRUE;
}

void clear_exc(struct excdb *excdb){
//    dbg("clear_exc()\n");
    z_ptr_array_free_all(excdb->vexcia);
    excdb->vexcia = z_ptr_array_new();
    g_hash_table_foreach_remove(excdb->vexc, free_vexc_item, NULL);
    g_hash_table_foreach_remove(excdb->cxe, free_cxe_item, NULL);
    g_hash_table_foreach_remove(excdb->exc, free_exc_item, NULL);

    zg_free0(excdb->excname);
}

void free_exc(struct excdb *excdb){
	progress(VTEXT(T_FREE_EXCDB));		

    clear_exc(excdb);
    z_ptr_array_free_all(excdb->vexcia);
    g_hash_table_destroy(excdb->vexc);
    g_hash_table_destroy(excdb->cxe);
    g_hash_table_destroy(excdb->exc);
    g_free(excdb);
}



gint get_exc_size(struct excdb *excdb){
    return g_hash_table_size(excdb->exc);
}

gint get_cxe_size(struct excdb *excdb){
    return g_hash_table_size(excdb->cxe);
}

gint get_vexc_size(struct excdb *excdb){
    return g_hash_table_size(excdb->vexc);
}

#define EXC_DELIM " \t\r\n"

void load_one_exc(struct excdb *excdb, gchar *s){
    gchar *call, *exc, *stamp_str;
    char *token_ptr;
    
    z_str_uc(s);
    call = strtok_r(s, EXC_DELIM, &token_ptr);
    if (!call) return;

    exc = strtok_r(NULL, EXC_DELIM, &token_ptr);
    if (!exc) return;

    stamp_str = strtok_r(NULL, EXC_DELIM, &token_ptr);
    if (!stamp_str) return;

    add_exc(excdb, call, exc, atoi(stamp_str));
    add_cxe(excdb, exc, call, atoi(stamp_str));
}

int load_exc_from_file(struct excdb *excdb, gchar *filename){
    FILE *f;
    GString *gs;

    f = fopen(filename, "rt");
    if (!f) return -1;
    
    gs = g_string_sized_new(256);

    while(1){
        if (!zfile_fgets(gs, f, 1)) break;
        g_strstrip(gs->str);

        load_one_exc(excdb,gs->str);
    }
    fclose(f);
    g_string_free(gs, 1);
    return 0;
}

void load_one_vexc(struct excdb *excdb, gchar *s){
    gchar *vexc;
    char *token_ptr;
    
    z_str_uc(s);
    for (vexc = strtok_r(s, EXC_DELIM, &token_ptr);
         vexc != NULL;
         vexc = strtok_r(NULL, EXC_DELIM, &token_ptr)){
         
         if (!*vexc) continue;
         if (g_hash_table_lookup(excdb->vexc, vexc) != NULL) continue;
         z_str_uc(vexc);
         g_hash_table_insert(excdb->vexc, g_strdup(vexc), (void*)1);
    }
}

int load_vexc_from_file(struct excdb *excdb, gchar *filename){
    FILE *f;
    GString *gs;

    f = fopen(filename, "rt");
    if (!f) return -1;
    
    gs = g_string_sized_new(256);

    while(1){
        if (!zfile_fgets(gs, f, 1)) break;
        g_strstrip(gs->str);

        load_one_vexc(excdb, gs->str);
    }
    fclose(f);
    g_string_free(gs, 1);
    return 0;
}

int load_vexc_from_mem(struct excdb *excdb, const char *file, const long int len){
    GString *gs;
    long int pos;
    
    gs = g_string_sized_new(256);
    pos = 0;
    
    while(1){
        if (!zfile_mgets(gs,  file, &pos, len, 0)) break;
        g_strstrip(gs->str);

        load_one_vexc(excdb, gs->str);
    }
    g_string_free(gs, 1);
    return 0;
}
            

void read_exc_files(struct excdb *excdb, enum exctype exctype, gchar *excname){
    gchar *s, *ename;
    int ret, i;

    //dbg("read_exc_files(%d, %s)\n", exctype, excname);
    if (!excname || !*excname) return;

    excdb->exctype = exctype;
    ename = g_strdup(excname);
    z_str_uc(ename);
    zg_free0(excdb->excname);
    excdb->excname = g_strdup(ename);
    z_str_lc(ename);
    
    s = g_strconcat(tucnak_dir, "/tucnakexc", ename, NULL); 
	z_wokna(s);
    ret = load_exc_from_file(excdb, s);
    dbg("    %s %d\n", s, ret);
    g_free(s);
    
    s = g_strconcat(tucnak_dir, "/tucnakvexc", ename, NULL); 
	z_wokna(s);
	ret = load_vexc_from_file(excdb, s);
    dbg("    %s %d\n", s, ret);
    g_free(s);
    if (ret<0){
        dbg("disk loading failed, try to load memory defaults '%s'\n", ename);
        if (strcasecmp(ename, "agcw")==0) {
            ret = load_vexc_from_mem(excdb, txt_tucnakvexcagcw, sizeof(txt_tucnakvexcagcw));
        }
        if (strcasecmp(ename, "okres")==0) {
            ret = load_vexc_from_mem(excdb, txt_tucnakvexcokres, sizeof(txt_tucnakvexcokres));
        }
        if (strcasecmp(ename, "rsgbdc")==0) {
            ret = load_vexc_from_mem(excdb, txt_tucnakvexcrsgbdc, sizeof(txt_tucnakvexcrsgbdc));
        }
        if (strcasecmp(ename, "usaca")==0) {
            ret = load_vexc_from_mem(excdb, txt_tucnakvexcusaca, sizeof(txt_tucnakvexcusaca));
        }
        if (strcasecmp(ename, "kac")==0) {
            ret = load_vexc_from_mem(excdb, txt_tucnakvexckac, sizeof(txt_tucnakvexckac));
        }
        if (strcasecmp(ename, "dok")==0) {
            ret = load_vexc_from_mem(excdb, txt_tucnakvexcdok, sizeof(txt_tucnakvexcdok));
        }
        if (strcasecmp(ename, "wna")==0) {
            ret = load_vexc_from_mem(excdb, txt_tucnakvexcwna, sizeof(txt_tucnakvexcwna));
        }
        if (strcasecmp(ename, "wsa")==0) {
            ret = load_vexc_from_mem(excdb, txt_tucnakvexcwsa, sizeof(txt_tucnakvexcwsa));
        }
        if (strcasecmp(ename, "rcont")==0) {
            ret = load_vexc_from_mem(excdb, txt_tucnakvexcrcont, sizeof(txt_tucnakvexcrcont));
        }
        if (strcasecmp(ename, "lcont")==0) {
            ret = load_vexc_from_mem(excdb, txt_tucnakvexclcont, sizeof(txt_tucnakvexclcont));
        }
        if (strcasecmp(ename, "uba")==0) {
            ret = load_vexc_from_mem(excdb, txt_tucnakvexcuba, sizeof(txt_tucnakvexcuba));
        }
        dbg("    %s %d\n", ename, ret);
    }
    g_free(ename);

    // set vexcia
    switch(excdb->exctype){
        case EXC_MULTIPLIED:
        case EXC_VERIFIED:
            g_hash_table_foreach(excdb->vexc, exc_ia_verified, (gpointer) excdb->vexcia);
            break;
        case EXC_WAZ:  
//            g_hash_table_foreach(dw->dw, exc_ia_waz, (gpointer) excdb->vexcia);
            for (i=1; i<=40; i++) z_ptr_array_add(excdb->vexcia, g_strdup_printf("%02d", i));
            break;
        case EXC_ITU:
//            g_hash_table_foreach(dw->dw, exc_ia_itu, (gpointer) excdb->vexcia);
            for (i=1; i<=90; i++) z_ptr_array_add(excdb->vexcia, g_strdup_printf("%02d", i));
            break;
        default:
            break;
    }
    z_ptr_array_qsort(excdb->vexcia, z_compare_string);
    z_ptr_array_uniq(excdb->vexcia, z_compare_string, TRUE);

}

void exc_ia_verified(gpointer key, gpointer value, gpointer user_data){
    gchar *exc          = (gchar *) key;
    ZPtrArray *vexcia = (ZPtrArray *) user_data;
    char s[10];

    if (strcasecmp(ctest->excname, "USACA")==0){
        strcpy(s, exc);
        usaca_multiple_exc(s);
        if (strcmp(s, exc) != 0) return; // QC, MAN, ALB, ALT, YU
    }
    z_ptr_array_add(vexcia, g_strdup(exc));
}


/*void exc_ia_waz(gpointer key, gpointer value, gpointer user_data){
    struct dw_item *dwi = (struct dw_item*) value;
    GIndexArray *vexcia = (GIndexArray *) user_data;

    z_ptr_array_add(vexcia, g_strdup_printf("%02d", dwi->waz));
}

void exc_ia_itu(gpointer key, gpointer value, gpointer user_data){
    struct dw_item *dwi = (struct dw_item*) value;
    GIndexArray *vexcia = (GIndexArray *) user_data;

    z_ptr_array_add(vexcia, g_strdup_printf("%02d", dwi->itu));
} */



void save_one_exc(gpointer key, gpointer value, gpointer user_data){
    GString *gs;
    gchar *call;
    struct exc_item *exci;

    gs   = (GString *) user_data;
    call = (gchar *) key;
    exci  = (struct exc_item *) value;
    
    
    if (exci->exc0) 
        g_string_append_printf(gs, "%-14s %-6s %08d\n", call, exci->exc0, exci->stamp0);
    
    if (exci->exc1) 
        g_string_append_printf(gs, "%-14s %-6s %08d\n", call, exci->exc1, exci->stamp1);
}

int save_exc_string(struct excdb *excdb, GString *gs){
    g_hash_table_foreach(excdb->exc, save_one_exc, (gpointer) gs);
    return 0;
}

int save_exc_into_file(struct excdb *excdb, gchar *filename){
    FILE *f;
    GString *gs;
    int ret;

    f = fopen(filename, "wt"); /* FIXME swp first */
    if (!f) {
        return errno;
    }
    
    gs=g_string_sized_new(100000);
    save_exc_string(excdb, gs);
    ret = fprintf(f,"%s",gs->str) != gs->len ? errno: 0;
    fclose(f);
    g_string_free(gs,TRUE);
    return ret;
}


void add_exc(struct excdb *excdb, gchar *call, gchar *exc, gint stamp){
    struct exc_item *exci;
    
    if (stamp<0) return; 
    if (stamp>excdb->latest) excdb->latest=stamp;
    
    exci = g_hash_table_lookup(excdb->exc, call);
    if (!exci){
        exci = g_new0(struct exc_item, 1);
        g_hash_table_insert(excdb->exc, g_strdup(call), exci);
    }
    
    if (stamp < exci->stamp1) return;
                
    if (stamp < exci->stamp0){
        if (strcasecmp(exci->exc0, exc)==0) return;

        if (exci->exc1) g_free(exci->exc1);
        exci->exc1   = g_strdup(exc);
        exci->stamp1 = stamp;
        return;
    }

    if (exci->exc0 && strcasecmp(exci->exc0, exc)==0) {
        exci->stamp0 = stamp;
        return;
    }

    if (exci->exc1) g_free(exci->exc1);
    exci->exc1   = exci->exc0;
    exci->stamp1 = exci->stamp0;
    
    exci->exc0   = g_strdup(exc);
    exci->stamp0 = stamp;
}


void add_cxe(struct excdb *excdb, gchar *exc, gchar *call, gint stamp){
    struct cxe_item *wci;
    
    if (stamp<0) return; 
    if (stamp>excdb->latest) excdb->latest=stamp;
    
    wci = g_hash_table_lookup(excdb->cxe, exc);
    if (!wci){
        wci = g_new0(struct cxe_item, 1);
        g_hash_table_insert(excdb->cxe, g_strdup(exc), wci);
    }

    if (stamp < wci->stamp1) return;

    if (stamp < wci->stamp0){
        if (strcasecmp(wci->call0, call)==0) return;

        if (wci->call1) g_free(wci->call1);
        wci->call1  = g_strdup(call);
        wci->stamp1 = stamp;
        return;
    }

    if (wci->call0 && strcasecmp(wci->call0, call)==0) {
        wci->stamp0 = stamp;
        return;
    }
    
    
    if (wci->call1) g_free(wci->call1);
    wci->call1  = wci->call0;
    wci->stamp1 = wci->stamp0;
    
    wci->call0  = g_strdup(call);
    wci->stamp0 = stamp;
}


gchar *find_exc_by_call(struct excdb *excdb, const gchar *call){
    struct exc_item *exci;
    struct dw_item *dwi;
    static char s[20];
            
    if (!call) return exc_next;
    exc_next = NULL;
        
    exci = g_hash_table_lookup(excdb->exc, call);
    if (exci) {
        exc_next = exci->exc1;
        return exci->exc0;
    }
    
    switch(excdb->exctype){
        case EXC_WAZ:  
			dwi = get_dw_item_by_call(dw, call);
            if (!dwi) return NULL;
            
            g_snprintf(s, 20, "%02d", dwi->waz);
            return s;
            
        case EXC_ITU:
			dwi = get_dw_item_by_call(dw, call);
            if (!dwi) return NULL;
            g_snprintf(s, 20, "%02d", dwi->itu);
            return s;
        default:
            break;
    }
    
    return NULL;
}

gchar *find_exc_by_call_newer(struct excdb *excdb, gchar *call, int maxstamp){
    struct exc_item *exci;

    exci = g_hash_table_lookup(excdb->exc, call);
    if (!exci) return NULL;
    if (exci->stamp0 > maxstamp) return exci->exc0;
    if (exci->stamp1 > maxstamp) return exci->exc1;
    return NULL;
}

gchar *find_call_by_exc(struct excdb *excdb, gchar *exc){
    struct cxe_item *wci;

    if (!excdb) return cxe_next;
    cxe_next = NULL;
        
    wci = g_hash_table_lookup(excdb->cxe, exc);
    if (!wci) return NULL;
    
    cxe_next = wci->call1;
    return wci->call0;
}



int is_valid_vexc(struct excdb *excdb, gchar *vexc){
    gchar *s;
    int ret;
 
    switch (ctest->exctype){
        case EXC_FREE:
        case EXC_MULTIPLIED:
            return 1;
            
        case EXC_VERIFIED:
            s = g_strdup(vexc);
            z_str_uc(s);
            ret = g_hash_table_lookup(excdb->vexc, s) != NULL;
            g_free(s);
            return ret;

        case EXC_WAZ:
        case EXC_ITU:
            return 1;
    }
    return 0;
}
         

void update_exc_from_band(struct excdb *excdb, struct band *band){
    int i;
    struct qso *q;

    stats_thread_join(band);

    for (i=0; i<band->qsos->len; i++){
        q = (struct qso *)g_ptr_array_index(band->qsos, i);

        if (q->error) continue;
        
        if (!q->callsign || !q->exc || !q->date_str) continue;

        add_exc(excdb, q->callsign, q->exc, atoi(q->date_str));
        add_cxe(excdb, q->exc, q->callsign, atoi(q->date_str));
    }
}


void update_exc_from_ctest(struct excdb *excdb, struct contest *ctest){
    int i;
    struct band *band;

    if (!ctest) return;

    for (i=0; i<ctest->bands->len; i++){
        band = (struct band *) g_ptr_array_index(ctest->bands, i);

        update_exc_from_band(excdb, band);
    }
    
}


struct rsgbdc{
    char *name;
    int maxcount;
};
  
static struct rsgbdc rsgbdc[] = {
    {"AB", 3 },
    {"BT", 6 },
    {"DD", 3 },
    {"DG", 3 },
    {"EH", 3 },
    {"FK", 3 },
    {"GS", 3 },
    {"HS", 3 },
    {"IV", 3 },
    {"KA", 3 },
    {"KY", 3 },
    {"PA", 3 },
    {"PH", 3 },
    {"TD", 3 },
    {"ZE", 3 },
    {NULL, 0 }
};


int rsgbdc_maxcount(char *exc){
    struct rsgbdc *p;

    for (p=rsgbdc; p->name != NULL; p++){
        if (strcasecmp(p->name, exc) != 0) continue;
        return p->maxcount;
    }
    return 1;
}

void usaca_multiple_exc(char *s){
    if (strcasecmp(s, "QC")==0)  { strcpy(s, "PQ"); return; }
    if (strcasecmp(s, "MAN")==0) { strcpy(s, "MB"); return; }
    if (strcasecmp(s, "ALB")==0) { strcpy(s, "AB"); return; }
    if (strcasecmp(s, "ALT")==0) { strcpy(s, "AB"); return; }
    if (strcasecmp(s, "YU")==0)  { strcpy(s, "YT"); return; }
}
