/*
    Tucnak - VHF contest log
    Copyright (C) 2002-2021  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 "bfu.h"
#include "cwdb.h"
#include "dwdb.h"
#include "excdb.h"
#include "fifo.h"
#include "header.h"
#include "main.h"
#include "misc.h"
#include "qsodb.h"
#include "session.h"
#include "stats.h"
#include "tsdl.h"

char *get_wwl(char *buf, char *wwl){
    safe_strncpy(buf, wwl,5);
    return buf;
}

//static void enum_valid (gpointer key, gpointer value, gpointer user_data){
//    dbg("%s key='%p'\n", user_data, key);
//    dbg("%s key='%s'\n", user_data, key);
//    dbg("%s value='%p'\n", user_data, value);
//}

void draw_one_bigdigit(int x, int y, int num){
    switch(num%10){
        case 0:
            fill_area(x,y,3,5,COL_INV);
            fill_area(x+1,y+1,1,3,COL_NORM);
            break;
        case 1:
            fill_area(x,y,3,5,COL_INV);
            fill_area(x+2,y,1,4,COL_NORM);
            fill_area(x,y+1,1,3,COL_NORM);
            break;
        case 2:
            fill_area(x,y,3,5,COL_INV);
            fill_area(x,y+1,2,1,COL_NORM);
            fill_area(x+1,y+3,2,1,COL_NORM);
            break;
        case 3:
            fill_area(x,y,3,5,COL_INV);
            fill_area(x,y+1,2,1,COL_NORM);
            fill_area(x,y+3,2,1,COL_NORM);
            break;
        case 4:
            fill_area(x,y,3,5,COL_INV);
            fill_area(x+1,y,1,2,COL_NORM);
            fill_area(x,y+3,2,2,COL_NORM);
            break;
        case 5:
            fill_area(x,y,3,5,COL_INV);
            fill_area(x+1,y+1,2,1,COL_NORM);
            fill_area(x,y+3,2,1,COL_NORM);
            break;
        case 6:
            fill_area(x,y,3,5,COL_INV);
            fill_area(x+1,y+1,2,1,COL_NORM);
            fill_area(x+1,y+3,1,1,COL_NORM);
            break;
        case 7:
            fill_area(x,y,3,5,COL_INV);
            fill_area(x,y+1,2,4,COL_NORM);
            break;
        case 8:
            fill_area(x,y,3,5,COL_INV);
            fill_area(x+1,y+1,1,1,COL_NORM);
            fill_area(x+1,y+3,1,1,COL_NORM);
            break;
        case 9:
            fill_area(x,y,3,5,COL_INV);
            fill_area(x+1,y+1,1,1,COL_NORM);
            fill_area(x,y+3,2,1,COL_NORM);
            break;

    }
}

void draw_bigdigit(int x, int y, int num){
   int qsonr=num;
   
   if (qsonr>=10000) set_char(x-2,y+2,'+');
   
   if (qsonr>=1000) {
       draw_one_bigdigit(x,y,(qsonr/1000)%10);
   }else{
       x-=2;
   }

   draw_one_bigdigit(x+4 ,y, (qsonr/100)%10); 
   draw_one_bigdigit(x+8 ,y, (qsonr/10)%10); 
   draw_one_bigdigit(x+12,y, qsonr%10); 
    
}

struct stats *init_stats(char bandchar){
    struct stats *st;

    st = (struct stats *)g_new0(struct stats, 1); 
	st->bandchar = bandchar;
	//dbg("new stats %p for %c\n", st, st->bandchar);

    st->wwls  = g_hash_table_new(g_str_hash, g_str_equal);
    st->dxcs  = g_hash_table_new(g_str_hash, g_str_equal);
    st->prefs = g_hash_table_new(g_str_hash, g_str_equal);
    st->excs  = g_hash_table_new(g_str_hash, g_str_equal);
	st->valid = g_hash_table_new(g_str_hash, g_str_equal);
    st->hours = g_hash_table_new(g_str_hash, g_str_equal);
    
    clear_stats(st);
    
    return st;
}


void free_stats(struct stats *st){

    clear_stats(st);

    g_hash_table_destroy(st->wwls);
    g_hash_table_destroy(st->dxcs);
    g_hash_table_destroy(st->prefs);
    g_hash_table_destroy(st->excs);
	g_hash_table_destroy(st->valid);
    g_hash_table_destroy(st->hours);
    zg_free0(st->mycontinent);
    zg_free0(st->mydxcc);
    g_free(st);
    
}


void clear_stats(struct stats *st){
    struct dw_item *dxi;
    char s[120];

 /*   dbg("clear_stats(%p)\n", st);*/
    st->first_date = G_MAXINT;
    st->last_date  = 0;
    
    st->nqsos = 0;
    st->nqsop = 0;
    st->nwwlp = 0;
    st->nexcp = 0;
    st->ndxcp = 0;
    st->nprefs = 0;
    st->ntotal = 0;
    
    g_hash_table_foreach_remove(st->wwls,  free_gpointer_item, NULL);
    g_hash_table_foreach_remove(st->dxcs,  free_gpointer_item, NULL);
    g_hash_table_foreach_remove(st->prefs, free_gpointer_item, NULL);
    g_hash_table_foreach_remove(st->excs,  free_gpointer_item, NULL);
    //dbg("clear_stats(b) size=%d\n", g_hash_table_size(st->valid));
    //g_hash_table_foreach(st->valid, enum_valid, "UPDATEB");
	g_hash_table_foreach_remove(st->valid, zg_hash_free_key, NULL);
    //dbg("clear_stats(e) size=%d\n", g_hash_table_size(st->valid));
    //g_hash_table_foreach(st->valid, enum_valid, "UPDATEE");
    g_hash_table_foreach_remove(st->hours, free_gpointer_item, NULL);
    
    if (st->odxcall)     {g_free(st->odxcall);     st->odxcall      = NULL;}
    if (st->odxwwl)      {g_free(st->odxwwl);      st->odxwwl       = NULL;}
    if (st->odxoperator) {g_free(st->odxoperator); st->odxoperator  = NULL;}
    st->odxqrb_int = 0;

	dxi = get_dw_item_by_call(dw, ctest->pcall);
    if (!dxi) zinternal("unknown dxcc of my callsign");

    zg_free0(st->mycontinent);
    st->mycontinent = g_strdup(dxi->continent);
    zg_free0(st->mydxcc);
    st->mydxcc = g_strdup(get_dxcc(dw, s, ctest->pcall));
	//dbg("*** clear_stats(%c, %s) st=%p    st->mydxcc = %s\n", st->bandchar, from, st, st->mydxcc);
	st->oldphase = 1;
}

int find_str_in_arr(const char *str, const char *values[]) {
    if (values == NULL)
        return -1;

    for (int i = 0; values[i] != NULL; i++)
        if (strcasecmp(str, values[i]) == 0)
            return i;

    return -1;
}

int find_preferred_dxc(int type, char *dxc){
	char *prefs9[]  = {"ES","LA","LY","OH","OH0","OJ0","OY","OZ","SM","TF","YL",NULL};
	char *prefs10[] = {"G","GD","GI","GJ","GM","GU","GW",NULL};
    char *prefs11[] = {"F",NULL}; 
    char *prefs16[] = {"F","TK", "TM",NULL}; 
	char **prefs;
	int multi=1;
	
	switch(type){
		case 9: 
			prefs=prefs9;
			multi=3;
			break;
		case 10:
		case 17:
		case 18:
			prefs=prefs10;
			multi=5;
			break;
		case 11: 
			prefs=prefs11;
			multi=3;
			break;
        case 16:
            prefs=prefs16;
            multi=4;
            break;
		default:
			return 1;	
	}

	if (find_str_in_arr(dxc, (const char**)prefs) != -1)
		return multi;
	return 1;
}

