/*
    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 "ac.h"
#include "bfu.h"
#include "chart.h"
#include "control.h"
#include "cwdaemon.h"
#include "cwdb.h"
#include "edi.h"
#include "fifo.h"
#include "hf.h"
#include "inputln.h"
#include "main.h"
#include "map.h"
#include "menu.h"
#include "net.h"
#include "oct8tor.h"
#include "qrvdb.h"
#include "qsodb.h"
#include "rain.h"
#include "rc.h"
#include "rotar.h"
#include "session.h"
#include "sked.h"
#include "ssbd.h"
#include "stats.h"
#include "subwin.h"
#include "trig.h"
#include "wizz.h"
#include "zstring.h"

struct contest *ctest;
struct band *aband;

char *mode_msg[]={
    CTEXT(T_UNSPECIFIED),
    CTEXT(T_SSB),
    CTEXT(T_CW),
    CTEXT(T_SSBS_CWR),
    CTEXT(T_CWS_SSBR),
    CTEXT(T_AM),
    CTEXT(T_FM),
    CTEXT(T_RTTY),
    CTEXT(T_SSTV),
    CTEXT(T_ATV)   /* 9 */
};


int init_ctest(){
    /*dbg("init_ctest\n");*/
    if (ctest) return 0;

    ctest = g_new0(struct contest, 1);
    ctest->bands = g_ptr_array_new();
    ctest->bystamp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_bystamp_value);
    ctest->allqsos = z_ptr_array_new();
    ctest->spypeers = g_ptr_array_new();
    ctest->allb_prefs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
    ctest->exclen = 0;
	ctest->phase = 1;

/*    ctest->serial = 1;*/
    ctest->minute_timer_id=zselect_timer_new(zsel, 100, timer_minute_stats_all, NULL);
    return 0;
}

int new_ctest(char *tdate){
    struct stat st;
    int counter;
    time_t now;
    struct tm tm;
    gchar *c;
    static char s[1024]; /* for msg_box */
    int nowdate_int, ctestdate_int;
	FILE *f;

    now = time(NULL);
    gmtime_r(&now, &tm);
    
    if (tdate){ 
        ctest->cdate = g_strdup(tdate);
        nowdate_int = (tm.tm_year+1900)*10000 + (tm.tm_mon+1)*100 + tm.tm_mday;
        ctestdate_int = atoi(ctest->cdate);
        if (/*nowdate_int >= ctestdate_int && */nowdate_int <= ctestdate_int + 2) 
            ctest->oldcontest = 0;
        else
            ctest->oldcontest = 1;
    }else{
        ctest->cdate = g_strdup_printf("%4d%02d%02d", tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday );
        ctest->oldcontest = 0;
    }
    
	ctest->directory = g_strdup_printf("%s/%s", logs_dir, ctest->cdate);
    
    counter=0;
    while (stat(ctest->directory,&st)==0){
        counter++;
        g_free(ctest->directory);
		ctest->directory = g_strdup_printf("%s/%s.%d", logs_dir, ctest->cdate, counter);
    }
        
    if (z_mkdir_p(ctest->directory, 0777)){

        g_snprintf(s,1000, VTEXT(T_CANT_CREATE_HOME_S),ctest->directory);
        errbox(s,errno);
        free_ctest();
        return -1;
    }

    
    c = g_strdup_printf("%s/desc.lock",ctest->directory);
	z_wokna(c);
    ctest->lockfile = fopen(c, "wt");
    if (!ctest->lockfile){
        g_snprintf(s,1000, "%s %s", VTEXT(T_CANT_WRITE),c);
        errbox(s,errno);
        g_free(c);
        free_ctest();
        return -1;
    }
    g_free(c);
    
    if (z_lockf(fileno(ctest->lockfile), F_TLOCK, 0, 1)){
        g_snprintf(s,1000, "%s %s", VTEXT(T_CANT_LOCK),c);
        errbox(s,errno);
        free_ctest();
        return -1;
    }
    
    c = g_strdup_printf("%s/desc",ctest->directory);
	z_wokna(c);
    f = fopen(c, "wt");
    if (!f){
        g_snprintf(s,1000, "%s %s", VTEXT(T_CANT_WRITE),c);
        errbox(s,errno);
        g_free(c);
        free_ctest();
        return -1;
    }
    g_free(c);

    ctest->exclen = strlen(ctest->pexch);
    fprintf(f,"%s %s %s\n", ctest->cdate, z_str_uc(ctest->pcall), ctest->tname);    
	fclose(f);
    
    
    if (ctest->logfile) {
        fclose(ctest->logfile);
        ctest->logfile=NULL;
    }
    
    c=g_strdup_printf("%s/log", ctest->directory);
    ctest->logfile = fopen(c, "at");
    if (!ctest->logfile){
        g_snprintf(s,1000, VTEXT(T_CANT_OPEN_S),c);
        g_free(c);
        msg_box(NULL, VTEXT(T_ERROR), AL_CENTER, s, NULL, 1, VTEXT(T_OK), NULL, B_ENTER | B_ESC);
        g_free(c);
        free_ctest();
        return -1;
    }
    g_free(c);
    setvbuf(ctest->logfile, NULL, _IONBF, 0);

    
    set_ctest_title();
	update_hw();
#ifdef Z_HAVE_SDL
    maps_reload();
	maps_update_showwwls();
	rain_reload();
#endif  

#ifdef OCT8TOR
	if (aband != NULL){
		struct config_band *cb = get_config_band_by_bandchar(aband->bandchar);
		oct8tor_send_band(goct8tor, cb->adifband);
	}
#endif

    return 0; 
}




int load_ctest_from_mem(struct contest *ctest, gchar *datedir, GHashTable *hash){
    gchar *c; 

    ctest->directory = g_strdup(datedir); 
    
/*    if (ctest->tname) g_free((gpointer)ctest->tname);
    ctest->tname= g_strdup((gchar *)g_hash_table_lookup(hash, "tname")); */

    STORE_HASH_STR(ctest, tname);
    STORE_HASH_STR(ctest, tdate);
    STORE_HASH_STR(ctest, pcall);
    STORE_HASH_STR(ctest, pwwlo);
    STORE_HASH_STR(ctest, pexch);
    STORE_HASH_STR(ctest, padr1);
    STORE_HASH_STR(ctest, padr2);
    STORE_HASH_STR(ctest, pclub);
    STORE_HASH_STR(ctest, rname);
    STORE_HASH_STR(ctest, rcall);
    STORE_HASH_STR(ctest, radr1);
    STORE_HASH_STR(ctest, radr2);
    STORE_HASH_STR(ctest, rpoco);
    STORE_HASH_STR(ctest, rcity);
    STORE_HASH_STR(ctest, rcoun);
    STORE_HASH_STR(ctest, rphon);
    STORE_HASH_STR(ctest, rhbbs);

    c = (gchar *)g_hash_table_lookup(hash, "qsoused"); 
    if (c) ctest->qsoused= atoi(c); 
    else ctest->qsoused = 0; 

    STORE_HASH_STR(ctest, default_rs);
    STORE_HASH_STR(ctest, default_rst);
    STORE_HASH_INT(ctest, qsoused);
    STORE_HASH_INT(ctest, qsomult);
    STORE_HASH_INT(ctest, qsoglob);
    STORE_HASH_INT(ctest, prefmult);
    STORE_HASH_INT(ctest, prefglob);
    STORE_HASH_INT(ctest, dxcbonu);
    STORE_HASH_INT(ctest, dxcmult);
    STORE_HASH_INT(ctest, excused);
	STORE_HASH_ENUM(ctest, exctype, enum exctype);
    STORE_HASH_STR(ctest, excname);
    STORE_HASH_INT(ctest, excbonu);
    STORE_HASH_INT(ctest, excmult);
    STORE_HASH_INT_DEF(ctest, exccfm, 1);
    STORE_HASH_INT(ctest, qsop_method);
    STORE_HASH_INT(ctest, rstused);
    STORE_HASH_INT(ctest, defrstr);
    STORE_HASH_INT(ctest, total_method);
    STORE_HASH_INT(ctest, wwltype);
    STORE_HASH_INT(ctest, wwlused);
    STORE_HASH_INT(ctest, wwlbonu);
    STORE_HASH_INT(ctest, wwlmult);
    STORE_HASH_INT_DEF(ctest, wwlcfm, 1);
    STORE_HASH_ENUM_DEF(ctest, tttype, enum tttype, TT_RSTS);
	STORE_HASH_INT(ctest, expmode);
	STORE_HASH_INT_DEF(ctest, phase, 1);
    
    set_ctest_title();
   
    return 0; 
}



