/*
    Tucnak - VHF contest log
    Copyright (C) 2002-2023  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 "bfu.h"
#include "fifo.h"
#include "language2.h"
#include "net.h"
#include "qsodb.h"
#include "stats.h"
#include "tsdl.h"

#define ADIF_STR(iname,item) if (strlen(item)) g_string_append_printf(gs,"<%s:%d>%s  ",iname, (int)strlen(item),safe_strncpy0(s,item,1024))
#define ADIF_STR_UC(iname,item) if (strlen(item)) g_string_append_printf(gs,"<%s:%d>%s  ",iname,(int)strlen(item),z_str_uc(safe_strncpy0(s,item,1024)))
#define ADIF_DATE(iname,item) if (strlen(item)) g_string_append_printf(gs,"<%s:%d>%s  ",iname, (int)strlen(item),safe_strncpy0(s,item,1024))
#define ADIF_INT(iname,item) sprintf(s,"%d",item); g_string_append_printf(gs,"<%s:%d>%s  ",iname, (int)strlen(s),s)


static gchar *convert_lt(gchar *s){
    char *c;

    for (c=s; *c!='\0'; c++)
        if (*c=='<') *c='(';
    return s;
}

static int write_adif_header(struct band *b, struct config_band *confb, FILE *f){
    char s[1024],dummy[10], *psect;
    GString *gs;
    int ret;
    
    switch(b->psect){
        case 1:  psect="Single"; break;
        case 2:  psect="Check"; break;
        default: psect="Multi"; break;
    }
    
    gs = g_string_sized_new(1024);
    g_string_append_printf(gs, "Created by TUCNAK-%s ver. %s\r\n", Z_PLATFORM, VERSION_STRING);
    g_string_append_printf(gs, "TName=%s\r\n", safe_strncpy0(s, ctest->tname, 69));
    if (!b->qsos->len)
        g_string_append_printf(gs, "TDate=%s;%s\r\n", ctest->cdate, ctest->cdate); 
    else
        g_string_append_printf(gs, "TDate=%d;%d\r\n", b->stats->first_date, b->stats->last_date); 
    g_string_append_printf(gs, "PCall=%s\r\n", z_str_uc(safe_strncpy0(s, ctest->pcall, 69)));
    g_string_append_printf(gs, "PWWLo=%s\r\n", z_str_uc(safe_strncpy0(s, ctest->pwwlo, 69)));
    g_string_append_printf(gs, "PExch=%s\r\n", z_str_uc(safe_strncpy0(s, ctest->pexch, 69)));
    g_string_append_printf(gs, "PAdr1=%s\r\n",    safe_strncpy0(s, ctest->padr1, 69));
    g_string_append_printf(gs, "PAdr2=%s\r\n",    safe_strncpy0(s, ctest->padr2, 69));
    g_string_append_printf(gs, "PSect=%s\r\n", psect);
/*    g_string_append_printf(gs, "PBand=%s\r\n", b->pband);*/
    g_string_append_printf(gs, "PClub=%s\r\n", z_str_uc(safe_strncpy0(s, ctest->pclub, 69)));
    g_string_append_printf(gs, "RName=%s\r\n",    safe_strncpy0(s, ctest->rname, 69));
    g_string_append_printf(gs, "RCall=%s\r\n", z_str_uc(safe_strncpy0(s, ctest->rcall, 69)));
    g_string_append_printf(gs, "RAdr1=%s\r\n",    safe_strncpy0(s, ctest->radr1, 69));
    g_string_append_printf(gs, "RAdr2=%s\r\n",    safe_strncpy0(s, ctest->radr2, 69));
    g_string_append_printf(gs, "RPoCo=%s\r\n",    safe_strncpy0(s, ctest->rpoco, 69));
    g_string_append_printf(gs, "RCity=%s\r\n",    safe_strncpy0(s, ctest->rcity, 69));
    g_string_append_printf(gs, "RCoun=%s\r\n",    safe_strncpy0(s, ctest->rcoun, 69));
    g_string_append_printf(gs, "RPhon=%s\r\n",    safe_strncpy0(s, ctest->rphon, 69));
    g_string_append_printf(gs, "RHBBS=%s\r\n",    safe_strncpy0(s, ctest->rhbbs, 69));