int iac_bonus(struct band *band, struct qso *qso) {
    const char *bonus_250[] = {"JN35", "JN44", "JN45", "JN53",
                          "JN54", "JN55", "JN65", NULL};
    const char *bonus_500[] = {"JN33", "JN34", "JN40", "JN41", "JN43",
                          "JN46", "JN52", "JN56", "JN61", "JN62",
                          "JN63", "JN64", "JN66", "JN72", NULL};
    const char *bonus_1000[] = {
        "JM48", "JM49", "JM56", "JM65", "JM66", "JM67", "JM68", "JM76", "JM77",
        "JM78", "JM79", "JM87", "JM88", "JM89", "JM99", "JN36", "JN51", "JN57",
        "JN60", "JN67", "JN70", "JN71", "JN80", "JN81", "JN90", NULL};
    gchar s[20];

    // no bonus after 1,3 GHz
    if (band->qrg_min > 2000000)
        return 0;

    get_dxcc(dw, s, qso->callsign);
    if (strcasecmp(s, "I") != 0)
        return 0;

    int bonus = 0;
    get_wwl(s, qso->locator);

    if(find_str_in_arr(s, bonus_250) != -1)
        bonus = 250;
    else if(find_str_in_arr(s, bonus_500) != -1)
        bonus = 500;
    else if(find_str_in_arr(s, bonus_1000) != -1)
        bonus = 1000;

    dbg("IAC: WWL: %s  Bonus: %4d Callsign: %s\n", s, bonus, qso->callsign);

    return bonus;
}

/* set and dirty */
#define SAD_INT(item,value) {if (q->item!=value) band->dirty_save=1;q->item=value;}