struct band *init_band(struct config_band *confb, GHashTable *opt_band, struct zstring *zs, struct wizz_qsomult_item *wqi){
    struct band *b;
    gchar *c;
    static char s[1024]; /* for msg_box */

    if (init_ctest()) return NULL;
    
    if (confb){
        b=find_band_by_pband(confb->pband);
        if (b) return b;
    }

    b = g_new0(struct band, 1);
	b->qsomultb = 1;
	if (wqi) b->qsomultb = wqi->qsomult;

    b->qsos = g_ptr_array_new();
	b->rawqsoshash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
//    b->mode  = MOD_SSB_SSB;
//    b->bandmulti = 1;
/*    b->spymode = SM_INPUTLINE;*/
    if (confb){   /* usally new contest */
        b->bandchar = confb->bandchar;
        b->pband = g_strdup(confb->pband);
        if (confb->qrg_min>30000)
            b->bandname = g_strdup(b->pband);
        else
            b->bandname = g_strdup(confb->adifband);
        b->qrg_min = confb->qrg_min;
        b->qrg_max = confb->qrg_max;
        
        b->readonly = confb->readonly;

        b->psect = confb->psect; /* gint */
		b->opsect = g_strdup(confb->opsect);

        if (cfg->operator_ && *cfg->operator_){
            b->operator_ = fixsemi(g_strdup(cfg->operator_));
        }else{
            b->operator_ = fixsemi(g_strdup(z_get_raw_call(s,ctest->pcall)));
        }
        b->stxeq = g_strdup(confb->stxeq);
        b->spowe = g_strdup(confb->spowe);
        b->srxeq = g_strdup(confb->srxeq);
        b->sante = g_strdup(confb->sante);
        b->santh = g_strdup(confb->santh);
        b->mope1 = g_strdup(confb->mope1);
        b->mope2 = g_strdup(confb->mope2);
        b->remarks = g_strdup(confb->remarks);
        b->skedqrg = g_strdup(confb->skedqrg);
        b->band_sw = confb->band_sw;
        b->wwlradius = confb->wwlradius;

/*        struct zstring *zs = config_band_format(confb);
        dbg("zs='%s'\n", zs->str);
        struct config_band *cb2 = config_band_parse(zs);
        printf("%p\n", cb2);
        */
    }
    
    if (opt_band){ /* usually loading contest */
        GHashTable *hash = opt_band;
        gchar *ccc;
		struct config_band *tmpconfb;

        STORE_HASH_STR(b, pband);   
        STORE_HASH_CHR(b, bandchar);
        tmpconfb=get_config_band_by_pband(b->pband);
        if (tmpconfb){
            if (!b->bandchar) b->bandchar=tmpconfb->bandchar;
            if (tmpconfb->qrg_min>30000)
                b->bandname = g_strdup(b->pband);
            else
                b->bandname = g_strdup(tmpconfb->adifband);
            b->qrg_min = tmpconfb->qrg_min;
            b->qrg_max = tmpconfb->qrg_max;
            b->band_sw = tmpconfb->band_sw;
            b->wwlradius = tmpconfb->wwlradius;
        }else{
            // contest created under other band configuration
            b->bandname = g_strdup(b->pband);
        }
//        STORE_HASH_INT(b, mode);   
        STORE_HASH_ENUM(gses, mode, enum modes);   
        //STORE_HASH_STR(b, operator_);
		g_free(b->operator_);
		b->operator_ = g_strdup((char *)g_hash_table_lookup(hash, "operator"));
        if (!b->operator_){
            b->operator_ = fixsemi(g_strdup(z_get_raw_call(s,ctest->pcall)));
        }
        
        ccc = (gchar *)g_hash_table_lookup(hash, "psect"); 
        /*dbg("c=%s\n",ccc);*/
        if (isdigit(ccc[0]))
            b->psect=atoi(ccc);
        else
            b->psect=get_psect_int(ccc);
		STORE_HASH_STR(b, opsect);
                
        STORE_HASH_INT(b, readonly);
        STORE_HASH_INT(b, saveid);
        b->saveid++;
        STORE_HASH_STR(b, stxeq);
        STORE_HASH_STR(b, spowe);
        STORE_HASH_STR(b, srxeq);
        STORE_HASH_STR(b, sante);
        STORE_HASH_STR(b, santh);
/*        STORE_HASH_INT(b, mode);*/
        STORE_HASH_STR(b, mope1);
        STORE_HASH_STR(b, mope2);
 /*        STORE_HASH_STR(b, remarks); NO */
        STORE_HASH_STR(b, skedqrg);
        if (!b->skedqrg) b->skedqrg=g_strdup("");
		STORE_HASH_INT_DEF(b, qsomultb, 1);
		if (b->qsomultb == 0) b->qsomultb = 1; // 3.12-3.13 set to 0 -> 0 points for QSO
		STORE_HASH_DOUBLE_DEF(b, qrg, 0.0);
    }   
    b->bi = toupper(b->bandchar) - 'A';
    //dbg("%s bandname=%s\n",__FUNCTION__, b->bandname);  
    if (zs){
        struct config_band *tmpconfb;

        band_parse(zs, b);

        if (cfg->operator_ && *cfg->operator_){
            b->operator_ = fixsemi(g_strdup(cfg->operator_));
        }else{
            b->operator_ = fixsemi(g_strdup(z_get_raw_call(s,ctest->pcall)));
        }
        tmpconfb=get_config_band_by_pband(b->pband);
        if (tmpconfb) b->readonly = tmpconfb->readonly;
    }

    
    b->il = g_new0(struct inputln,1);
    b->il->term = term;
    b->il->enter = process_input;
    b->il->enterdata = (void *)b;
    b->il->upconvert = 1;
    b->il->valid_chars = VALID_CHARS; 
    b->il->readonly  = b->readonly;
    b->il->band = b;

    {
        struct event ev = {EV_INIT, 0,0,0};
        inputln_func(b->il,&ev);
    }

    b->stats = init_stats(b->bandchar);
	b->tmpstats = init_stats(b->bandchar);
    MUTEX_INIT(b->stats);
    
    c = g_strdup_printf("%s/%c.swp",ctest->directory, z_char_lc(b->bandchar));
            
    clear_tmpqsos(b, 1);
    
    if (!aband){
        aband=b;
        il_set_focus(aband->il);
    }
    
//    dbg("swap=%s\n", c);
    b->swap = fopen(c,"at");
    if (!b->swap){
        log_addf(VTEXT(T_CANT_APPEND_S),c);
        g_free(c);
        free_band(b);
        return NULL;
    }
    g_free(c);
    fprintf(b->swap, "ST %d\n", b->saveid-1);

   
    b->swapfifo = init_fifo(500);
    b->qs = g_ptr_array_new();
    b->oqs = g_ptr_array_new();
    b->unfi = init_fifo(100);        
    b->skeds = g_ptr_array_new();        
    b->statsfifo1 = init_fifo(1000);        
    
    g_ptr_array_add(ctest->bands, b);
    ctest->qrv |= 1 << b->bi;
    aband->tmplocqso.band=aband;
    
/*    init_spypeer(b->spypeers, "127.0.0.1:55555");
    init_spypeer(b->spypeers, "127.0.0.1:55556");*/
    //dbg("bandname='%s'\n", b->bandname);
    
    return b;
}


struct band *find_band_by_pband(char *pband){
    int i;
    struct band *b;