/*    g_string_append_printf(gs, "MOpe1=%s\r\n", z_str_uc(safe_strncpy0(s, b->mope1, 69)));
    g_string_append_printf(gs, "MOpe2=%s\r\n", z_str_uc(safe_strncpy0(s, b->mope2, 69)));
    g_string_append_printf(gs, "STXEq=%s\r\n",    safe_strncpy0(s, b->stxeq, 69));
    g_string_append_printf(gs, "SPowe=%s\r\n",    safe_strncpy0(s, b->spowe, 69));
    g_string_append_printf(gs, "SRXEq=%s\r\n",    safe_strncpy0(s, b->srxeq, 69));
    g_string_append_printf(gs, "SAnte=%s\r\n",    safe_strncpy0(s, b->sante, 69));
    g_string_append_printf(gs, "SAntH=%s\r\n",    safe_strncpy0(s, b->santh, 69));
    g_string_append_printf(gs, "CQSOs=%d;%d\r\n",    b->stats->nqsos, b->bandmulti);         
    g_string_append_printf(gs, "CQSOP=%d\r\n",       b->stats->nqsop);            
    g_string_append_printf(gs, "CWWLs=%d;%d;%d\r\n", g_hash_table_size(b->stats->wwls),  ctest->wwlbonu, ctest->wwlmult==0 ? 1 : ctest->wwlmult);  
    g_string_append_printf(gs, "CWWLB=%d\r\n",       g_hash_table_size(b->stats->wwls) * ctest->wwlbonu);
    g_string_append_printf(gs, "CExcs=%d;%d;%d\r\n", g_hash_table_size(b->stats->excs),  ctest->excbonu, ctest->excmult==0 ? 1 : ctest->excmult); 
    g_string_append_printf(gs, "CExcB=%d\r\n",       g_hash_table_size(b->stats->excs) * ctest->excbonu);             
    g_string_append_printf(gs, "CDXCs=%d;%d;%d\r\n", g_hash_table_size(b->stats->dxcs),  ctest->dxcbonu, ctest->dxcmult==0 ? 1 : ctest->dxcmult); 
    g_string_append_printf(gs, "CDXCB=%d\r\n",       g_hash_table_size(b->stats->dxcs) * ctest->dxcbonu);
    g_string_append_printf(gs, "CToSc=%d\r\n",       b->stats->ntotal); 
    g_string_append_printf(gs, "CODXC=%s;",  z_str_uc(safe_strncpy0(s, b->stats->odxcall, 20)));
    g_string_append_printf(gs, "%s;%d\r\n",    z_str_uc(safe_strncpy0(s, b->stats->odxwwl,  20)), b->stats->odxqrb_int);  */
    convert_lt(gs->str);
    strcpy(dummy,"1.00");
    ADIF_STR("adif_ver",dummy);
    g_string_append_printf(gs, "<eoh>\r\n");
    ret=fprintf(f, "%s", gs->str) != gs->len;
    g_string_free(gs, TRUE);
    
    return ret;
}