void update_stats(struct band *band, struct stats *st, struct qso *q){
    gint date, qsop, anew;
    gchar s[120], *c;
    gpointer orig_key;
    struct cntpts *orig_value;
    gint wwls, dxcs, prefs, excs;
    struct dw_item *dwi;
    int oldmult;

    qsop=0;
    if (band->stats_thread && band->stats_thread!=g_thread_self()){
        /* Thread is computing statistics.
         * No reason to update stats, they will be overwritten and statistics
         * must not be consistent. We must kill thread and create new to 
         * cover all qsos. 
         */
        stats_thread_kill(band);
        stats_thread_create(band);
        return;
    }
    if (band->stats==st) MUTEX_LOCK(band->stats);
    
    //dbg("update_stats(%p, %c, %p)\n", st, band->bandchar, q);
    if (!q->error){
        date = atoi(q->date_str);
        if (date < st->first_date) st->first_date = date;
        if (date > st->last_date)  st->last_date  = date;
    }
    
    if (q->error) {
        SAD_INT(new_,0);
        SAD_INT(qsop,0);
        SAD_INT(susploc,0);
        SAD_INT(unkcall,0);
        goto x;
    }
    st->nqsos++;

    
/*    dbg("update_stats(%s) calls->size=%d \n", q->qsonrs, g_hash_table_size(st->calls));
    dbg("   dupe=%d  qsop=%d \n", q->dupe, q->qsop);*/
    
	char *valid_key = stats_valid_key(s, q->callsign, q->locator, q->phase, q->mode);
    
    //dbg("update_stats(%s) size=%d\n", valid_key, g_hash_table_size(st->valid));
    //g_hash_table_foreach(st->valid, enum_valid, "UPDATE");

    if (ctest->expmode == 0){
        if (g_hash_table_lookup_extended(st->valid, valid_key, &orig_key, (gpointer*)&orig_value)){
            SAD_INT(dupe,1);
        }else{
            char *v = g_strdup(valid_key);
            //dbg("v=%p %s\n", v, v);
            g_hash_table_insert(st->valid, v, q);
            SAD_INT(dupe,0);
        }
    }else{
        SAD_INT(dupe,0);
    }

    switch (ctest->qsop_method){
        case 1: /* rounded distance */
			qsop = iaru_round(q->qrb) * ctest->qsomult * band->qsomultb;
            break;
        case 2: /* 1 */
            qsop = ctest->qsomult * band->qsomultb;
            break;
		case 3: /* FIXME! it's really correct? */
			qsop = (int)(((q->qrb/50)*2+1 ) * ctest->qsomult * band->qsomultb);
			break;	
        case 4: /* WWL ring */
            qsop = qsopwr(ctest->pwwlo, q->locator) * ctest->qsomult * band->qsomultb;
            break;
		case 5: /* statute miles (anglicka mile) */
			qsop = (int)(q->qrb/1.6094 * ctest->qsomult * band->qsomultb);
			break;
		case 6: /* 1 * qso-points multi + 1 point for CW QSO FIXME! bug in taclog? */
			qsop = ctest->qsomult * band->qsomultb;
			if (q->mode==MOD_CW_CW) q->qsop++;
			break;
		case 7: /* distance(min 50) */	
			qsop = (int)((q->qrb>50 ? q->qrb : 50) * ctest->qsomult * band->qsomultb);
			break;
		case 8: /* distance(min 10) */	
			qsop = (int)( (q->qrb>10 ? q->qrb : 10) * ctest->qsomult * band->qsomultb );
			break;
		case 9:
		case 10:
		case 11:
			qsop = ctest->qsomult * band->qsomultb * 
				find_preferred_dxc(ctest->qsop_method,
						get_dxcc(dw,s,q->callsign));
			break;
        case 12:
            qsop = iaru_round(q->qrb) * ctest->qsomult * band->qsomultb;
            break;
        case 13:
//            dbg("call=%s \n", q->callsign);
			dwi = get_dw_item_by_call(dw, q->callsign);
            if (!dwi){
//                dbg("Don't know dxcc of %s\n", ctest->pcall);
                qsop=0;              
                break;
            }
            if (strcasecmp(dwi->continent, st->mycontinent)==0){ // same continent
                c = get_dxcc(dw, s, q->callsign);
//                dbg("  dxcc=%s  mydxcc=%s\n", c, st->mydxcc);
                if (c && strcasecmp(c, st->mydxcc)==0){  // same DXCC
                    qsop = 0;               // same dxcc 0
                }else{
//                    dbg("  cont=%s\n", dwi->continent);
                    if (strcasecmp(dwi->continent, "NA")==0)
                        qsop = 2;           // NA has 2 points
                    else
                        qsop = 1;           // others only 1
                }
            }else{
                qsop = 3;                   // other continent
            }
            break;
        case 14:
//            dbg("call=%s \n", q->callsign);
			//dbg("st=%p   %c%s  call=%s mydxcc=%s\n", st, st->bandchar, q->qsonrs, q->callsign, st->mydxcc);
            dwi = get_dw_item_by_call(dw, q->callsign);
            if (!dwi){
//                dbg("Don't know dxcc of %s\n", ctest->pcall);
                qsop=0;              
                break;
            }
            qsop = 1;
            if (band->qrg_min<10000) qsop = 2; // bellow 10 MHz
            if (strcasecmp(dwi->continent, st->mycontinent)==0){ // same continent
                c = get_dxcc(dw, s, q->callsign);
                //dbg("st=%p   %c%s  call=%s dxcc=%s  mydxcc=%s\n", st, st->bandchar, q->qsonrs, q->callsign, c, st->mydxcc);
				if (c && strcasecmp(c, st->mydxcc) == 0){  // same DXCC
				//if (c && strcmp(c, st->mydxcc) == 0){  // same DXCC
                    qsop = 1;               // same dxcc 0
                }else{
//                    dbg("  cont=%s\n", dwi->continent);
                    if (strcasecmp(dwi->continent, "NA")==0)
                        qsop *= 2;           // NA has 2 points
                    else
                        qsop *= 1;           // others only 1
                }
            }else{
                qsop *= 3;                   // other continent
            }
            break;
        case 15:    // ARRL VHF contests http://www.arrl.org/contests/rules/2008/june-vhf.html
            dbg("%d\n", band->qrg_min);
            if (band->qrg_min > 2000000)  
                qsop = 4;   // 2,3 GHz and above
            else if (band->qrg_min > 500000)
                qsop = 3;   // 902 MHz, 1,3 GHz 
            else if (band->qrg_min > 200000)
                qsop = 2;   // 222 MHz, 432 MHz
            else 
                qsop = 1;    // 144 MHz and below
            break;
        case 16:
            qsop = iaru_round(q->qrb) * ctest->qsomult * band->qsomultb *
				find_preferred_dxc(ctest->qsop_method,
						get_dxcc(dw,s,q->callsign));
            qsop *= qsomult16(band);
            break;
		case 17:
			qsop = iaru_round(q->qrb) * ctest->qsomult * band->qsomultb;
			break;
		case 18:
			qsop = iaru_round(q->qrb) * ctest->qsomult * band->qsomultb;
			break;
        case 19: // WNA, WSA
            switch (q->mode){ 
                case MOD_CW_CW:
                    qsop = 6;
                    break;
                case MOD_SSB_SSB:
                case MOD_SSB_CW:
                case MOD_CW_SSB:
                    qsop = 4;
                    break;
                case MOD_FM_FM:
                    qsop = 2;
                    break;
            }
            break;
		case 20: /* WWL ring Contest Lombardia */
			qsop = (qsopwr(ctest->pwwlo, q->locator) - 1) * ctest->qsomult * band->qsomultb;
			break;
        case 21: // R-Contest, Ruhrgebiets Contest
            qsop = 1;
			break;
		case 22: /* Moon contest */
			qsop = iaru_round(q->qrb) * ctest->qsomult * band->qsomultb; // same as IARU
			break;
        case 23: /* Italian Activity Contest - IAC */
            qsop = iaru_round(q->qrb) * band->qsomultb;

    }
    if (q->dupe) qsop=0;
    SAD_INT(qsop,qsop);
    
/*    dbg("   dupe=%d  qsop=%d \n", q->dupe, q->qsop);*/
    
    st->nqsop += q->qsop;

    
    if ((int)q->qrb > st->odxqrb_int){
        if (st->odxcall)     { g_free(st->odxcall);     st->odxcall = NULL; }
        if (st->odxwwl)      { g_free(st->odxwwl);      st->odxwwl  = NULL; }
        if (st->odxoperator) { g_free(st->odxoperator); st->odxoperator  = NULL;}
         
        st->odx         = q;
        st->odxqrb_int  = (int)q->qrb;
        st->odxcall     = g_strdup(q->callsign);
        st->odxwwl      = g_strdup(q->locator);  
        st->odxoperator = g_strdup(q->operator_);
    }

    q->unkcall=!find_wwl_by_call(cw, q->callsign);
    q->susploc = get_susp_loc(cw, dw, q->callsign, q->locator) & 0xff;

    anew=q->new_;

    if (ctest->wwlused){    
		int accept_loc = 1;
		int double_loc = 0;
		char tmpdxcc[20];

        get_wwl(s, q->locator);
		if (ctest->qsop_method == 17){
			if (find_preferred_dxc(17, get_dxcc(dw, tmpdxcc, q->callsign)) == 1) accept_loc = 0;
		}
		if (ctest->qsop_method == 18){
			accept_loc = 1;
			if (find_preferred_dxc(18, get_dxcc(dw, tmpdxcc, q->callsign)) != 1) double_loc = 1;
		}
		if (accept_loc){
			if (g_hash_table_lookup_extended(st->wwls, s, &orig_key, (gpointer*) &orig_value)){
				orig_value->count++; 
				orig_value->points+=qsop;
				anew &= ~NEW_WWL;
			}else{
				orig_value = g_new0(struct cntpts, 1);
				orig_value->count = 1;
				orig_value->points = qsop;
				g_hash_table_insert(st->wwls, g_strdup(s), orig_value);
				if (double_loc) {
					orig_value = g_new0(struct cntpts, 1);
					orig_value->count = 0;
					orig_value->points = 0;
					g_hash_table_insert(st->wwls, g_strdup_printf("%s_M7", s), orig_value);
				}
				anew |= NEW_WWL;
			}
		}

        int bonus = 0;
        if (ctest->qsop_method == 23 &&              // IAC
            (anew & NEW_WWL) &&                      // New WWL
            (bonus = iac_bonus(band,q)) > 0) {       // has bonus
            qsop += bonus;
            SAD_INT(qsop,qsop);
            st->nqsop += bonus;
        }
    }
    

    get_dxcc(dw, s, q->callsign);
    if (g_hash_table_lookup_extended(st->dxcs, s, &orig_key, (gpointer*) &orig_value)){
        orig_value->count++; 
		orig_value->points+=qsop;
        anew &= ~NEW_DXC;
    }else{
        orig_value = g_new0(struct cntpts, 1);
		orig_value->count = 1;
		orig_value->points = qsop;
        g_hash_table_insert(st->dxcs, g_strdup(s), orig_value);
        anew |= NEW_DXC;
    }

    if (ctest->prefmult>0){
        get_pref(s, q->callsign);
        if (g_hash_table_lookup_extended(st->prefs, s, &orig_key, (gpointer*) &orig_value)){
            orig_value->count++; 
            orig_value->points+=qsop;
            anew &= ~NEW_PREF;
        }else{
            orig_value = g_new0(struct cntpts, 1);
            orig_value->count = 1;
            orig_value->points = qsop;
            g_hash_table_insert(st->prefs, g_strdup(s), orig_value);
            anew |= NEW_PREF;
        }
    }
        


    if (ctest->excused > 0 && ctest->excname){
        safe_strncpy0(s, q->exc, 20);
        if (strcasecmp(ctest->excname, "USACA")==0){
            usaca_multiple_exc(s);
        }
        if (ctest->exctype == EXC_MULTIPLIED){
            if (g_hash_table_lookup(excdb->vexc, s) == NULL) *s = '\0';
        }
        if (*s){
            if (g_hash_table_lookup_extended(st->excs, s, &orig_key, (gpointer*) &orig_value)){
                orig_value->count++; 
                orig_value->points+=qsop;
                anew &= ~NEW_EXC;
            }else{
                orig_value = g_new0(struct cntpts, 1);
                orig_value->count = 1;
                orig_value->points = qsop;
                g_hash_table_insert(st->excs, g_strdup(s), orig_value);
                anew |= NEW_EXC;
            }
            if (ctest->qsop_method == 12){ // RSGB DC
                int max = rsgbdc_maxcount(q->exc);
                if (orig_value->count <= max) {
                    st->nexcp++;
                    anew |= NEW_EXC;
                    //dbg("protekce\n");
                }
                //dbg("%-12s exc %s can be counted %d times (after this qso %d times) st->nexcp=%d\n", q->callsign, q->exc, max, orig_value->count, st->nexcp); 
            }
        }

        
    } 

    safe_strncpy0(s, q->time_str, 3);
	if (!g_hash_table_lookup_extended(st->hours, s, &orig_key, (gpointer*) &orig_value)){
		orig_value = g_new0(struct cntpts, 1);
		g_hash_table_insert(st->hours, g_strdup(s), orig_value);
    }
    orig_value->count++; 
    orig_value->points+=qsop;
    if (!orig_value->qso){
        orig_value->qso = q;
    }else{
        if (q->qrb > orig_value->qso->qrb){
            orig_value->qso = q;
        }
    }

    wwls  = g_hash_table_size(st->wwls);
    dxcs  = g_hash_table_size(st->dxcs);
    prefs = g_hash_table_size(st->prefs);

    st->nwwlp = wwls;
    if (ctest->qsop_method != 12)
        st->nexcp = g_hash_table_size(st->excs);
    st->ndxcp = dxcs;
    
    excs = st->nexcp;
    oldmult = st->nmult;
    switch (ctest->total_method){
        case 1:      
            st->nmult = (ctest->wwlmult  ? wwls  * ctest->wwlmult  : 1 ) *
                        (ctest->prefmult ? prefs * ctest->prefmult : 1 ) *
                        (ctest->dxcmult  ? dxcs  * ctest->dxcmult  : 1 ) *
                        (ctest->excmult  ? excs  * ctest->excmult  : 1 );
            ;  
            st->ntotal = ( st->nqsop +
                           wwls * ctest->wwlbonu + 
                           dxcs * ctest->dxcbonu +
                           excs * ctest->excbonu 
                         ) * st->nmult;

            break;              
        case 2:      
        case 3:
            st->nmult = wwls  * ctest->wwlmult  +
                        prefs * ctest->prefmult +
                        dxcs  * ctest->dxcmult  +
                        excs  * ctest->excmult;  
            st->ntotal = ( st->nqsop +
                           wwls * ctest->wwlbonu + 
                           dxcs * ctest->dxcbonu +
                           excs * ctest->excbonu 
                         ) * st->nmult;
            break;              
    }
    if (st->nmult != oldmult)
        anew |= NEW_MULT;
    else
        anew &= ~NEW_MULT;
    
    SAD_INT(new_,anew);

	if (q->phase != st->oldphase){
		q->new_ |= NEW_PHASE;
		st->oldphase = q->phase;
	}else{
		q->new_ &= ~NEW_PHASE;
	}


x:;    
	if (band->stats == st) MUTEX_UNLOCK(band->stats);
}