    if (!ctest) return NULL;
    
    for (i=0;i<ctest->bands->len; i++){
        b = (struct band *)g_ptr_array_index(ctest->bands,i);
        if (!pband) continue;
        if (strcasecmp(pband, b->pband)==0){
            return b;
        }
    }
    return NULL;
}

struct band *find_band_by_bandchar(char bandchar){
    int i;
	struct band *b;

	if (!ctest) return NULL;

	bandchar = z_char_uc(bandchar);
    for (i=0;i<ctest->bands->len; i++){
        b = (struct band *)g_ptr_array_index(ctest->bands,i);
        if (bandchar == z_char_uc(b->bandchar)){
            return b;
        }
    }
    return NULL;
}
        
struct band *find_band_by_qrg(double freq){
    struct band *b;
    int i;
	
    if (!ctest) return NULL;

    for (i=0;i<ctest->bands->len; i++){
        b = (struct band *)g_ptr_array_index(ctest->bands,i);
        if (freq < b->qrg_min * 1000.0) continue;
        if (freq > b->qrg_max * 1000.0) continue;
        return b;
    }
    return NULL;
}

struct band *init_qrv_bands(struct wizz_item *wi){
    int i;
    struct config_band *confb;

    /*dbg("init_qrv_bands(%p)\n", wi);*/
    for (i=0;i<cfg->bands->len; i++){
        confb = (struct config_band *)g_ptr_array_index(cfg->bands,i);
        if (!confb) continue;
        if (confb->qrvnow){
			struct wizz_qsomult_item *wqi = NULL;	// search for wizz qsomult item
            if (wi){
                for (wqi = wi->qsomults; wqi != NULL; wqi = wqi->next){
                    if (wqi->bandchar == z_char_uc(confb->bandchar)) break;
                }
            }
            init_band(confb, NULL, NULL, wqi);
        }
    }
    return NULL;
}



void clear_tmpqsos(struct band *b, int set_defaults){
    int i;
    char *c;

    if (!b) return;

	b->dupe_qso = NULL;
    b->last_item = LI_NONE;

    for(i=0;i<TMP_QSOS;i++){
        CLEAR_TMPQSO_STRING(b,date_str);
        CLEAR_TMPQSO_STRING(b,time_str);
        CLEAR_TMPQSO_STRING(b,callsign);
        CLEAR_TMPQSO_STRING(b,rsts);
        CLEAR_TMPQSO_STRING(b,rstr);
        CLEAR_TMPQSO_STRING(b,qsonrs);
        CLEAR_TMPQSO_STRING(b,qsonrr);
        CLEAR_TMPQSO_STRING(b,locator);
        CLEAR_TMPQSO_STRING(b,exc);
        CLEAR_TMPQSO_STRING(b,name);

        CLEAR_TMPQSO_GINT(b,qrb);/* qrb is double */
        CLEAR_TMPQSO_QTF(b);
        CLEAR_TMPQSO_GINT(b,ucallsign);
        CLEAR_TMPQSO_GINT(b,ulocator);
        CLEAR_TMPQSO_GINT(b,uexc);
        CLEAR_TMPQSO_GINT(b,uqrb);
        CLEAR_TMPQSO_GINT(b,uqtf);
        CLEAR_TMPQSO_GINT(b,suspcallsign);
        CLEAR_TMPQSO_GINT(b,susplocator);
        CLEAR_TMPQSO_GINT(b,suspexc);
        CLEAR_TMPQSO_GINT(b,qsl);
        CLEAR_TMPQSO_STRING(b,remark);
		CLEAR_TMPQSO_GINT(b, dupe);
	}
    CONDGFREE(b->unres);
    CONDGFREE(b->qrv_str);
    CONDGFREE(b->wkd_str);
    wkd_tmpqso(b, WT_CLEAR, "");
    CONDGFREE(b->call_played);
    b->agcall = 0;
    //dbg("clear_tmpqso: call_played='%s' agcall=%d\n", b->call_played, b->agcall);
    
    if (set_defaults){

		if (ctest->qsoused){
			c=g_strdup_printf("%03d",b->qsos->len+1);
			ADD_TMPQSO_STRING(b,qsonrs,c,1,udummy);
			wkd_tmpqso(b, WT_QSONRS, c);
			g_free(c);
		}

		default_rst_to_tmpqsos(b);
#ifdef HAVE_SNDFILE
		ssbd_callsign(gssbd, "");
		gssbd->code = 0;
#endif
	}
	ac_update_tmpctp("");
	update_susp(b);
}

void default_rst_to_tmpqsos(struct band *b){
    gchar *c;
    int i;
    
    if (!ctest) return;
    for(i=0;i<TMP_QSOS;i++)  CLEAR_TMPQSO_STRING(b,rsts);

    /*if (!ctest->qsoused) return;*/
    
    if (gses->mode == MOD_CW_CW)
        c=g_strdup(cfg->default_rst);
    else
        c=g_strdup(cfg->default_rs);
       
    ADD_TMPQSO_STRING(b,rsts,c,1,udummy);
    wkd_tmpqso(b, WT_RSTS, c);

    if (ctest->defrstr){
      //  for(i=0;i<TMP_QSOS;i++)  CLEAR_TMPQSO_STRING(b,rstr);
        ADD_TMPQSO_STRING(b,rstr,c,1,udummy);
        wkd_tmpqso(b, WT_RSTR, c);
    }
    
    g_free(c);
}
       
#define APPEND_Q(item) \
    if (item) g_string_append(gs, item);
    
int write_qso_to_swap(struct band *b, struct qso *q){
    GString *gs;

    gs = g_string_new("Q ");
    
    add_qso_str1(gs, q, b);
    add_qso_str3(gs, q, b);
    
    /*fprintf(b->swap, "%s\n", gs->str);*/
    fprintf(b->swap, "%s  qrb=%4.1f\n", gs->str,q->qrb);
    fflush(b->swap);
    fifo_adds(b->swapfifo, gs->str);
    g_string_free(gs, TRUE);
    return 0;
}

/* after adding you must call z_ptr_array_qsort */
void add_qso(struct band *b, struct qso *q){

    q->band = b; /* don't dealloc! */
    /*q->serial = ctest->serial++;*/
    
    stats_thread_kill(b);
    g_ptr_array_add(b->qsos,q);
    add_qso_to_index(q, 0);

	if (!q->dupe && !q->error){
		char raw[30];
		struct qso *qq;

		z_get_raw_call(raw, q->callsign);
		qq = (struct qso *)g_hash_table_lookup(b->rawqsoshash, raw);
		if (!qq) g_hash_table_insert(b->rawqsoshash, g_strdup(raw), q);
   // }else{
   // 	log_addf("Warning: QSO %c %s has dupe=%d error=%d", q->band->bandchar, q->callsign, q->dupe, q->error);
	}

    dirty_band(b);
}

struct qso *get_qso(struct band *b, gint i){
    
    if (i<0 || i >=b->qsos->len) return NULL;
    return (struct qso *)g_ptr_array_index(b->qsos, i);
}

struct qso *get_gqso(struct band *b, struct subwin *sw, gint i){
    
	if (sw->allqsos){
        if (i<0 || i >=ctest->allqsos->len) return NULL;
        return (struct qso *)g_ptr_array_index(ctest->allqsos, i);
    }else{
        if (i<0 || i >=b->qsos->len) return NULL;
        return (struct qso *)g_ptr_array_index(b->qsos, i);
    }
}

// returns if callsign is worked. Callsign can be worked more times, it returns only one.
struct qso *get_qso_by_callsign(struct band *b, gchar *callsign){
    char raw[25];
	struct qso *q;

    z_get_raw_call(raw, callsign);
	q = (struct qso *)g_hash_table_lookup(b->rawqsoshash, raw);
	return q;
/*    struct qso *qso;
    int i;
    char raw[25], qraw[25];

    z_get_raw_call(raw, callsign);
    
    for(i=0;i<b->qsos->len; i++){
        qso = g_ptr_array_index(b->qsos, i);

        if (qso->error) continue;
        
        z_get_raw_call(qraw, qso->callsign);
        if (strcasecmp(raw, qraw)==0) return qso;
    }
    return NULL;*/
}