static int write_adif_qsos(struct band *b, struct config_band *confb, FILE *f){
    struct qso *q;
    GString *gs;
    int i;
    int ret;
    char s[1024];
    char *adif_modes[]={
        "",
        "SSB",
        "CW",
        "SSB",
        "SSB",
        "AM",   /* 5 */
        "FM",
        "RTTY",
        "SSTV",
        "ATV"  /* 9 */
    };
        

    gs = g_string_new("");
    
    
    for (i=0; i<b->qsos->len; i++){
        q = get_qso(b, i); 
	if (q->error && cfg->adif_export_mode_t2r == 2){
	    continue;
	}
        
        ADIF_STR_UC("BAND",confb->adifband);
        ADIF_DATE  ("QSO_DATE",q->date_str);
        ADIF_STR   ("TIME_ON",q->time_str);
        if (q->error){
            if (cfg->adif_export_mode_t2r == 1){
                ADIF_STR_UC("CALL",q->callsign);
            }else{
                ADIF_STR_UC("CALL","ERROR");
            }
        }else{
            ADIF_STR_UC("CALL",q->callsign);
        }
        ADIF_STR   ("MODE",adif_modes[abs(q->mode)%10]); /* TODO sent vs rcvd */
        ADIF_STR_UC("RST_SENT",q->rsts);
        if (ctest->excused > 0) {
            ADIF_STR_UC("STX_STRING",ctest->pexch);
        }
        if (ctest->qsoused > 0){
            ADIF_STR_UC("STX",q->qsonrs);
        }
        ADIF_STR_UC("RST_RCVD",q->rstr);
        if (ctest->excused > 0) {
            ADIF_STR_UC("SRX_STRING",q->exc);
        }
        if (ctest->qsoused > 0){
            ADIF_STR_UC("SRX",q->qsonrr);
        }
        ADIF_STR_UC("CONTEST_ID", ctest->tname);
        ADIF_STR_UC("GRIDSQUARE",q->locator);
		ADIF_INT("TX_PWR", atoi(b->spowe));
		ADIF_INT("QRB", (int)q->qrb);
        ADIF_INT   ("QTF", q->qtf);
        ADIF_INT   ("POINTS", q->qsop);
        ADIF_STR_UC("OPERATOR",q->operator_);
        ADIF_STR   ("COMMENT",q->remark);
        
        /* ES,MS,AUR,TR,RS,AUE,BS,EME,FAI,F2,ION,LOS,SAT,TEP,TRS,TRD */
        if (q->rsts && strlen(q->rsts)>=3 && q->rsts[2]=='A'){
            ADIF_STR   ("PROP_MODE","AUR");
        }else if (q->rsts && strlen(q->rsts)==3 && q->rsts[2]=='S'){
            ADIF_STR   ("PROP_MODE","RS");
        }else if (q->rsts && strlen(q->rsts)==3 && q->rsts[2]=='F'){
            ADIF_STR   ("PROP_MODE","FAI");
        }else{
            ADIF_STR   ("PROP_MODE","TR");
        }
        
        if (q->qrg){
            sprintf(s,"%d.%06d",(int)(q->qrg/1000000.0), (int)fmod(q->qrg, 1000000.0)); 
            g_string_append_printf(gs,"<%s:%d>%s  ", "FREQ", (int)strlen(s),s);
        }
            
        
        g_string_append_printf(gs, "<eor>\r\n");
        /*g_string_append_printf(gs, "%s\r\n", gs->str);*/
    }
    
    ret=fprintf(f, "%s", gs->str) != gs->len;
    g_string_free(gs, TRUE);
    
    return ret;
}


int export_all_bands_adif(void){
    struct band *band;
    struct config_band *confb;
    int i, err;
    gchar *filename;
    FILE *f;
    char callbuf[20];
    int ignoreerror=0;
    int header_saved=0;
    
    if (!ctest) return -1;
    
    dbg("export_all_bands_adif()\n");
    
    filename = g_strdup_printf("%s/%s_%s.adi",
                    ctest->directory,
                    ctest->cdate,
                    z_get_raw_call(callbuf,ctest->pcall));
    z_wokna(filename);    
    f=fopen(filename,"wb"); /* must be b for windoze */
    if (!f) {
        if (!ignoreerror) { errbox(VTEXT(T_CANT_WRITE), errno); ignoreerror=1;}
        g_free(filename);
        return -1;
    }
    
    err = 0;
    
    for (i=0; i<ctest->bands->len; i++){
        band = g_ptr_array_index(ctest->bands, i);
        confb = get_config_band_by_bandchar(band->bandchar);

		progress(VTEXT(T_EXPORTING_S), band->bandname);

        stats_thread_join(band);
        if (band->stats->nqsos <=0) continue;

        if (!header_saved){
            err|=write_adif_header(band,confb,f);
            header_saved=1;
        }
        err|=write_adif_qsos  (band,confb,f);
    }
    
    fclose(f);
    if (err) {
        if (!ignoreerror) { errbox(VTEXT(T_CANT_WRITE), 0); ignoreerror=1; }
        g_free(filename);
		progress(NULL);
        return -1;
    }
    log_addf(VTEXT(T_SAVED_S), filename);
    g_free(filename);
	progress(NULL);
    return 0;
}


static int adif_ignore(FILE *f){
    int cc;

    while ((cc=fgetc(f))!=EOF){
        if (cc=='<') return '<';
    }
    return EOF;
}