void recalc_stats(struct band *band){
	//dbg("*** recalc_stats(%c) dirty=%d\n", band->bandchar, band->dirty_stats);
    
    if (!band->dirty_stats) return;
    stats_thread_kill(band);
    stats_thread_create(band);
    
            
  /*  ST_STOP;*/
}

void recalc_all_stats(struct contest *ctest){
    int i;
    struct band *band;

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

void recalc_all_qrbqtf(struct contest *ctest){
    int i, j;
    struct band *band;
	struct qso *q;

    for (i=0; i<ctest->bands->len; i++){
        band = (struct band *)g_ptr_array_index(ctest->bands, i);
		
		for (j=0; j<band->qsos->len; j++){
			q = get_qso(band, j);
			compute_qrbqtf(q);
		}

        for (j=0; j<TMP_QSOS;j++){
            int qtf_int;
            double qrb, qtf;
            
            if (!band->tmpqsos[j].locator || !*band->tmpqsos[j].locator) break;
            
            /* CHANGE look at add_tmqso_locator */
            qrbqtf(ctest->pwwlo, band->tmpqsos[j].locator, &qrb, &qtf, NULL, 2);
            qtf_int=(int)(qtf+0.5);
            if (qrb < 0.1) {
                qtf_int=0;
            }  
            band->tmpqsos[j].qrb=qrb;
            band->tmpqsos[j].qtf=qtf_int;
        }
    }
}

#define STATS_X 2
#define STATS_Y 2

void redraw_stats(struct band *band){
    gchar *c;
    struct stats *st=band->stats;    
    int nqsos, nqsop, nwwlp, nexcp, ndxcp, ntotal;
	GString *gs;
    
    MUTEX_LOCK(band->stats);

    nqsos  = st->nqsos;
    nqsop  = st->nqsop;
    nwwlp  = st->nwwlp;
    nexcp  = st->nexcp;
    ndxcp  = st->ndxcp;
    ntotal = st->ntotal;
    if (ctest->qsoglob){
        nqsos  = ctest->allb_nqsos;
        nqsop  = ctest->allb_nqsop;
        nwwlp  = ctest->allb_nwwlp;
        nexcp  = ctest->allb_nexcp;
        ndxcp  = ctest->allb_ndxcp;
        ntotal = ctest->allb_ntotal;
    }
    if (ctest->total_method == 3){
        ntotal = ctest->allb_nqsop * (ctest->allb_ndxcp + ctest->allb_nexcp);
        ctest->allb_ntotal = ntotal; // for HF subwin
    }

    
    print_text(STATS_X,STATS_Y,-1,VTEXT(T_SES_ODX),COL_NORM);
    if (st->odxcall && st->odxwwl){
        c = g_strdup_printf(VTEXT(T_SES_ODX_DATA), st->odxcall, st->odxwwl, st->odxqrb_int, st->odxoperator);
        print_text(STATS_X+5,STATS_Y,term->x-QSONR_WIDTH-BAND_WIDTH-8, c,COL_NORM);
        g_free(c);
    }    
    print_text(STATS_X,STATS_Y+2,term->x-QSONR_WIDTH-BAND_WIDTH-3,VTEXT(T_SES_STAT_TIT),COL_NORM);
	gs = g_string_sized_new(100);
	g_string_append_printf(gs, "%4d  %6d  %4d  %4d   %4d  %7d ", nqsos, nqsop, nexcp, nwwlp, ndxcp, ntotal);
	if (nqsos != 0) g_string_append_printf(gs, "%6.1f", (double)ntotal/(double)nqsos);
    print_text(STATS_X,STATS_Y+3,term->x-QSONR_WIDTH-BAND_WIDTH-3,gs->str,COL_NORM);
    g_string_free(gs, TRUE);

/*    if (st->nqsos){
        c = g_strdup_printf(VTEXT(T_SES_AVG), (double)st->ntotal/(double)st->nqsos);
        print_text(term->x-QSONR_WIDTH-BAND_WIDTH,ORIG_Y+1,BAND_WIDTH-1, c, COL_NORM); 
        g_free(c);
    }*/
    MUTEX_UNLOCK(band->stats);
    
}


#define F b->statsfifo1

void sf_wwl_func(gpointer key, gpointer value, gpointer user_data){
    gchar       *wwl;
    struct cntpts *n;
    ZPtrArray *ia;
    
    
    wwl   = (gchar *)       key;  
	n     = (struct cntpts *) value;
    ia    = (ZPtrArray *) user_data;

	if (ctest->qsop_method == 18)
		z_ptr_array_add(ia, g_strdup_printf("%7s:%6d %3d    ", wwl, n->points, n->count));
	else
		z_ptr_array_add(ia, g_strdup_printf("%4s:%6d %3d    ", wwl, n->points, n->count));
}


void sf_miss_exc_func(gpointer key, gpointer value, gpointer user_data){
    gchar       *missexc;
    struct miss_struct *ms;
    int maxlen = 0;
    
    missexc   = (gchar *) key;  
    ms        = (struct miss_struct *) user_data;

    if (g_hash_table_lookup_extended(ms->st->excs, missexc, NULL, NULL)) return;

    maxlen = strlen(missexc);
    if (maxlen > ms->maxlen) ms->maxlen = maxlen;
    z_ptr_array_add(ms->ia, g_strdup(missexc));
}


gint compare_colon_int (gconstpointer a, gconstpointer b){
	gchar **ca, **cb, *c;
	int ia, ib;

	ca=(gchar **)a;
	cb=(gchar **)b;

	c=strchr(*ca, ':');
	if (c) ia=atoi(c+1);
	else ia=0;
	c=strchr(*cb, ':');
	if (c) ib=atoi(c+1);
	else ib=0;

	return ib-ia;
}


#define COLS 4
void add_sf_wwls(struct band *b){
    GString *gs;
    ZPtrArray *ia;
    int i,j,k,lines;
    gchar *c;
    
    gs = g_string_new("");
    fifo_addf(F, "");
	if (ctest->qsop_method == 17 || ctest->qsop_method == 18){
		fifo_addf(F, VTEXT(T_MULTIPLIED_WWLS_D), g_hash_table_size(b->stats->wwls));
	    fifo_addf(F, VTEXT(T_HYPHENS1));
	}else{
		fifo_addf(F, VTEXT(T_SF_WWLS), g_hash_table_size(b->stats->wwls));
		fifo_addf(F, VTEXT(T_SF_WWLS_UNDER));
	}
   /* for (i=0;i<COLS;i++) g_string_append(gs, " WWL  QSO-p cnt    ");
	fifo_addf(F, gs->str);*/
    
    ia = z_ptr_array_new();
    g_hash_table_foreach(b->stats->wwls, sf_wwl_func, ia);
    z_ptr_array_qsort(ia, compare_colon_int);
    g_string_truncate(gs, 0);
    j = 0;
    lines=(ia->len+COLS-1)/COLS;
    
    for (i=0; i<lines ; i++){
        for (j=0;j<COLS; j++){
            k = i+j*lines;
            if (k>=ia->len) continue;
            c = (gchar *)z_ptr_array_index(ia, k);
            g_string_append(gs, c);        
        }
        fifo_addf(F, "%s", gs->str);
        g_string_truncate(gs, 0);
    }
    g_string_free(gs,1);
    z_ptr_array_free_all(ia);
}

////////////

void sf_exc_func(gpointer key, gpointer value, gpointer user_data){
    gchar       *exc;
    struct cntpts *n;
    ZPtrArray *ia;
    
    
    exc   = (gchar *)       key;  
	n     = (struct cntpts *) value;
    ia    = (ZPtrArray *) user_data;

    z_ptr_array_add(ia, g_strdup_printf("%4s:%6d %3d    ", exc, n->points, n->count));
}

void add_sf_excs(struct band *b){
    GString *gs;
    ZPtrArray *ia;
    int i,j,k,lines;
    gchar *c;
    
    gs = g_string_new("");
    fifo_addf(F, "");
    fifo_addf(F, VTEXT(T_SF_EXCS), g_hash_table_size(b->stats->excs));
    fifo_addf(F, VTEXT(T_SF_EXCS_UNDER));
   /* for (i=0;i<COLS;i++) g_string_append(gs, " EXC  QSO-p cnt    ");
	fifo_addf(F, gs->str);*/
    
    ia = z_ptr_array_new();
    g_hash_table_foreach(b->stats->excs, sf_wwl_func, ia);
    z_ptr_array_qsort(ia, compare_colon_int);
    g_string_truncate(gs, 0);
    j = 0;
    lines=(ia->len+COLS-1)/COLS;
    
    for (i=0; i<lines ; i++){
        for (j=0;j<COLS; j++){
            k = i+j*lines;
            if (k>=ia->len) continue;
            c = (gchar *)z_ptr_array_index(ia, k);
            g_string_append(gs, c);        
        }
        fifo_addf(F, "%s", gs->str);
        g_string_truncate(gs, 0);
    }
    g_string_free(gs,1);
    z_ptr_array_free_all(ia);
}

#define MISS_EXC_COLS 10

void miss_compute_ms(struct miss_struct *ms, struct band *b){
    ms->ia = z_ptr_array_new();
    ms->st = b->stats;
    ms->maxlen = 0;
    g_hash_table_foreach(excdb->vexc, sf_miss_exc_func, ms);
    z_ptr_array_qsort(ms->ia, z_compare_string);
}

void add_sf_miss_excs(struct band *b){
    GString *gs;
//    ZPtrArray *ia;
    int i,j,k,lines;
    gchar *c;
    struct miss_struct ms;
    
    if (!excdb) return;
    gs = g_string_new("");
    fifo_addf(F, "");
    fifo_addf(F, VTEXT(T_SF_MISS_EXCS), g_hash_table_size(excdb->vexc) - g_hash_table_size(b->stats->excs));
    fifo_addf(F, VTEXT(T_SF_MISS_EXCS_UNDER));
    
    miss_compute_ms(&ms, b);

    g_string_truncate(gs, 0);
    j = 0;
    lines=(ms.ia->len+MISS_EXC_COLS-1)/MISS_EXC_COLS;
    
    //dbg("lines=%d\n", lines);
    for (i=0; i<lines ; i++){
        for (j=0;j<MISS_EXC_COLS; j++){
            k = i+j*lines;
            if (k>=ms.ia->len) continue;
            c = (gchar *)z_ptr_array_index(ms.ia, k);
            g_string_append_printf(gs, "%-*s", ms.maxlen+1, c);        
        }
        fifo_addf(F, "%s", gs->str);
        g_string_truncate(gs, 0);
    }
    g_string_free(gs,1);
    z_ptr_array_free_all(ms.ia);
}

////////////////////
void sf_dxc_func(gpointer key, gpointer value, gpointer user_data){
    gchar       *dxc;
    struct cntpts *n;
    ZPtrArray *ia;
    
    
    dxc   = (gchar *)       key;  
	n     = (struct cntpts *) value;
    ia    = (ZPtrArray *) user_data;

    z_ptr_array_add(ia, g_strdup_printf("%4s:%6d %3d    ", dxc, n->points, n->count));
}

#undef COLS
#define COLS 4
void add_sf_dxcs(struct band *b){
    GString *gs;
    ZPtrArray *ia;
    int i,j,k,lines;
    gchar *c;
    
    gs = g_string_new("");
    fifo_addf(F, "");
    fifo_addf(F, VTEXT(T_SF_DXCS), g_hash_table_size(b->stats->dxcs));
    fifo_addf(F, VTEXT(T_SF_DXCS_UNDER));
/*	for (i=0;i<COLS;i++) g_string_append(gs, " DXCC QSO-p cnt    ");
	fifo_addf(F, gs->str);*/
    
    ia = z_ptr_array_new();
    g_hash_table_foreach(b->stats->dxcs, sf_dxc_func, ia);
    z_ptr_array_qsort(ia, compare_colon_int);
    g_string_truncate(gs, 0);
    j = 0;
    lines = (ia->len+COLS-1)/COLS;
    for (i=0; i<lines ; i++){
        for (j=0;j<COLS; j++){
            k = i+j*lines;
            if (k>=ia->len) continue;
            c = (gchar *)z_ptr_array_index(ia, k);
            g_string_append(gs, c);        
        }
        fifo_addf(F, "%s", gs->str);
        g_string_truncate(gs, 0);
    }
    g_string_free(gs,1);
    z_ptr_array_free_all(ia);
}

void add_sf_prefs(struct band *b){
    GString *gs;
    ZPtrArray *ia;
    int i,j,k,lines;
    gchar *c;
    GHashTable *hash;

    if (ctest->prefglob)
        hash = ctest->allb_prefs;
    else
        hash = b->stats->prefs;

    
    gs = g_string_new("");
    fifo_addf(F, "");
    fifo_addf(F, VTEXT(T_SF_PREFS), g_hash_table_size(hash));
    fifo_addf(F, VTEXT(T_SF_PREFS_UNDER));
/*	for (i=0;i<COLS;i++) g_string_append(gs, " DXCC QSO-p cnt    ");
	fifo_addf(F, gs->str);*/
    
    ia = z_ptr_array_new();
    g_hash_table_foreach(hash, sf_dxc_func, ia);
    z_ptr_array_qsort(ia, compare_colon_int);
    g_string_truncate(gs, 0);
    j = 0;
    lines = (ia->len+COLS-1)/COLS;
    for (i=0; i<lines ; i++){
        for (j=0;j<COLS; j++){
            k = i+j*lines;
            if (k>=ia->len) continue;
            c = (gchar *)z_ptr_array_index(ia, k);
            g_string_append(gs, c);        
        }
        fifo_addf(F, "%s", gs->str);
        g_string_truncate(gs, 0);
    }
    g_string_free(gs,1);
    z_ptr_array_free_all(ia);
}

void add_sf_wkd_wwls(struct band *b){
    int w,len, lines, i, j, myw, myh;
    GString *gs;

    fifo_addf(F, "");
    fifo_addf(F, VTEXT(T_SF_WKD_WWLS), g_hash_table_size(b->stats->wwls));
    fifo_addf(F, VTEXT(T_SF_WKD_WWLS_UNDER));
    
    w=term->x - 2;
    dbg("b->wwlradius=%d   w/5=%d\n", b->wwlradius, w/5);
    len = Z_MIN(b->wwlradius * 2 + 1, w / 5);
    lines = len;
    
    myh = qthwr(ctest->pwwlo, 0);
    myw = qthwr(ctest->pwwlo, 1);

   /* fifo_addf(F, "w: %d, h:%d ", myw, myh);*/

    gs=g_string_new("");
    
    for(i=0; i<lines ; i++){
        g_string_truncate(gs,0);
        for (j=0; j<len; j++){
            char s[10];
            int aw, ah;
            
            aw = lines - i - 1 - lines/2; 
            ah = j - len/2;

            if (aw==0 && ah==0){
                g_string_append(gs, ">QTH<");
                continue;
            }
            mkwwl4(s, aw+ myw , ah + myh);

            if (g_hash_table_lookup(b->stats->wwls, s)){
                g_string_append(gs, "     ");
                continue;
            }
            g_string_append_printf(gs, " %s", s); 
            
        }
        fifo_addf(F, "%s", gs->str);
    }
    g_string_free(gs, 1);
}


void add_sf_cnt_in_wwls(struct band *b){
    int w,len, lines, i, j, myw, myh;
    GString *gs;

    fifo_addf(F, "");
    fifo_addf(F, VTEXT(T_SF_QSO_WWL4), g_hash_table_size(b->stats->wwls));
    fifo_addf(F, VTEXT(T_SF_QSO_WWL4_UNDER));
    
    w=term->x - 2;
    len = Z_MIN(b->wwlradius * 2 + 1, w / 5);
    lines = len;
    
    myh = qthwr(ctest->pwwlo, 0);
    myw = qthwr(ctest->pwwlo, 1);

    /*fifo_addf(F, "w: %d, h:%d ", myw, myh);*/

    gs=g_string_new("");
    
    for(i=0; i<lines ; i++){
        g_string_truncate(gs,0);
        for (j=0; j<len; j++){
            char s[10];
            gint *cnt;
            int aw, ah;
            
            
            aw = lines - i - 1 - lines/2; 
            ah = j - len/2;

            mkwwl4(s, aw+ myw , ah + myh);

            if ((cnt = (gint *)g_hash_table_lookup(b->stats->wwls, s))!=NULL){
                if (aw==0 && ah==0){
                    g_string_append_printf(gs, ">%3d<", *cnt);
                }else{
                    g_string_append_printf(gs, " %3d ", *cnt); 
                }
                continue;
            }
            g_string_append(gs, "     ");
        }
        fifo_addf(F, "%s", gs->str);
    }
    g_string_free(gs, 1);
}

void add_sf_top_dx(struct band *b)
{
#define TOPS_NUM	10
	int i, cnt=0;
	signed int j;
	struct qso *q, *top[TOPS_NUM];

	memset(top, 0, TOPS_NUM * sizeof(struct qso *));
	for (i = 0; i < b->qsos->len; i++) {
		if (!top[0]) { /* init first item */
			q = get_qso(b, i);
			if (!q->error && !q->dupe) {
				cnt = 0;
				top[cnt++] = q;
				/*dbg("init: %s %u\n=\n", top[0]->callsign, (int) top[0]->qrb);*/
			}
			continue;
		}

		q = get_qso(b, i);
		/*dbg("selected %u: %s %u\n", i, q->callsign, (int) q->qrb);*/
		if (q->error || q->dupe)
			continue;

		if (cnt >= TOPS_NUM)
			cnt = TOPS_NUM - 1;
		
		/* initialize or find top dx and sort top array */
		if (!top[cnt] || q->qrb > top[cnt]->qrb) {
			for (j = cnt; j >= 0; j--) {
				if (j && q->qrb > top[j - 1]->qrb) {
					top[j] = top[j - 1]; /* move down */
					/*dbg("down %u: %s %u\n", j, top[j]->callsign, (int) top[j]->qrb);*/
				} else {
					top[j] = q; /* insert */
					cnt++;
					/*dbg("insert %u: %s %u\n", j, top[j]->callsign, (int) top[j]->qrb);*/
					break;
				}
			}
		}
	}

	fifo_addf(F, "");
	fifo_addf(F, VTEXT(T_SF_TOP_DX), TOPS_NUM);
	fifo_addf(F, VTEXT(T_SF_TOP_DX_UNDER));
	for (i = 0; i < TOPS_NUM && i < b->qsos->len && top[i]; i++) {
		q = top[i];
		fifo_addf(F, "%-12s  %-6s %4u km by %-6s %s", q->callsign, q->locator, (int) q->qrb, q->operator_, mode_msg[q->mode]);
	}
}

gint compare_hour14 (gconstpointer a, gconstpointer b){
	gchar **ca, **cb;
	int ia, ib;

	ca=(gchar **)a;
	cb=(gchar **)b;

	ia=atoi(*ca);
	if (ia<14) ia+=100;
	
	ib=atoi(*cb);
	if (ib<14) ib+=100;

	return ia-ib;
}

static int maxhourpoints, maxhourcount;
void sf_hour_max_func(gpointer key, gpointer value, gpointer user_data){
    struct cntpts *n;
	n = (struct cntpts *) value;
    
    if (n->points > maxhourpoints) maxhourpoints=n->points;
    if (n->count > maxhourcount) maxhourcount=n->count;
}

#define PS 20
#define CS 20

void sf_hour_func(gpointer key, gpointer value, gpointer user_data){
    gchar       *hour;
    struct cntpts *n;
    ZPtrArray *ia;
	int h, i, l, avg;
    char ps[40], cs[40];
    
    
    hour   = (gchar *)       key;  
	n     = (struct cntpts *) value;
    ia    = (ZPtrArray *) user_data;

	h=atoi(hour)+1;
	if (h==25) h=0;
	
    if (maxhourpoints>0) l=(n->points*PS)/maxhourpoints;
    else l=0;
    for (i=0;i<PS;i++) if (i<l) ps[i]='*'; else ps[i]=' ';
    ps[i]='\0';
    
    if (maxhourcount>0) l=(n->count*PS)/maxhourcount;
    else l=0;
    for (i=0;i<CS;i++) if (i<l) cs[i]='*'; else cs[i]=' ';
    cs[i]='\0';

    if (n->count>0) avg=n->points/n->count;
    else avg=0;
    
	
    z_ptr_array_add(ia, g_strdup_printf("%2s-%02d: [%s]%6d  :  [%s]%3d  : %4d  odx %s", hour, h,ps, n->points, cs, n->count, avg, n->qso?n->qso->callsign:"")); /* n->qso should not be nul but for sure... */
}

#undef COLS
void add_sf_hours(struct band *b){
    ZPtrArray *ia;
    int i/*,j,k,lines*/;
    gchar *c;
    
    fifo_addf(F, "");
    fifo_addf(F, VTEXT(T_SF_HOURS));
    fifo_addf(F, VTEXT(T_SF_HOURS_UNDER));
    fifo_addf(F, VTEXT(T_SF_HOURS_TITLE));
    
    ia = z_ptr_array_new();
    maxhourpoints=0;
    maxhourcount=0;
    g_hash_table_foreach(b->stats->hours, sf_hour_max_func, ia);
    g_hash_table_foreach(b->stats->hours, sf_hour_func, ia);
    z_ptr_array_qsort(ia, compare_hour14);
    for (i=0; i<ia->len ; i++){
        c = (gchar *)z_ptr_array_index(ia, i);
        fifo_addf(F, "%s", c);
    }
    z_ptr_array_free_all(ia);
}


void recalc_statsfifo(struct band *b){
    
    //dbg("recalc_statsfifo(%c, %d)\n", b->bandchar, b->dirty_statsf);
    if (!b || !b->dirty_statsf) return;

    stats_thread_join(b);
    /*ST_START;*/
    free_fifo(F);
    F = init_fifo(1000);
    F->withouttime = 1;

	// html_band_header reads from row 5
    fifo_addf(F, "");
    fifo_addf(F, VTEXT(T_SF_STATS_BAND), b->bandname);
    fifo_addf(F, VTEXT(T_SF_STATS_BAND_UNDER));
    fifo_addf(F, "");
    fifo_addf(F, VTEXT(T_SF_CONTEST), ctest->tname);
    if (ctest->total_method == 3 || ctest->qsoglob){
        fifo_addf(F, VTEXT(T_SF_ALLBQSOS), ctest->allb_nqsos); 
        fifo_addf(F, VTEXT(T_SF_ALLBQSOP), ctest->allb_nqsop);
        fifo_addf(F, VTEXT(T_SF_ALLBPTS), ctest->allb_ntotal);
    }
    fifo_addf(F, VTEXT(T_SF_QSOS), b->stats->nqsos);  
	fifo_addf(F, VTEXT(T_SF_VQSO), g_hash_table_size(b->stats->valid));
    fifo_addf(F, VTEXT(T_SF_QSOP), b->stats->nqsop);
    fifo_addf(F, VTEXT(T_SF_PTS), b->stats->ntotal);
    

    if (b->stats->nqsos>0){
        fifo_addf(F, VTEXT(T_SF_AVG_PTS), ((double)b->stats->ntotal) / ((double)b->stats->nqsos));
        fifo_addf(F, VTEXT(T_SF_AVG_QSOP), ((double)b->stats->nqsop) / ((double)b->stats->nqsos));
		if (b->stats->qso_per_hour > 0) fifo_addf(F, VTEXT(T_SF_QSO_HOUR), b->stats->qso_per_hour);
		if (b->stats->pts_per_hour > 0) fifo_addf(F, VTEXT(T_SF_QSOP_HOUR), b->stats->pts_per_hour);
		fifo_addf(F, VTEXT(T_SF_QSOP_50QSO), b->stats->pts_per_50qso);
    }
    fifo_addf(F, "");

	if (ctest->qsop_method==4 || ctest->wwlmult != 0 || ctest->wwlbonu != 0){
    	add_sf_wkd_wwls(b);
    	add_sf_cnt_in_wwls(b);
	}
    if (ctest->wwlused > 0) add_sf_wwls(b);
	if (ctest->excused > 0) {
        add_sf_excs(b);   
        add_sf_miss_excs(b);
    }
    add_sf_dxcs(b);
    if (ctest->prefmult > 0) add_sf_prefs(b);
    add_sf_hours(b);
	if (ctest->wwlused > 0) add_sf_top_dx(b);
   
    fifo_addf(F, "");
    b->dirty_statsf = 0;
    /*ST_STOP; */
}

/* Exports statistics of actual band to a text file */
void export_stats_fifo(void) {
	gchar *fname;
	int i;
	
	progress(VTEXT(T_EXPORTING));

	fname = NULL;
	for (i = 0; 1; i++) {
		struct stat st;
		
		fname = g_strdup_printf("%s/stat%c%d.txt", ctest->directory, z_char_lc(aband->bandchar), i);
		if (stat(fname, &st) != 0)
			break;

		g_free(fname);
		fname = NULL;
	}
	
	recalc_statsfifo(aband);
	save_fifo_to_file(aband->statsfifo1, fname);
	
	g_free(fname);
	progress(NULL);
}

gpointer stats_thread_func(gpointer data){
    struct qso *q;
    int i;
    struct band *band=(struct band*)data;
    struct stats *tmp;

	zg_thread_set_name("Tucnak stats");

/*    ST_START;*/
    clear_stats(band->tmpstats);

    for (i=0; i<band->qsos->len; i++){
        if (band->stats_break) break;
        q = get_qso(band, i);
        update_stats(band, band->tmpstats, q);
    }
    MUTEX_LOCK(band->stats);
    tmp=band->stats;
    band->stats=band->tmpstats;
    band->tmpstats=tmp;
    MUTEX_UNLOCK(band->stats);

	minute_stats(band);
    
    band->dirty_save = 1;
    
    //sprintf(s, "ST\n");
    //ret = write(tpipe->threadpipe_write, s, strlen(s));
	zselect_msg_send(zsel, "ST");
                    
    band->dirty_stats = 0;
   /* dbg("stats done %c\n", band->bandchar);*/
    return 0;
}

void stats_thread_create(struct band *band){
    if (band->stats_thread) zinternal("stats_threadcreate band->stats_thread");
    /*dbg("stats_thread_create(%c)\n", band->bandchar);*/
    band->stats_break=0;
    band->stats_thread=g_thread_try_new("stats", stats_thread_func, (gpointer)band, NULL);
    if (!band->stats_thread) zinternal("stats_threadcreate !band->stats_thread");
}

void stats_thread_join(struct band *band){
    if (!band->stats_thread) return;
    dbg("join stats ...\n");
    g_thread_join(band->stats_thread);
    dbg("done\n");
    band->stats_thread=NULL;
    
}

void stats_thread_kill(struct band *band){
    if (!band->stats_thread) return;
/*    dbg("stats_thread_kill(%c)\n", band->bandchar);*/
    band->stats_break=1;
    dbg("join stats ...\n");
    g_thread_join(band->stats_thread);
    dbg("done\n");
    band->stats_thread=NULL;
}


void minute_stats(struct band *band){
    struct qso *qso;
    int j,brk, cnt50;
	int qso_per_hour, pts_per_hour, pts_per_50qso;
	char s[100], hourbefore[100];
    time_t hb;
    struct tm utc;
    
	hb=time(NULL)-3600;
    gmtime_r(&hb, &utc);
    sprintf(hourbefore, "%4d%02d%02d%02d%02d",1900+utc.tm_year, 1+utc.tm_mon, utc.tm_mday, utc.tm_hour, utc.tm_min);  


	
	if (band->qsos->len==0) return;
	qso_per_hour=pts_per_hour=pts_per_50qso=0;
	cnt50=50;
	brk=0;
    ctest->lastmultqso = NULL;
	for (j=band->qsos->len-1;j>=0;j--){
		if (band->stats_break) break;
		qso = (struct qso *)g_ptr_array_index(band->qsos, j);

		if (qso->error || qso->dupe) continue;
		
		strcpy(s, qso->date_str);
		strcat(s, qso->time_str);
/*		dbg("s=%s h=%s\n", s, hourbefore);*/
		if (strcmp(s, hourbefore)>0){
			qso_per_hour++;
			pts_per_hour+=qso->qsop;
		}else{
			brk|=0x01;
		}
		
		
		if (cnt50-->0){
			pts_per_50qso+=qso->qsop;
		}else{
			brk|=0x02;
		}

        if ((qso->new_ & NEW_MULT) && !ctest->lastmultqso){
            brk |= 0x04;
            ctest->lastmultqso = qso;
        }

		if (brk==0x07) break;
	}
	MUTEX_LOCK(band->stats);
	if (band->stats->qso_per_hour!=qso_per_hour ||
		band->stats->pts_per_hour!=pts_per_hour ||
		band->stats->pts_per_50qso!=pts_per_50qso){
	
		band->stats->qso_per_hour=qso_per_hour;
		band->stats->pts_per_hour=pts_per_hour;
		band->stats->pts_per_50qso=pts_per_50qso;
		band->dirty_statsf = 1;
	}
	MUTEX_UNLOCK(band->stats);
}

void minute_stats_all(void){
    struct band *band;
    time_t hb;
    struct tm utc;
	int i, brk;
	char s[100], hourbefore[100], tenbefore[100];
    int qso_per_10, qso_per_60;
    struct qso *qso;

	if (!aband) return;
	

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

		minute_stats(band);
	}
	if (aband->dirty_statsf) recalc_statsfifo(aband);

	hb=time(NULL)-3600;
    gmtime_r(&hb, &utc);
    sprintf(hourbefore, "%4d%02d%02d%02d%02d",1900+utc.tm_year, 1+utc.tm_mon, utc.tm_mday, utc.tm_hour, utc.tm_min);  

	hb=time(NULL)-600;
    gmtime_r(&hb, &utc);
    sprintf(tenbefore, "%4d%02d%02d%02d%02d",1900+utc.tm_year, 1+utc.tm_mon, utc.tm_mday, utc.tm_hour, utc.tm_min);  

	if (ctest->allqsos->len==0) return;
    qso_per_10 = qso_per_60 = 0;
	brk=0;
    for (i=ctest->allqsos->len-1;i>=0;i--){
		qso = (struct qso *)g_ptr_array_index(ctest->allqsos, i);
		if (qso->error || qso->dupe) continue;
		
        strcpy(s, qso->date_str);
		strcat(s, qso->time_str);
		
        if (strcmp(s, hourbefore)>0)
			qso_per_60++;
		else
			brk|=0x01;
		
        if (strcmp(s, tenbefore)>0)
			qso_per_10++;
		else
			brk|=0x02;
		
		if (brk==0x03) break;
    }    
    if (ctest->qso_per_60 != qso_per_60 ||
        ctest->qso_per_10 != qso_per_10){

        ctest->qso_per_60 = qso_per_60;
        ctest->qso_per_10 = qso_per_10;
        redraw_later();
    }
}