/*int get_qso_index_by_callsign(struct band *b, gchar *callsign){
    struct qso *qso;
    int i;
    char raw[25], qraw[25];

    z_get_raw_call(raw, callsign);
    
              // TODO callsigns in hash 
    for(i=0;i<b->qsos->len; i++){
        qso = (struct qso *)g_ptr_array_index(b->qsos, i);

        if (qso->error) continue;
        
        z_get_raw_call(qraw, qso->callsign);
        if (strcasecmp(raw, qraw)==0) return i;
    }
    return -1;
}	*/

struct qso *get_qso_by_id(struct band *b, gchar *source, gint ser_id){
    struct qso *qso;
    int i;
            
    /* TODO id's in hash */
    /*dbg("get_qso_by_id(src=%s, ser_id=%d) \n", source, ser_id);*/
    for(i=0;i<b->qsos->len; i++){
        qso = (struct qso *)g_ptr_array_index(b->qsos, i);

        /*dbg("     %s %s %d ",qso->callsign, qso->source, qso->ser_id);*/
        if (ser_id != qso->ser_id) continue;
        if (strcasecmp(source, qso->source)!=0) continue;
        /*dbg("     OK\n");*/
        return qso;
    }
    /*dbg("     NULL\n");*/
    return NULL;
}

struct qso *get_qso_by_qsonr(struct band *b, gint qsonrs){
    struct qso *qso;
    int i;
              /* TODO id's in hash */
    for(i=0;i<b->qsos->len; i++){
        qso = (struct qso *)g_ptr_array_index(b->qsos, i);

        if (qsonrs != atoi(qso->qsonrs)) continue;
        return qso;
    }
    return NULL;
}

void add_error(struct band *b, gchar *remark){
    struct qso *eq;
    time_t now;
    struct tm utc;

    if (!ctest && !b) return;
    if (b->readonly) return;    

    log_addf(VTEXT(T_ADDED_ERROR), b->bandchar, b->qsos->len+1);
    
    time(&now);
    gmtime_r(&now, &utc);
           
    eq = g_new0(struct qso, 1);
    eq->source    = g_strdup(gnet->myid);
    eq->operator_ = g_strdup(b->operator_);
    if (b->tmpqsos[0].date_str){
        eq->date_str = g_strdup(b->tmpqsos[0].date_str);
        eq->time_str = g_strdup(b->tmpqsos[0].time_str);
    }else{
       eq->date_str = g_strdup_printf("%4d%02d%02d",1900+utc.tm_year, 1+utc.tm_mon, utc.tm_mday);
       eq->time_str = g_strdup_printf("%02d%02d",utc.tm_hour, utc.tm_min);    
    }
    eq->callsign = g_strdup(b->tmpqsos[0].callsign&&b->tmpqsos[0].ucallsign?b->tmpqsos[0].callsign:"ERROR");    
    eq->rsts     = g_strdup(b->tmpqsos[0].rsts?b->tmpqsos[0].rsts:"");        
    eq->rstr     = g_strdup(b->tmpqsos[0].rstr?b->tmpqsos[0].rstr:"");    
    eq->qsonrs   = g_strdup(b->tmpqsos[0].qsonrs?b->tmpqsos[0].qsonrs:"");    
    eq->qsonrr   = g_strdup(b->tmpqsos[0].qsonrr?b->tmpqsos[0].qsonrr:"");    
    eq->exc      = g_strdup(b->tmpqsos[0].exc?b->tmpqsos[0].exc:"");    
    eq->locator  = g_strdup(b->tmpqsos[0].locator&&b->tmpqsos[0].ulocator?b->tmpqsos[0].locator:"");    
    eq->error    = 1;
    eq->remark   = g_strdup(remark?remark:"");
	eq->phase    = ctest->phase;
    eq->qrg      = 0.0;
    eq->ser_id   = -1;  /* computed by add_qso_to_index */
    
    add_qso(b, eq);
    stats_thread_join(b);
    update_stats(b, b->stats, eq);  
    recalc_allb_stats();
    minute_stats(b);
    recalc_statsfifo(b);
    
    if (b->tmpqsos[0].qsonrs != NULL && b->qsos->len+1 != atoi(b->tmpqsos[0].qsonrs)){
        g_free(b->tmpqsos[0].qsonrs);
        if (ctest->qsoused)
            b->tmpqsos[0].qsonrs = g_strdup_printf("%03d", b->qsos->len+1);
        else
            b->tmpqsos[0].qsonrs = g_strdup("");
    }
    wkd_tmpqso(aband, WT_QSONRS, TMPQ.qsonrs);

    replicate_qso(NULL, eq);
    redraw_later();
    check_autosave();

}

/* use update_stats() after this */
void qso_mark_as_error(struct band *b, gint i){
    struct qso *qso;
    
    qso = g_ptr_array_index(b->qsos, i);
    qso->error = 1;
    qso->qsop = 0;
    dirty_band(b);
    replicate_qso(NULL, qso);
}

int add_tmpxchg(struct band *band, gchar *xchg){
    if (band->ignoreswap) return -1;
    fprintf(band->swap, "# %s\n", xchg);
    fflush(band->swap);
    fifo_adds(band->swapfifo, xchg );
    return 0;
}

int add_swap(struct band *band, gchar *s){
    if (band->ignoreswap) return -1;
    fprintf(band->swap, "%s\n", s);
    fflush(band->swap);
    fifo_adds(band->swapfifo, s);
    return 0;
}


//tern int *pnn;
void activate_band(struct band *b){
	int i;
    struct event ev={EV_RESIZE, 0, 0, 0};
	struct subwin *sw;
    
    /*dbg("activate_band(ses=%p,b=%p) %c\n",ses,b);*/
    /* dbg("aband=%p\n",aband);*/
    if (!b) return;


 // dbg("pnn=%d\n", *pnn);
    il_unset_focus(aband->il);
    aband=b;
    //sw_all_func(&ev, 1);
	win_func(gses->win, &ev, 1 );
/*    sw_default_func(gses->ontop, &ev, 1);*/
	sw = gses->ontop;
	if (gses->ontop->type == SWT_QSOS && aband) gses->ontop->cur = QSOS_LEN;
	gses->ontop->check_bounds(gses->ontop);
	sw_unset_focus();
    il_set_focus(aband->il);
    get_band_qs(aband, aband->il->cdata);
    get_oband_qs(aband, aband->il->cdata);
    get_cw_qs(aband->il->cdata);
    get_hf_dxc(aband->il->cdata);
    if (gses->ontop){
        switch(gses->ontop->type){
            case SWT_STAT:
                recalc_statsfifo(b);
                break;
            case SWT_QRV:
	        case SWT_KST:
                sw_qrv_sort(qrv);
                break;
            default:
                break;
        }
    }


    cwdaemon_band(cwda, b->band_sw);
    default_rst_to_tmpqsos(aband);

#ifdef HAVE_HAMLIB
	for (i = 0; i < gtrigs->trigs->len; i++){
		struct trig *trig = (struct trig *)g_ptr_array_index(gtrigs->trigs, i);
        if (trig->qrg_t2r){
            if (aband->qrg != 0){
                trig_set_qrg(trig, aband->qrg);
            }
        }
        trig_resend_freq(trig);
    }
#endif
#ifdef OCT8TOR
	struct config_band *cb = get_config_band_by_bandchar(b->bandchar);
	oct8tor_send_band(goct8tor, cb->adifband);
#endif
	wkd_send_all(aband);

    redraw_later();
    qrv_recalc_gst(qrv);
    maps_reload();
    chart_reload();
#ifdef Z_HAVE_SDL
	rain_reload();
#endif
}




struct config_band *get_config_band_by_bandchar(char bandchar){
    struct config_band *confb;
    int i;