static int adif_read_tag(FILE *f, GString *gs, int *taglen){
    int cc;
    gchar **items;
    g_string_truncate(gs, 0);
    
    while ((cc=fgetc(f))!=EOF){
        if (cc=='>') {
            items = g_strsplit(gs->str,":",3);
            if (items[0]){
                if (!items[1]) 
                    *taglen=0;
                else
                    *taglen=atoi(items[1]);
                gs->str[strlen(items[0])]='\0';
                z_str_uc(gs->str);
            }
            g_strfreev(items);
            return 0;
        }
        g_string_append_c(gs, cc);
    }
    return EOF;
}

static gchar *adif_item(GHashTable *hash, gchar *key) {
    return g_hash_table_lookup(hash,key);
}
#define ADIF_ITEM(key) adif_item(hash,(key))

static gchar *safe_adif_item(GHashTable *hash, gchar *key) {
    gchar *tmp;
    tmp=g_hash_table_lookup(hash,key);
    return (tmp?tmp:"");
}
#define SAFE_ADIF_ITEM(key) safe_adif_item(hash,(key))

static int adif_add_qso(struct band *band, GHashTable *hash){
    struct qso *q;
    struct band *b;
    int i;
    
    /* mandatory */
    gchar *call, *rsts, *rstr,*qso_date,*time_on; 
    /* optional */
    gchar *mode;
    gchar *band_str;
    int qrg; /* kHz */
    
    call=ADIF_ITEM("CALL");
    rsts=ADIF_ITEM("RST_SENT");
    rstr=ADIF_ITEM("RST_RCVD");
    qso_date=ADIF_ITEM("QSO_DATE");
    time_on=ADIF_ITEM("TIME_ON");

    if (!call || strcasecmp(call,"ERROR")!=0){
        if (!call || !rsts || !rstr || !qso_date || !time_on){
            dbg("Incomplete record (call='%s', rsts='%s', rstr='%s', qso_date='%s', time_on='%s'\n", call, rsts, rstr,qso_date,time_on);
            return -1;
        }
    }

    b=NULL;
    band_str=ADIF_ITEM("BAND");
    if (band_str){
        for (i=0;i<cfg->bands->len;i++){
            struct config_band *cb=(struct config_band *)g_ptr_array_index(cfg->bands, i);
            if (!cb->adifband) continue;
            if (strcasecmp(cb->adifband, band_str)) continue;

            b=find_band_by_bandchar(cb->bandchar);
        }
    }
    
    qrg=1000*atoi(SAFE_ADIF_ITEM("FREQ"));
    
    if (!b && qrg>0){

        for (i=0;i<cfg->bands->len;i++){
            struct config_band *cb=(struct config_band *)g_ptr_array_index(cfg->bands, i);
            
            if (cb->qrg_min<=0) continue;
            if (cb->qrg_max<=0) continue;

            if (qrg<cb->qrg_min || qrg>cb->qrg_max) continue;

            b=find_band_by_bandchar(cb->bandchar);
        }
    }

    if (!b) b=band; /* default is active band */
    
    
    q=g_new0(struct qso, 1);
    
    q->date_str=g_strdup(SAFE_ADIF_ITEM("QSO_DATE"));
    
    q->time_str=g_strdup(SAFE_ADIF_ITEM("TIME_ON"));
    if (strlen(q->time_str)>4) q->time_str[4]='\0';
    
    if (strcasecmp(call,"ERROR")==0) {
        q->error=1;
        q->callsign=g_strdup("");
    }else{
        q->callsign=z_str_uc(g_strdup(call));
    }
    
    mode=SAFE_ADIF_ITEM("MODE");
    if (!strcasecmp(mode, "SSB")) q->mode=1;
    else if (!strcasecmp(mode, "CW")) q->mode=2;
    else if (!strcasecmp(mode, "RTTY")) q->mode=7;
    else if (!strcasecmp(mode, "AM")) q->mode=5;
    else if (!strcasecmp(mode, "FM")) q->mode=6;
    else if (!strcasecmp(mode, "SSTV")) q->mode=8;
    else if (!strcasecmp(mode, "ATV")) q->mode=9;
    
    q->rsts=g_strdup(SAFE_ADIF_ITEM("RST_SENT"));
    
    if (ctest->excused > 0) {
        ctest->pexch=g_strdup(SAFE_ADIF_ITEM("STX_STRING"));
    }

    if (ctest->qsoused > 0){
        if (ADIF_ITEM("STX")){
            q->qsonrs=g_strdup_printf("%03d", atoi(SAFE_ADIF_ITEM("STX")));
        }else{
            q->qsonrs=g_strdup_printf("%03d", atoi(SAFE_ADIF_ITEM("NO_SENT")));
        }
    } else {
        q->qsonrs=g_strdup("");
    }
    
    q->rstr=g_strdup(SAFE_ADIF_ITEM("RST_RCVD"));
    
    if (ctest->excused > 0) {
        q->exc=g_strdup(SAFE_ADIF_ITEM("SRX_STRING"));
    }

    if (ctest->qsoused > 0){
        if (ADIF_ITEM("SRX")){
            q->qsonrr=g_strdup_printf("%03d", atoi(SAFE_ADIF_ITEM("SRX")));
        }else{
            q->qsonrr=g_strdup_printf("%03d", atoi(SAFE_ADIF_ITEM("NO_RCVD")));
        }
    } else {
        q->qsonrr=g_strdup("");
    }
    
    q->locator=g_strdup(SAFE_ADIF_ITEM("GRIDSQUARE"));

    q->source=g_strdup(gnet->myid);

    q->operator_=fixsemi(g_strdup(SAFE_ADIF_ITEM("OPERATOR")));
    
    q->qrg = 0;

    q->stamp=time(NULL);

    q->remark=fixsemi(g_strdup(SAFE_ADIF_ITEM("COMMENT")));
	q->phase = 1;
    q->ser_id=-1; /* computed by add_qso_to_index */
    compute_qrbqtf(q);
    add_qso(b, q);
/*    update_stats(band->stats, band, q);*/

    
    return 0;
}