void timer_minute_stats_all(void *arg){
	minute_stats_all();
	ctest->minute_timer_id=zselect_timer_new(zsel, 60000, timer_minute_stats_all, NULL);

/*#ifdef Z_ANDROID
    struct tm utc;
	time_t now = time(NULL);
    gmtime_r(&now, &utc);
    dbg("%02d:%02d:%02dz", utc.tm_hour, utc.tm_min, utc.tm_sec);
#endif*/
}

void stats_merge_prefs_fn(gpointer key, gpointer value, gpointer data){
    gchar *pref = (gchar *)key;
    struct cntpts *cp = (struct cntpts *)value;
    gpointer orig_key;
    struct cntpts *orig_value;

    //dbg("stats_merge_prefs_fn('%s') cp=%d %d\n", key, cp->count, cp->points); 
    if (!g_hash_table_lookup_extended(ctest->allb_prefs, pref, &orig_key, (gpointer) &orig_value)){
        orig_value = g_new0(struct cntpts, 1);
        g_hash_table_insert(ctest->allb_prefs, g_strdup(pref), orig_value);
    } 
    orig_value->count += cp->count; 
    orig_value->points += cp->points;
}

// called from main thread, no locking needed
void recalc_allb_stats(){
    int i;
    struct band *band;

    //dbg("recalc_allb_stats\n");
    if (ctest->total_method != 3 && ctest->qsoglob == 0) return;
//    dbg("do!\n");

    ctest->allb_nqsos  = 0;
    ctest->allb_nqsop  = 0;
    ctest->allb_nwwlp  = 0;
    ctest->allb_nexcp  = 0;
    ctest->allb_nprefp = 0;
    ctest->allb_ndxcp  = 0;
    ctest->allb_nmult = 0;
    ctest->allb_ntotal = 0;

    if (ctest->prefmult > 0){
        g_hash_table_foreach_remove(ctest->allb_prefs, free_gpointer_item, NULL);
    }

    for (i=0; i<ctest->bands->len; i++){
        band = (struct band *)g_ptr_array_index(ctest->bands, i);
        
        ctest->allb_nqsos  += band->stats->nqsos;
        ctest->allb_nqsop  += band->stats->nqsop;
        ctest->allb_nwwlp  += band->stats->nwwlp;
        ctest->allb_nexcp  += band->stats->nexcp;
        ctest->allb_ndxcp  += band->stats->ndxcp;
        ctest->allb_nmult  += band->stats->nmult;
        ctest->allb_ntotal += band->stats->ntotal;

        if (ctest->prefmult > 0){
            g_hash_table_foreach(band->stats->prefs, stats_merge_prefs_fn, NULL);
            ctest->allb_nprefp = g_hash_table_size(band->stats->prefs);
        }
    }
    if (ctest->total_method == 4){
        ctest->allb_nmult = ( ctest->wwlmult  ? ctest->allb_nwwlp * ctest->wwlmult : 1 ) *
                            ( ctest->dxcmult  ? ctest->allb_ndxcp * ctest->dxcmult : 1 ) *
                            ( ctest->excmult  ? ctest->allb_nexcp * ctest->excmult : 1 );

        ctest->allb_ntotal = ( ctest->allb_nqsop + 
                               ctest->allb_nwwlp * ctest->wwlbonu + 
                               ctest->allb_ndxcp * ctest->dxcbonu + 
                               ctest->allb_nexcp * ctest->excbonu 
                             ) * ctest->allb_nmult;
    }

}