	bandchar = z_char_uc(bandchar);
    for (i=0;i<cfg->bands->len;i++){
        confb = (struct config_band *)g_ptr_array_index(cfg->bands, i);
        if (bandchar == z_char_uc(confb->bandchar))
            return confb;
    }
    return NULL;
}

struct config_band *get_config_band_by_pband(char *pband){
    struct config_band *confb;
    int i;

    for (i=0;i<cfg->bands->len;i++){
        confb = (struct config_band*)g_ptr_array_index(cfg->bands, i);
        if (strcasecmp(pband, confb->pband)==0)
            return confb;
    }
    return NULL;
}

struct config_band *get_config_band_by_qrg(int qrg){ /* kHz */
    struct config_band *confb;
    int i;

    for (i=0;i<cfg->bands->len;i++){
        confb = (struct config_band *)g_ptr_array_index(cfg->bands, i);
        if (qrg>=confb->qrg_min && qrg<=confb->qrg_max)
            return confb;
    }
    return NULL;
}


void foreach_add_latests_str(gpointer key, gpointer value, gpointer data){
    ZPtrArray *ia;
    GString *gs;
    struct qso *q;

    ia = (ZPtrArray *)value;
    gs = (GString *)data;
    
    if (ia->len<=0) return;
    if (strcmp((const char *)key,"neterr")==0) return;
    q = (struct qso *) z_ptr_array_index(ia, ia->len - 1);
    if (q){
        g_string_append_printf(gs, "%s;%d;", (char *)key, (int)q->stamp);
    }else{ /* not reached, NULL can be only in the middle of iarray */
        dbg("ERROR! q is NULL (foreach_add_latests_str)\n");
        g_string_append_printf(gs, "%s;NULL;", (char *)key);
    }
}

gchar *get_latests_str(){
    GString *gs;
    gchar *c;

    gs = g_string_new("LA ");
    
    g_hash_table_foreach(ctest->bystamp, foreach_add_latests_str, gs);
    /*dbg("get_latests_str='%s'\n", gs->str);*/
    g_string_append(gs, "\n");
    c = g_strdup(gs->str);
    g_string_free(gs, 1);
    return c;
}

int compare_stamp(const void *a, const void *b){
    struct qso **qa, **qb;

    qa = (struct qso **)a;
    qb = (struct qso **)b;
    
    if (!*qa && !*qb) return 0;
    if (!*qa) return -1;
    if (!*qb) return +1;
    return (int)((*qa)->stamp - (*qb)->stamp);
}

/* only for backward compatibility with version without ser_id in edi file */
void foreach_source_recalc_ser_id(gpointer key, gpointer value, gpointer data){
    int i;
    struct qso *q;
    ZPtrArray *ia;

    ia = (ZPtrArray *)value;

    for (i=0; i<ia->len; i++){
        q=(struct qso *)z_ptr_array_index(ia, i);
        q->ser_id=i;
    }
} 

void foreach_source_qsort_by_stamp(gpointer key, gpointer value, gpointer data){
    ZPtrArray *ia;

    ia = (ZPtrArray *)value;
    z_ptr_array_qsort(ia, compare_stamp);
}

void foreach_source_print(gpointer key, gpointer value, gpointer data){
    ZPtrArray *ia;
    struct qso *q;
    int i;

    ia = (ZPtrArray *)value;
    trace(cfg->trace_qsos, "source: %s", key);

    for (i=0; i<ia->len; i++){
        q = (struct qso *) z_ptr_array_index(ia, i);   
        dump_qso(q, "foreach_source_print");
    }
}

void dump_all_sources(struct contest *ctest){
    int i;
    struct qso *q;

    return;
    if (!ctest){
        trace(cfg->trace_qsos, "dump_all_sources(NULL)\n");
        return;
    }
    g_hash_table_foreach(ctest->bystamp, foreach_source_print, NULL);
    dbg("ctest->allqsos:\n");
    for (i=0; i<ctest->allqsos->len; i++){
        q = (struct qso *) z_ptr_array_index(ctest->allqsos, i);   
        dump_qso(q, "foreach_source_print");
    }
    trace(cfg->trace_qsos, "---end---");
}

void add_qso_to_index(struct qso *q, int qsort_if_needed){
    ZPtrArray *ia;
    struct qso *ql;
    
    if (q && q->source && strcmp(q->source,"neterr")==0) return;
    
    ia = (ZPtrArray *) g_hash_table_lookup(ctest->bystamp, q->source);
    if (!ia) {
        ia = z_ptr_array_new();
        g_hash_table_insert(ctest->bystamp, g_strdup(q->source), ia);
    }
        
    if (q->ser_id==-1) q->ser_id=ia->len; /* ia is NOT sorted by ser_id! */
    z_ptr_array_add(ia, q);

    if (qsort_if_needed && ia->len >= 2){
        ql = (struct qso *) z_ptr_array_index(ia, ia->len-2);
        if (ql->stamp > q->stamp){ /* q is not latest */
            z_ptr_array_qsort(ia, compare_stamp);
        }
    }
    z_ptr_array_add(ctest->allqsos, q);
    if (q->exc != NULL && strlen(q->exc) > ctest->exclen) ctest->exclen = strlen(q->exc);

    qrv_set_wkd(qrv, q);
}

void remove_qso_from_index(struct qso *q){
    ZPtrArray *ia;
    int ret;
    
    ia = (ZPtrArray *) g_hash_table_lookup(ctest->bystamp, q->source);
    ret=z_ptr_array_remove(ia, q);
    ret=z_ptr_array_remove(ctest->allqsos, q);
    dbg("remove: ret=%d\n", ret);
}

void compute_qrbqtf(struct qso *q){
    double qtf;
    
    if (!q) return;
    /* CHANGE look at add_tmpqso_locator */    
    qrbqtf(ctest->pwwlo, q->locator, &q->qrb, &qtf, NULL, 2);
    q->qtf = (int) (qtf+0.5);
    if (q->qrb < 0.1) {
        q->qrb = 0;
        q->qtf = 0;
    }
    q->qtfrad=qtf*M_PI/180.0;
    q->kx = (int)(  q->qrb*sin(q->qtfrad));
    q->ky = (int)(- q->qrb*cos(q->qtfrad));
    /*dbg("compute_qrbqtf(%s) %s->%s qrb=%f qtf=%d kx=%d ky=%d\n", 
            q->callsign, ctest->pwwlo, q->locator, q->qrb, q->qtf, q->kx, q->ky); */
}



int check_autosave(void){
    
    save_all_bands_txt(1);

    return 0;
}


GPtrArray *get_band_qs(struct band *band, gchar *str){
    int i, len;
    gchar *c;
    struct qs *qs;
    struct qso *qso;
	char valid[150];

    /*ST_START;*/
    zg_ptr_array_free_items(band->qs);

    len = strlen(str);
    if (len<2) return NULL;
    
    qs = g_new0(struct qs, 1);
    qs->result1 = band->qs;
    qs->str = str;


    for (i=0;i<band->qsos->len;i++){
        qso = (struct qso *)g_ptr_array_index(band->qsos, i);
        if (qso->error || qso->dupe) continue;
        if (qs->result1->len == term->y - QSONR_HEIGHT - 4 - cfg->loglines - ctest->spypeers->len - 1) break;
        
        if (qso->callsign && z_strstr(qso->callsign, qs->str)){
			stats_valid_key_display(valid, qso->callsign, qso->locator, qso->phase, qso->mode);
            if (qso->locator)
				c = g_strdup_printf("%-10s %-6s%5d/%3d", valid, qso->locator, (int)qso->qrb, qso->qtf);
            else
				c = g_strdup(valid);
/*            dbg(",%s", c);*/
            g_ptr_array_add(qs->result1, c);
            continue;
        }
        
        if (qso->locator && z_strstr(qso->locator, qs->str)){
			stats_valid_key_display(valid, qso->callsign, qso->locator, qso->phase, qso->mode);
			if (qso->callsign)
				c = g_strdup_printf("%-10s %-6s%5d/%3d", valid, qso->locator, (int)qso->qrb, qso->qtf);
            else
                c=g_strdup_printf("%-10s %-6s%5d/%3d", "", qso->locator, (int)qso->qrb, qso->qtf);
/*            dbg(",%s", c);*/
            g_ptr_array_add(qs->result1, c);
            continue;                 
        }
        
    }
/*    dbg("<\n");*/
    
    g_free(qs);
    z_ptr_array_qsort((ZPtrArray *)band->qs, z_compare_string);
    /*ST_STOP;*/
    return band->qs;
}