void import_adif(void *xxx, char *filename){
    FILE *f;
    int cc, taglen = 0;
    GString *gs, *gs2;
    GHashTable *hash;
    int gs2size;
    int i;

    f = fopen(filename, "r");
    if (!f) return;

    cc = fgetc(f);
    if (cc==EOF) { fclose(f); return;}
       
    if (cc!='<') adif_ignore(f);

    gs = g_string_sized_new(20);
    gs2 = g_string_sized_new(gs2size=20);
    hash = g_hash_table_new(g_str_hash, g_str_equal);
    
    while (adif_read_tag(f, gs, &taglen)!=EOF){
        if (taglen+1>gs2size){
            g_string_free(gs2,1);
            gs2=g_string_sized_new(gs2size=taglen+1);
        }
/*  g_string_set_size(gs2, taglen+1); not present in glib1*/
        memset(gs2->str, 0, taglen+1);
        if (taglen) 
            if (!fread(gs2->str,taglen,1, f)) break; 
/*      dbg("  tag='%s'  \tvalue: '%s'\n", gs->str, gs2->str);*/
        if (strcasecmp(gs->str, "EOR")==0){
/*            dbg("-------------------------\n");
            dbg_str_hash(hash);*/                       
            adif_add_qso(aband, hash);
            g_hash_table_foreach_remove(hash, zg_hash_free_item, NULL);
        }else{
            zg_hash_safe_insert(hash, g_strdup(gs->str), g_strdup(gs2->str));
        }
        
        
        
        adif_ignore(f);
    }
    
    
    g_string_free(gs, TRUE);
    g_string_free(gs2, TRUE);
    g_hash_table_foreach_remove(hash, zg_hash_free_item, NULL);
    
    
	g_hash_table_foreach(ctest->bystamp, foreach_source_qsort_by_stamp, NULL);
    z_ptr_array_qsort(ctest->allqsos, compare_date_time_qsonrs);

/*   don't needed   DIRTY_BAND(); */

    for (i=0;i<ctest->bands->len;i++){
        struct band *b=(struct band *)g_ptr_array_index(ctest->bands,i);
        recalc_stats(b);
        clear_tmpqsos(b, 1);
        
        if (ctest->qsoused && b->qsos->len+1 != atoi(b->tmpqsos[0].qsonrs)){
            g_free(b->tmpqsos[0].qsonrs);
            b->tmpqsos[0].qsonrs = g_strdup_printf("%03d", b->qsos->len+1);
        }
    }

    fclose(f);
    return;
}