int qsomult16(struct band *band){
    int qsomult = 1;

    if (band->qrg_min > 2000000)
        qsomult = 10;     // 2,3 GHz and above
    else if (band->qrg_min > 1000000)
        qsomult = 5;      // 1,3 GHz
    else if (band->qrg_min > 400000)
        qsomult = 3;      // 432 MHz
    else
        qsomult = 1;      // 144 MHz

    return qsomult;
}

	
char *stats_valid_key(char *buf, const char *call, const char *wwl, const int phase, const enum modes mode){
	z_get_raw_call(buf, call);
	if (phase > 1){
		sprintf(buf + strlen(buf), "_%d", phase);
	}
	if (z_call_is_rover(call) && wwl){
		strcat(buf, "_");
		safe_strncpy0(buf + strlen(buf), wwl, 4 + 1);
	}
	if (ctest->qsop_method == 22){ // Moon contest
		switch (mode){
		case MOD_NONE:
		case MOD_SSB_SSB:
		case MOD_SSB_CW:
		case MOD_CW_SSB:
		case MOD_AM_AM:
		case MOD_FM_FM:
			strcpy(buf + strlen(buf), "_PH");
			break;
		case MOD_CW_CW:
			strcpy(buf + strlen(buf), "_CW");
			break;
		case MOD_RTTY_RTTY:
		case MOD_SSTV_SSTV:
		case MOD_ATV_ATV:
			strcpy(buf + strlen(buf), "_DI");
			break;
		}
	}
    //printf("stats_valid_key='%s'\n", buf);
	return buf;
}