GPtrArray *get_oband_qs(struct band *oband, gchar *str){
    int i, len, j;
    gchar *c;
    struct qs *qs;
    struct qso *qso;

    /*ST_START;*/
    if (ctest->bands->len<2) return oband->oqs;
    
    zg_ptr_array_free_items(oband->oqs);

    len = strlen(str);
    if (len<2) return NULL;
    
    qs = g_new0(struct qs, 1);
    qs->result1 = oband->oqs;
    qs->str = str;

    for (j=0;j<ctest->bands->len;j++){
        struct band *band;
        
        band = (struct band *)g_ptr_array_index(ctest->bands, j);
        if (band==oband) continue;
        
        for (i=0;i<band->qsos->len;i++){
            qso = (struct qso *)g_ptr_array_index(band->qsos, i);
            if (qso->error || qso->dupe) continue;
            if (qs->result1->len == term->y - QSONR_HEIGHT - 4 - cfg->loglines - ctest->spypeers->len - 1) break;
            /* FIXME contest without locator */
            if ((qso->callsign && z_strstr(qso->callsign, qs->str))||
                (qso->locator  && z_strstr(qso->locator, qs->str))){    
                
                int j;

                for (j=0;j<qs->result1->len; j++){
                    char d[20], *pd, *line;
                    
                    safe_strncpy(d, line=g_ptr_array_index(qs->result1,j), 10);
                    pd = strchr(d, ' ');
                    if (pd) *pd='\0';
                    
                    if (strcmp(d, qso->callsign)==0){ 
/*                        dbg("add %s %c\n", line, band->bandchar);*/
                        c = g_strdup_printf("%s%c", line, z_char_uc(band->bandchar));
                        g_ptr_array_index(qs->result1, j) = c;
                        g_free(line);
                        goto cont;
                    }
                }
/*                dbg("ins %s %s %c\n", qso->callsign, qso->locator, band->bandchar);*/
                c=g_strdup_printf("%-10s %-6s %c", qso->callsign, qso->locator, z_char_uc(band->bandchar));
                set_mem_comment(c, c, strlen(c));
                g_ptr_array_add(qs->result1, c);
                goto cont;
            }
cont:;                
        }
    }
/*    dbg("<\n");*/
    
    g_free(qs);
    z_ptr_array_qsort((ZPtrArray *)oband->oqs, z_compare_string);
    /*ST_STOP;*/
    return oband->oqs;
}


gchar *find_wwl_by_oband(struct band *oband, gchar *call){
    int i, j;
    struct qso *qso;
    static char raw[256];

    if (ctest->bands->len<2) return NULL;
    if (!ctest->wwlused) return NULL;
    
    for (j=0;j<ctest->bands->len;j++){
        struct band *band;
        
        band = (struct band *)g_ptr_array_index(ctest->bands, j);
        if (band==oband) continue;
        
        for (i=0;i<band->qsos->len;i++){
            qso = (struct qso *)g_ptr_array_index(band->qsos, i);
            if (qso->error || qso->dupe) continue;
            z_get_raw_call(raw, qso->callsign);
            if (strcasecmp(raw,call)) continue;
            return qso->locator; /* can be NULL */
        }
    }
    return NULL;
}


gchar *find_exc_by_oband(struct band *oband, gchar *call){
    int i, j;
    struct qso *qso;
    static char raw[256];

    if (ctest->bands->len<2) return NULL;
    if (ctest->excused != 1) return NULL;
    
    for (j=0;j<ctest->bands->len;j++){
        struct band *band;
        
        band = (struct band *)g_ptr_array_index(ctest->bands, j);
        if (band==oband) continue;
        
        for (i=0;i<band->qsos->len;i++){
            qso = (struct qso *)g_ptr_array_index(band->qsos, i);
            if (qso->error || qso->dupe) continue;
            z_get_raw_call(raw, qso->callsign);
            if (strcasecmp(raw,call)) continue;
            return qso->exc; /* can be NULL */
        }
    }
    return NULL;
}



int get_psect_int(char *psect){
    if (strcasecmp(psect, "single")==0) return 1;
    else if (strcasecmp(psect, "check")==0) return 2;
    else return 0;
}

void dump_qso(struct qso *q, char *desc){
    if (!q){
        trace(cfg->trace_qsos, "  NULL");
        return;
    }
   /* trace(cfg->trace_qsos, "  %8s %c%03d %s:%d.%05d  %s", q->callsign, z_char_uc(q->band->bandchar), atoi(q->qsonrs), q->source, q->ser_id, q->stamp%100000, desc);*/
    trace(cfg->trace_qsos, "  %8s %c%03d %s:%d.%05d  (qsop=%d error=%d dupe=%d) %s", q->callsign, q->band->bandchar, atoi(q->qsonrs), q->source, q->ser_id, (int)(q->stamp%100000), q->qsop, q->error, q->dupe,  desc);
}

void invalidate_tmpqso(struct band *b, struct qso *q){
    char raw[20];
    char s[20];
    int i;

    z_get_raw_call(raw, q->callsign);
    for (i=0;i<TMP_QSOS;i++){
		if (!b->tmpqsos[i].callsign) continue;
        z_get_raw_call(s, b->tmpqsos[i].callsign);
        if (strcasecmp(s, raw)==0){
            b->tmpqsos[i].ucallsign=0;   
            redraw_later();
        }
    }
}

void free_bystamp_value(gpointer value){
    ZPtrArray *ia;

    ia = (ZPtrArray *)value;
    z_ptr_array_free(ia, TRUE);

}
    
void free_ctest(){
    int i;

    if (!ctest) return;


    zselect_timer_kill(zsel, ctest->minute_timer_id);
    if (gses->skedw_timer_id > 0) {
        zselect_timer_kill(zsel, gses->skedw_timer_id); 
        gses->skedw_timer_id = 0;
    }

    dump_all_sources(ctest);
    
    if (gses->last_cq_timer_id) {
        zselect_timer_kill(zsel, gses->last_cq_timer_id);
        gses->last_cq_timer_id = 0;
    }
    
    clear_qrv(qrv);
	clear_hicalls();
    chart_clear_all();

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

    if (ctest->lockfile)  fclose(ctest->lockfile);
    if (ctest->logfile)   fclose(ctest->logfile);
    CONDGFREE(ctest->directory);
    CONDGFREE(ctest->cdate);
    CONDGFREE(ctest->tname);
    CONDGFREE(ctest->tdate);
    CONDGFREE(ctest->pcall);
    CONDGFREE(ctest->padr1);
    CONDGFREE(ctest->padr2);
    CONDGFREE(ctest->pclub);
    CONDGFREE(ctest->pwwlo);
    CONDGFREE(ctest->pexch);
    CONDGFREE(ctest->default_rs);
    CONDGFREE(ctest->default_rst);
    CONDGFREE(ctest->excname);
    
    CONDGFREE(ctest->rname);
    CONDGFREE(ctest->rcall);
    CONDGFREE(ctest->radr1);
    CONDGFREE(ctest->radr2);
    CONDGFREE(ctest->rpoco);
    CONDGFREE(ctest->rcity);
    CONDGFREE(ctest->rcoun);
    CONDGFREE(ctest->rphon);
    CONDGFREE(ctest->rhbbs);
    
    CONDGFREE(ctest->directory);
/*    CONDGFREE(ctest->pband);
    CONDGFREE(ctest->);
    CONDGFREE(ctest->);
    CONDGFREE(ctest->);
  */

    g_hash_table_destroy(ctest->allb_prefs);
    g_hash_table_destroy(ctest->bystamp);
    z_ptr_array_free(ctest->allqsos, TRUE);
    g_ptr_array_free(ctest->bands, TRUE);
    free_spypeers(ctest->spypeers);
    g_free(ctest);
    ctest=NULL;
    aband=NULL;
    
    set_ctest_title();
    cq_abort(1);
	update_hw();
    if (!zselect_terminating(zsel))	maps_reload();
	maps_update_showwwls();
    chart_reload();
#ifdef Z_HAVE_SDL
	rain_reload();
#endif
}



/* called ONLY when ctest is freeed. bands remain in memory */
void free_band(struct band *b){
    int i;
    struct event ev = {EV_ABORT, 0,0,0};

    stats_thread_kill(b);
    clear_tmpqsos(b, 0);

    for (i=0;i<b->qsos->len;i++){
        struct qso *qso;
        qso=(struct qso *)g_ptr_array_index(b->qsos, i);
        free_qso(qso);
    }
	g_ptr_array_free(b->qsos, TRUE);
	g_hash_table_destroy(b->rawqsoshash);
    
    CONDGFREE(b->pband);
    CONDGFREE(b->opsect);
    CONDGFREE(b->bandname);
    CONDGFREE(b->operator_);
    CONDGFREE(b->stxeq);
    CONDGFREE(b->spowe);
    CONDGFREE(b->srxeq);
    CONDGFREE(b->sante);
    CONDGFREE(b->santh);
    CONDGFREE(b->mope1);
    CONDGFREE(b->mope2);
    CONDGFREE(b->remarks);
    CONDGFREE(b->skedqrg);
    /*CONDGFREE(b->);
    CONDGFREE(b->);
    CONDGFREE(b->);
    CONDGFREE(b->);*/
    
    
    if (b->skeds)     {
        for (i=0;i<b->skeds->len;i++){
            struct sked *sked;
            sked=(struct sked*)g_ptr_array_index(b->skeds, i);
            free_sked(sked);
        }
        g_ptr_array_free(b->skeds, TRUE);
    }
    
    inputln_func(b->il,&ev);
    g_free(b->il);
    if (b->unres) g_free(b->unres);
    if (b->stats) free_stats(b->stats);
    if (b->tmpstats) free_stats(b->tmpstats);
    MUTEX_FREE(b->stats);
    if (b->swap) fclose(b->swap);
    if (b->qs)  zg_ptr_array_free_all(b->qs);
    if (b->oqs) zg_ptr_array_free_all(b->oqs);
    if (b->swapfifo)  free_fifo(b->swapfifo);
    if (b->unfi)      free_fifo(b->unfi);
    if (b->statsfifo1) free_fifo(b->statsfifo1);
    if (b->ctrlsp)    g_free(b->ctrlsp);
    CONDGFREE(b->tmplocqso.locator);
	zg_free0(b->edifile);
	zg_free0(b->mapfile);
	zg_free0(b->chartfile);
    g_free(b);
}


void free_qso(struct qso *qso){
    CONDGFREE(qso->source);
    CONDGFREE(qso->operator_);
    CONDGFREE(qso->date_str);
    CONDGFREE(qso->time_str);
    CONDGFREE(qso->callsign);
    CONDGFREE(qso->rsts);
    CONDGFREE(qso->rstr);
    CONDGFREE(qso->qsonrs);
    CONDGFREE(qso->qsonrr);
    CONDGFREE(qso->exc);
    CONDGFREE(qso->locator);
    CONDGFREE(qso->remark);

    g_free(qso);
}


void dirty_band(struct band *band) {
    trace(cfg->trace_qsos, "DIRTY_BAND %c", band->bandchar);
    band->dirty_save = 1;
    band->dirty_stats = 1;
    band->dirty_statsf = 1;
};

enum modes get_mode(void){
    
   /* if (aband) return aband->mode;
    else*/ return gses->mode;
}

void set_mode(enum modes mode){
	int i;
    gses->mode=mode;

    dbg("set_mode(%d)\n", mode);
#ifdef HAVE_HAMLIB
	for (i = 0; i < gtrigs->trigs->len; i++){
		struct trig *trig = (struct trig *)g_ptr_array_index(gtrigs->trigs, i);
		if (!trig->mode_t2r) return;
		switch (mode){
			case MOD_SSB_SSB:
				trig_set_mode_qrg(trig, RIG_MODE_USB, gtrigs->qrg - trig->ssbcw_shift);
				break;
			case MOD_CW_CW:
				trig_set_mode_qrg(trig, RIG_MODE_CW, gtrigs->qrg + trig->ssbcw_shift);
				break;
			case MOD_AM_AM:
				trig_set_mode_qrg(trig, RIG_MODE_AM, gtrigs->qrg);
				break;
			case MOD_FM_FM:
				trig_set_mode_qrg(trig, RIG_MODE_FM, gtrigs->qrg);
				break;
			case MOD_RTTY_RTTY:
				trig_set_mode_qrg(trig, RIG_MODE_RTTY, gtrigs->qrg);
				break;
			case MOD_SSTV_SSTV:
				trig_set_mode_qrg(trig, RIG_MODE_USB, gtrigs->qrg); // SSTV uses SSB
				break;
			case MOD_ATV_ATV:
				trig_set_mode_qrg(trig, RIG_MODE_FM, gtrigs->qrg); // SSTV uses FMB
				break;
			default:
				/* nic jineho se nanastavuje */
				break;
		}
	}
#endif    
}

struct zstring *contest_format(struct contest *ctest){
    gchar *ints;
    struct zstring *zs;

	ints = g_strdup_printf("%d;%d;%d;%d;"
		"%d;%d;%d;%d;"
		"%d;%d;%d;%d;"
		"%d;%d;%d;%d;"
		"%d;%d;%d;%d;"
		"%d;%d;%d",
        ctest->qsoused, ctest->qsomult, ctest->qsoglob, ctest->wwlused, 
        ctest->wwlcfm,  ctest->wwlbonu, ctest->wwlmult, ctest->excused,
        ctest->exctype, ctest->excbonu, ctest->excmult, ctest->exccfm, 
        ctest->dxcbonu, ctest->dxcmult, ctest->rstused, ctest->defrstr, 
        ctest->qsop_method, ctest->total_method, ctest->tttype, ctest->prefmult, 
		ctest->prefglob, ctest->expmode, ctest->phase);
            
            
    zs = zconcatesc(ints, ctest->tname, ctest->cdate, ctest->pcall, 
        ctest->pclub, ctest->pwwlo, ctest->pexch, ctest->default_rst, 
        ctest->default_rs, ctest->rname, ctest->rcall, ctest->radr1, 
        ctest->radr2, ctest->rpoco, ctest->rcity, ctest->rcoun, 
        ctest->rphon, ctest->rhbbs, ctest->excname, NULL);
    return zs;
}