// versus stats_valid_key - not raw call
char *stats_valid_key_display(char *buf, const char *call, const char *wwl, const int phase, const enum modes mode){
	strcpy(buf, call);
	if (phase > 1){
		sprintf(buf + strlen(buf), "_%d", phase);
	}
	if (z_call_is_rover(call) && wwl){
		strcat(buf, "_");
		safe_strncpy0(buf + strlen(buf), wwl, 4 + 1);
	}
	if (ctest->qsop_method == 22){ // Moon contest
		switch (mode){
			case MOD_NONE:
			case MOD_SSB_SSB:
			case MOD_SSB_CW:
			case MOD_CW_SSB:
			case MOD_AM_AM:
			case MOD_FM_FM:
				strcpy(buf + strlen(buf), "_PH");
				break;
			case MOD_CW_CW:
				strcpy(buf + strlen(buf), "_CW");
				break;
			case MOD_RTTY_RTTY:
			case MOD_SSTV_SSTV:
			case MOD_ATV_ATV:
				strcpy(buf + strlen(buf), "_DI");
				break;
		}
	}
    //printf("stats_valid_key_display='%s'\n", buf);
	return buf;
}

static void dump(gpointer key, gpointer value, gpointer userdata)
{
	g_string_append_printf((GString *)userdata, "%s ", (char*)key);
}

struct qso *stats_get_dupe(struct stats *st, const char *call, const char *wwl, const int phase, const enum modes mode){
	char s[125];
	char *valid_key = stats_valid_key(s, call, wwl, phase, mode);
	
	//if (z_call_is_rover(call)) return NULL;
	if (ctest->expmode) return NULL;
	struct qso *ret = g_hash_table_lookup(st->valid, valid_key);
	
	GString *gs = g_string_sized_new(1000);
	g_hash_table_foreach(st->valid, dump, gs);
	dbg("stats_get_dupe = %s (%s)\n", valid_key, gs->str);
	if (ret != NULL)
		dbg("  ret = %s %-3s\n", ret->callsign, ret->qsonrs);
	else
		dbg("  ret = NULL\n");
	g_string_free(gs, TRUE);

	return ret;
}