void ctest_parse(struct zstring *zstr, struct contest *ctest){
    gchar *c;
    struct zstring *zsint;

    c = ztokenize(zstr, 1);
    zsint = zstrdup(c);
    ctest->qsoused      = atoi(ztokenize(zsint, 1));
    ctest->qsomult      = atoi(ztokenize(zsint, 0));
    ctest->qsoglob      = atoi(ztokenize(zsint, 0));
    ctest->wwlused      = atoi(ztokenize(zsint, 0));
    ctest->wwlcfm       = atoi(ztokenize(zsint, 0));
    ctest->wwlbonu      = atoi(ztokenize(zsint, 0));
    ctest->wwlmult      = atoi(ztokenize(zsint, 0));
    ctest->excused      = atoi(ztokenize(zsint, 0));
    ctest->exctype      = (enum exctype)atoi(ztokenize(zsint, 0));
    ctest->excbonu      = atoi(ztokenize(zsint, 0));
    ctest->excmult      = atoi(ztokenize(zsint, 0));
    ctest->exccfm       = atoi(ztokenize(zsint, 0));
    ctest->dxcbonu      = atoi(ztokenize(zsint, 0));
    ctest->dxcmult      = atoi(ztokenize(zsint, 0));
    ctest->rstused      = atoi(ztokenize(zsint, 0));
    ctest->defrstr      = atoi(ztokenize(zsint, 0));
    ctest->qsop_method  = atoi(ztokenize(zsint, 0));
    ctest->total_method = atoi(ztokenize(zsint, 0));
    ctest->tttype       = (enum tttype)atoi(ztokenize(zsint, 0));
    ctest->prefmult     = atoi(ztokenize(zsint, 0));
    ctest->prefglob     = atoi(ztokenize(zsint, 0));
	ctest->expmode      = atoi(ztokenize(zsint, 0));
	ctest->phase        = atoi(ztokenize(zsint, 0));

    STORE_STR3(ctest, tname, zstr, 0);
    STORE_STR3(ctest, cdate, zstr, 0);
    STORE_STR3(ctest, pcall, zstr, 0);
    STORE_STR3(ctest, pclub, zstr, 0);
    STORE_STR3(ctest, pwwlo, zstr, 0);
    STORE_STR3(ctest, pexch, zstr, 0);
    STORE_STR3(ctest, default_rst, zstr, 0);
    STORE_STR3(ctest, default_rs, zstr, 0);
    STORE_STR3(ctest, rname, zstr, 0);
    STORE_STR3(ctest, rcall, zstr, 0);
    STORE_STR3(ctest, radr1, zstr, 0);
    STORE_STR3(ctest, radr2, zstr, 0);
    STORE_STR3(ctest, rpoco, zstr, 0);
    STORE_STR3(ctest, rcity, zstr, 0);
    STORE_STR3(ctest, rcoun, zstr, 0);
    STORE_STR3(ctest, rphon, zstr, 0);
    STORE_STR3(ctest, rhbbs, zstr, 0);
    STORE_STR3(ctest, excname, zstr, 0);
    
    zfree(zsint);
}
struct zstring *band_format(struct band *b){
    gchar *ints;
    struct zstring *zs;
    
    ints = g_strdup_printf("%c;%d;%d;%d;%d;%d",
		z_char_lc(b->bandchar), b->psect, b->readonly, b->qrg_min, b->qrg_max, b->qsomultb);
    
    zs = zconcatesc(ints, b->pband, b->bandname, b->stxeq, 
                    b->spowe, b->srxeq, b->sante, b->santh, 
                    b->mope1, b->mope2, b->remarks, b->skedqrg, b->opsect, 
                    NULL);
    
    g_free(ints);
    return zs;
}



struct band *band_parse(struct zstring *zstr, struct band *b){
    gchar *c, *qsomult_str;
    struct zstring *zsint;

    c = ztokenize(zstr, 1);
    zsint = zstrdup(c);
    c = ztokenize(zsint, 1);
    b->bandchar    = z_char_uc(c[0]);
    b->bi          = b->bandchar - 'A';
    b->psect       = atoi(ztokenize(zsint, 0));
    b->readonly    = atoi(ztokenize(zsint, 0));
	b->qrg_min     = atoi(ztokenize(zsint, 0));
	b->qrg_max     = atoi(ztokenize(zsint, 0));
	qsomult_str = ztokenize(zsint, 0);
	if (qsomult_str) b->qsomultb = atoi(qsomult_str);

    STORE_STR3(b, pband, zstr, 0);
    STORE_STR3(b, bandname, zstr, 0);
    STORE_STR3(b, stxeq, zstr, 0);
    STORE_STR3(b, spowe, zstr, 0);
    STORE_STR3(b, srxeq, zstr, 0);
    STORE_STR3(b, sante, zstr, 0);
    STORE_STR3(b, santh, zstr, 0);
    STORE_STR3(b, mope1, zstr, 0);
    STORE_STR3(b, mope2, zstr, 0);
    STORE_STR3(b, remarks, zstr, 0);
    STORE_STR3(b, skedqrg, zstr, 0);
    STORE_STR3(b, opsect, zstr, 0);
    
    zfree(zsint);
    return b;
}

int compare_date_time_qsonrs(const void *a, const void *b){
    struct qso **qa, **qb;
    int ret;

    qa = (struct qso **)a;
    qb = (struct qso **)b;
    
    if (!*qa && !*qb) return 0;
    if (!*qa) return -1;
    if (!*qb) return +1;

    ret = strcmp((*qa)->date_str, (*qb)->date_str);
    if (ret) return ret;
    
    ret = strcmp((*qa)->time_str, (*qb)->time_str);
    if (ret) return ret;
    
    return atoi((*qa)->qsonrs) - atoi((*qb)->qsonrs);
}

/* 
    returns:
    0 - not worked on at least one read-write band
    1 - worked on all read-write band
*/    
int worked_on_all_rw(char *call){
	int ret = 1;
	//struct qrv_item *qi = qrv_get(qrvdb, call);

    if (ctest){
		zg_ptr_array_foreach(struct band *, b, ctest->bands)
		{
			if (b->readonly) continue;
			//if (qi && (qi->bands_qrv & (1 << b->bi)) == 0) continue;
			if (!get_qso_by_callsign(b, call)) ret = 0;
		}
	}
	//dbg("worked_on_all_rw(%s)=%d\n", call, ret);
    return ret;
}

int worked_on_aband(char *call){
    if (!aband) return 0;
		
    struct qso* q = get_qso_by_callsign(aband, call);
    if (q != NULL){
        if (q->phase != ctest->phase) return 0;
        return 1;   
    }
    else{
        return 0;
    }
}


void _dump_rw(gpointer key, gpointer value, gpointer data){

	fprintf((FILE *)data, "key='%s' value=%p ", (char*)key, value);
	if (value != NULL) {
		struct qso *qso = (struct qso *)value;
		fprintf((FILE *)data, "call='%s' qsonrs=%s time=%s", qso->callsign, qso->qsonrs, qso->time_str);
	}
	fprintf((FILE *)data, "\n");
}

void dump_rw(void *a, void *b)
{
	FILE *f;
	char *filename;

	if (!ctest) return;
	filename = g_strconcat(ctest->directory, "/", "_dump_rw.txt", NULL);
	z_wokna(filename);
	f = fopen(filename, "wt");
	if (f){
		zg_ptr_array_foreach(struct band *, b, ctest->bands)
		{
			if (b->readonly) continue;
			fprintf(f, "%c is RW\n", b->bandchar);
			g_hash_table_foreach(b->rawqsoshash, _dump_rw, f);
			fprintf(f, "\n\n");
		}
		fclose(f);
		log_addf(VTEXT(T_SAVED_S), filename);
	}else{
		log_addf(VTEXT(T_CANT_WRITE_S), filename);
	}
	g_free(filename);
}


int add_qsos_from_swap(struct band *b, FILE *f){
    char *s;
    gchar **items;
    struct qso *q;
	GString *gs;
   
    
	gs = g_string_sized_new(1024);
    while ((s = zfile_fgets(gs, f, 0)) != NULL){
		if (strlen(s) < 12) continue;
        if (s[0]!='Q') continue;

        
        items = g_strsplit(s + 2, ";", 0);
        q = g_new0(struct qso, 1);
		q->source = g_strdup(gnet->myid);
		q->operator_ = g_strdup(b->operator_);        

        q->date_str = g_strdup_printf("%s%s", items[14], items[0]);
        q->time_str = g_strdup(items[1]);
        q->callsign = g_strdup(items[2]);
        q->mode     = atoi(items[3]);
        q->rsts     = g_strdup(items[4]);
        q->qsonrs   = g_strdup(items[5]);
        q->rstr     = g_strdup(items[6]);
        q->qsonrr   = g_strdup(items[7]);
        q->exc      = g_strdup(items[8]);
        q->locator  = g_strdup(items[9]);
		if (items[10]){
			q->phase = atoi(items[10]);
		}
        q->ser_id=-1; /* computed by add_qso_to_index */
        g_strfreev(items);
        compute_qrbqtf(q);
		
        add_qso(b, q);
    }
	recalc_stats(b);
    check_autosave();
	g_string_free(gs, TRUE);
    return 0;
}


