/*
    Tucnak - VHF contest log
    Copyright (C) 2002-2022  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 "cwdb.h"
#include "dwdb.h"
#include "fifo.h"
#include "inputln.h"
#include "main.h"
#include "namedb.h"
#include "qrvdb.h"
#include "qsodb.h"
#include "session.h"
#include "stats.h"
#include "tregex.h"
#include "tsdl.h"


struct dw *dw;
static gchar *dw_next;  /* wwl1 */
static gchar *wd_next;  /* dxc1 */

struct dw *init_dw(void){
    struct dw *dw;

	progress(VTEXT(T_INIT_DWDB));		

    dw = g_new0(struct dw,1 );

    dw->pd = g_hash_table_new(g_str_hash, g_str_equal);
    dw->dw = g_hash_table_new(g_str_hash, g_str_equal);
    dw->wd = g_hash_table_new(g_str_hash, g_str_equal);

    dw_next = NULL;
    wd_next = NULL;

    return dw;
}




static gboolean free_dw_item(gpointer key, gpointer value, gpointer user_data){
    struct dw_item *dwi;
    
    g_free(key);
    dwi = (struct dw_item *)value;
    if (dwi->wwl0) g_free(dwi->wwl0);
    if (dwi->wwl1) g_free(dwi->wwl1);
    if (dwi->continent) g_free(dwi->continent);
    if (dwi->dxcname) g_free(dwi->dxcname);

    g_hash_table_destroy(dwi->wwls);
    
    g_free(value);
    return TRUE;
}

static gboolean free_wd_item(gpointer key, gpointer value, gpointer user_data){
    struct wd_item *wdi;
    
    g_free(key);
    wdi = (struct wd_item *)value;
    if (wdi->dxc0) g_free(wdi->dxc0);
    if (wdi->dxc1) g_free(wdi->dxc1);
    g_free(value);
    return TRUE;
}


void free_dw(struct dw *dw){
	progress(VTEXT(T_FREE_DWDB));		

    g_hash_table_foreach_remove(dw->pd, free_gpointer_item, NULL);
    g_hash_table_destroy(dw->pd);
    g_hash_table_foreach_remove(dw->dw, free_dw_item, NULL);
    g_hash_table_destroy(dw->dw);
    g_hash_table_foreach_remove(dw->wd, free_wd_item, NULL);
    g_hash_table_destroy(dw->wd);
    g_free(dw);
}

gint get_pd_size(struct dw *dw){
    return g_hash_table_size(dw->pd);
}

gint get_dw_size(struct dw *dw){
    return g_hash_table_size(dw->dw);
}

gint get_wd_size(struct dw *dw){
    return g_hash_table_size(dw->wd);
}


#define DW_DELIM " \t\r\n"


void add_pd(struct dw *dw, gchar *prefix, gchar *dxc){
    gpointer orig_prefix, orig_dxc;

//    dbg("add_pd(prefix='%s', dxc='%s'\n", prefix, dxc );
    
    if (g_hash_table_lookup_extended(dw->pd, (gpointer) prefix,
               &orig_prefix, &orig_dxc)){
        
        g_hash_table_remove(dw->pd, orig_prefix);
        g_free(orig_prefix);
        g_free(orig_dxc);
    } 
    g_hash_table_insert(dw->pd, g_strdup(prefix), g_strdup(dxc) );
}


struct dw_item *add_dxc(struct dw *dw, gchar *dxc, gchar *wwl0, gchar *wwl1, int waz, int itu, gchar *continent, gchar *dxcname, double latitude, double longitude){
    struct dw_item *dxci;

//    dbg("add_dxc(dxc='%s', wwl='%s' '%s')\n",dxc, wwl0, wwl1);
    

    dxci = (struct dw_item*)g_hash_table_lookup(dw->dw, dxc);
    if (!dxci) {
       dxci = g_new0(struct dw_item, 1); 
       
       dxci->wwls = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
       g_hash_table_insert(dw->dw, g_strdup(dxc), dxci);
    }
    
    if (dxci->wwl0 && wwl0) { g_free(dxci->wwl0); dxci->wwl0 = NULL; }
    if (dxci->wwl1 && wwl1) { g_free(dxci->wwl1); dxci->wwl1 = NULL; }

    dxci->wwl0 = g_strdup(wwl0);
    dxci->wwl1 = g_strdup(wwl1);
    if (waz) dxci->waz = waz;
    if (itu) dxci->itu = itu;
    if (continent && !dxci->continent) dxci->continent = g_strdup(continent);
    if (dxcname && !dxci->dxcname) dxci->dxcname = g_strdup(dxcname);
	if (longitude < 400) dxci->longitude = longitude;
	if (latitude < 400) dxci->latitude = latitude;
    
    return dxci;
}

void add_dw(struct dw_item *dwi, gchar *dxc, gchar *wwl){
    gpointer orig_wwl, orig_dxc;
    char wwl1[6];

    if (!wwl) return;
    
    
    
    if (g_hash_table_lookup_extended(dwi->wwls, (gpointer) wwl,
               &orig_wwl, &orig_dxc)){
        
        g_hash_table_remove(dwi->wwls, orig_wwl);
    } 
    safe_strncpy(wwl1, wwl, 5);
    /*dbg("add_dw(dxc='%s', wwl='%s')\n", dxc, wwl1);*/
    g_hash_table_insert(dwi->wwls, g_strdup(wwl1), g_strdup(dxc) );
    
}


void add_wd(struct dw *dw, gchar *wwl, gchar *dxc){
    struct wd_item *wdi;
    
    if (!wwl || !dxc) return;
   /*dbg("add_wd(wwl='%s', dxc='%s')  size=%d \n",wwl, dxc, g_hash_table_size(dw->wd));*/
    
    wdi = g_hash_table_lookup(dw->wd, wwl);
    if (!wdi){
        wdi = g_new0(struct wd_item, 1);

        g_hash_table_insert(dw->wd, g_strdup(wwl), wdi);
      /*  dbg("   adding wwl '%s' \n", wwl);*/
    }
    
    if (!wdi->dxc0) {
        wdi->dxc0 = g_strdup(dxc);
        /*dbg("      adding dxc0 '%s' \n", wdi->dxc0);*/
        return;
    }
    
    if (!wdi->dxc1) {
        wdi->dxc1 = g_strdup(dxc);       
       /* dbg("      adding dxc1 '%s' \n", wdi->dxc1);*/
        return;
    }
    
    return;
}



int load_dw_from_file(struct dw *dw, gchar *filename){
    FILE *f;
	long int len;
	gchar *file;
	int ret;
	size_t r;
    
    /*dbg("load_dw_from_file(%s, rt)\n", filename);*/
    f = fopen(filename, "rt");
    if (!f) {
/*        dbg("Can't open '%s'\n",filename);*/
        return -1;
    }
	if (fseek(f, 0L, SEEK_END)<0){
		fclose(f);
		return -1;
	}
	len=ftell(f);
	if (len<0 || len>0x10000000) {    /* g_new can allocate only gsize=unisgned int */
		fclose(f);
		return -1;
	}
	
	if (fseek(f, 0L, SEEK_SET)<0){
		fclose(f);
		return -1;
	}
	
	file=g_new(gchar, len);
	r=fread(file, 1, len, f);
    fclose(f);
	if (r!=len) return -1;
	ret=load_dw_from_mem(dw, file, len);
	g_free(file);
	return ret;
}

int load_dw_from_mem(struct dw *dw, const char *file, const long int len){
    GString *pref, *wwls, *centers; 
    gchar *dxc, *prefix, *wwl, *c0, *c1;
    struct dw_item *dxci, *maindxci;
    char *token_ptr;
	long int pos;

    pref    = g_string_sized_new(100);
    wwls    = g_string_sized_new(100);
    centers = g_string_sized_new(100);
    

	pos=0;
    while(1){
        do {
           if (!zfile_mgets(pref,  file, &pos, len, 1)) goto abort;
        }while (pref->len==0);
        
		/*if (pref->str[0] == 'E' && pref->str[1] == 'A'){
			int a = 1;
		} */

        dxc = strtok_r(pref->str, DW_DELIM, &token_ptr);
        if (!dxc) continue;
        //add_pd(dw, dxc, dxc); //must be in cty.dat
        
        while ((prefix=strtok_r(NULL, DW_DELIM, &token_ptr))!=NULL){
            add_pd(dw, prefix, dxc);
        } 
        
        if (!zfile_mgets(wwls,    file, &pos, len, 1)) break;
        if (!wwls->len) continue;

        if (!zfile_mgets(centers, file, &pos, len, 1)) break;

        if (centers->len)  c0 = strtok_r(centers->str, DW_DELIM, &token_ptr);
        else               c0 = strtok_r(wwls->str,    DW_DELIM, &token_ptr);
        
        c1 = strtok_r(NULL, DW_DELIM, &token_ptr);
            
		maindxci = get_dw_item_by_call(dw, dxc);
		if (maindxci){
			dxci = add_dxc(dw, dxc, c0, c1, maindxci->waz, maindxci->itu, maindxci->continent, maindxci->dxcname, maindxci->latitude, maindxci->longitude);
		}else{
			dxci = add_dxc(dw, dxc, c0, c1, 0, 0, NULL, NULL, 404, 404);
		}
        
        add_dw(dxci, dxc, c0);
        add_dw(dxci, dxc, c1);
        add_wd(dw, c0, dxc);
        add_wd(dw, c1, dxc);

        if (centers->len){  
            c0 = strtok_r(wwls->str, DW_DELIM, &token_ptr);
            add_dw(dxci, dxc, c0);
            add_wd(dw, c0, dxc);
        }
        
        while((wwl = strtok_r(NULL, DW_DELIM, &token_ptr))!=NULL){
            add_dw(dxci, dxc, wwl);
            add_wd(dw, wwl, dxc);
        }  
        
    }
abort:;
    g_string_free(pref, 1);
    g_string_free(wwls, 1);
    g_string_free(centers, 1);
    return 0;
}

#define CTY_DELIM ":"
#define PREF_DELIM ","

int load_cty_from_file(struct dw *dw, gchar *filename){
    FILE *f;
	long int len;
	gchar *file;
	int ret;
	size_t r;
    
    f = fopen(filename, "rt");
    if (!f) {
/*        dbg("Can't open '%s'\n",filename);*/
        return -1;
    }
	if (fseek(f, 0L, SEEK_END)<0){
		fclose(f);
		return -1;
	}
	len=ftell(f);
	if (len<0 || len>0x10000000) {    /* g_new can allocate only gsize=unisgned int */
		fclose(f);
		return -1;
	}
	
	if (fseek(f, 0L, SEEK_SET)<0){
		fclose(f);
		return -1;
	}
	
	file=g_new(gchar, len);
	r=fread(file, 1, len, f);
    fclose(f);
	if (r!=len) return -1;
	ret=load_cty_from_mem(dw, file, len);
	g_free(file);
	return ret;
}


int load_cty_from_mem(struct dw *dw, const char *file, const long int len){
    GString *gs, *gs_next; 
    gchar **line, **prefs, *continent;
    int i, waz, itu;
    gchar *dxc, *dxcname;
    char wwl4[6];
	long int pos;
	double latitude, longitude;
    
    
    gs      = g_string_sized_new(100);
    gs_next = g_string_sized_new(100);
    
    pos=0;
    while(1){
        if (gs_next->len>0){
            g_string_assign(gs, gs_next->str);
        }else{
            if (!zfile_mgets(gs,  file, &pos, len, 0)) break;
        }
        
        while(1){
            if (!zfile_mgets(gs_next,  file, &pos, len, 0)){
                g_string_truncate(gs_next,0); /* next safe_mgets breaks loop */
                break;
            }
            
            if (gs_next->str[0]==' '){ 
                g_string_append(gs, gs_next->str);
            }else{
                break;
            }
        }

        
        line = g_strsplit(gs->str, CTY_DELIM, 9);
        for (i=0;i<9;i++) {
            if (line[i] ==NULL){
                break;
            }
        }
        if (i!=9) {
            g_strfreev(line);
            continue;
        }
        
        g_strstrip(line[0]);
        dxcname = line[0];
        g_strstrip(line[1]);
        waz = atoi(line[1]);
        g_strstrip(line[2]);
        itu = atoi(line[2]);
        g_strstrip(line[3]);
        continent = line[3];
        
        g_strstrip(line[4]);
		latitude = atof(line[4]);
        g_strstrip(line[5]);
		longitude = - atof(line[5]);
        g_strstrip(line[7]);
        g_strstrip(line[8]);
        
        
        dxc=line[7];
        if (dxc[0]=='*') dxc++; // strip leading star, TNX to DL5YBZ
        /*slash = strchr(dxc, '/');
        if (slash) {
            *slash = '\0';
            slash++;;
        }         */
        z_str_uc(dxc);

        
        //dbg("dxc='%s'\n", dxc);

        if (!dxc || !*dxc) {
            g_strfreev(line);
            continue;
        }
        add_pd(dw, dxc, dxc);
        
/*        dbg("h=%s, w=%s, d=%s, p=%s\n", line[5], line[6], line[7], line[8]);*/
        
        prefs = g_strsplit(line[8], PREF_DELIM, 0);
        for (i=0; prefs[i]!=NULL; i++){
            char *c;

            g_strstrip(prefs[i]);
            if (strchr(prefs[i],'/')) continue;
            if ((c=strchr(prefs[i],'('))!=NULL) *c='\0';
            if ((c=strchr(prefs[i],'['))!=NULL) *c='\0';
            if ((c=strchr(prefs[i],';'))!=NULL) *c='\0';
            
            if (strlen(prefs[i])>2 && !isdigit(prefs[i][ strlen(prefs[i])-1])) continue;
            
            /*dbg("\t'%s'", prefs[i]);  */
            add_pd(dw, prefs[i], dxc);
            
        }
        g_strfreev(prefs);
        /*dbg("\n");*/
        compute_wwl4(wwl4, -atof(line[5]), atof(line[4]));

        /*dbg("h=%s, w=%s, d=%s, wwl4=%s \n", line[4], line[5], line[7], wwl4);*/
        add_dxc(dw, dxc, wwl4, NULL, waz, itu, continent, dxcname, latitude, longitude); 
        
        g_strfreev(line);
        
    }
    g_string_free(gs, 1);
    g_string_free(gs_next, 1);
    return 0;
}


void read_dw_files(struct dw *dw){
    gchar *s;
	int ret;
    
	progress(VTEXT(T_LOAD_DWDB));		

    s = g_strconcat(tucnak_dir, "/cty.dat", NULL);
    ret=load_cty_from_file(dw, s);
    g_free(s);
	if (ret<0){
		GString *gs = g_string_new("");
		g_string_append(gs, txt_cty);
		g_string_append(gs, txt_cty1);
    	load_cty_from_mem(dw, gs->str, gs->len);
		g_string_free(gs, TRUE);
	}
    
    s = g_strconcat(tucnak_dir, "/tucnakdw", NULL);
    ret=load_dw_from_file(dw, s);
    g_free(s);
	if (ret<0){
    	load_dw_from_mem(dw, txt_tucnakdw, sizeof(txt_tucnakdw));
	}
}


struct dw_item *get_dw_item_by_call(struct dw *dw, const char *call){
	char dxcc[25];
	char pref[25];
	struct dw_item *dwi = NULL;
	
	get_pref(pref, call);
	dwi = g_hash_table_lookup(dw->dw, pref); // prefix specific entry (EA1)
	if (dwi != NULL) return dwi;

    get_dxcc(dw, dxcc, call);
	dwi = g_hash_table_lookup(dw->dw, dxcc);
	return dwi; 
}

struct dw_item *get_dw_base_item_by_call(struct dw *dw, const char *call){
	char dxcc[25];
	char pref[25];
	GString *call2;
	struct dw_item *dwi = NULL;

	call2 = g_string_new(call);
	get_dxcc(dw, dxcc, call);
	if (strlen(dxcc) == strlen(call)) {
		g_string_free(call2, TRUE);
		return NULL;
	}

	// SK6DK -> SM6DK
	g_string_erase(call2, 0, strlen(dxcc));
	g_string_insert(call2, 0, dxcc); 

	get_pref(pref, call2->str);
	g_string_free(call2, TRUE);
	dwi = g_hash_table_lookup(dw->dw, pref); // prefix specific entry (EA1)
	if (dwi != NULL) return dwi;

	dwi = g_hash_table_lookup(dw->dw, dxcc);
	return dwi;
}

int wwl_is_ok_by_call(struct dw *dw, gchar *wwl, gchar *call){
    struct dw_item *dxci;
    char swwl[5];
    
    if (!wwl || !call) return 1;
    
	dxci = get_dw_item_by_call(dw, call);
	if (dxci) {
		safe_strncpy(swwl, wwl, 5);
		if (g_hash_table_lookup(dxci->wwls, swwl)){
			return 1;
		}
	}

	// SM6 -> SM
	dxci = get_dw_base_item_by_call(dw, call);
	if (dxci) {
		safe_strncpy(swwl, wwl, 5);
		if (g_hash_table_lookup(dxci->wwls, swwl)){
			return 1;
		}
	}

    return 0;
}


/* 0=OK
   1=WARN, known call from unknown locator
   2=ERR,  prefix and big wwl dont match
   256=WARN, suspicious call
   257=ERR, suspicious call
   
*/ 
int get_susp(struct cw *cw, struct dw *dw, gchar *call, gchar *wwl, int ambig){
    int susp=0;
    gchar *wwl0, *wwl1;
    
    /*dbg("get_susp('%s', '%s')\n", call, wwl);*/
    if (!call || !wwl || !*call || !*wwl) return 0;
    
    if (!wwl_is_ok_by_call(dw, wwl, call)) {
        susp=2; /* bad country */
        goto skip; 
    }

    wwl0=find_wwl_by_call(cw, call);
    wwl1=find_wwl_by_call(cw, NULL); 

    if (wwl0!=NULL){ /* we don't know this call, cannot be suspicious */
        susp = 1;
        if (wwl0 && strcasecmp(wwl0, wwl)==0) susp = 0; 
        if (wwl1 && strcasecmp(wwl1, wwl)==0) susp = 0; 
    }

skip:;    
    if (ambig) if (get_susp_ambiguous_call(cw, NULL, call, wwl, NULL, 1)) susp|=0x100;
    /* dbg("   ret=%d\n", susp);*/
    return susp;
}


int get_susp_call(struct cw *cw, struct dw *dw, gchar *call, gchar *wwl){
    int susp;
    char s[40]; 

    //dbg("get_susp_call('%s', '%s')\n", call, wwl);
    if (!call || !*call){  /* pokud je vsechno NULL nebo "", tak get_susp skonci s nulou a test s /P pokracuje */
        return 0;
    }

    susp=get_susp(cw, dw, call, wwl, 1);
    if (susp) return 1; /* get_susp muze vratit 0x100 a co bysme s tim delali */

    z_get_raw_call(s, call);
    if (strcmp(s, call)==0){ /* without /p */
        strcat(s, "/P");
        //dbg("without %s", s);
        if (find_wwl_by_call(cw, s)) {
            //dbg(" SUSP");
            susp=1;   /* call is without /p and in C_W exists call with /p */
        }
    }else{        /* call is with /p */
        //dbg("with %s", s);
        if (find_wwl_by_call(cw, s)) {
            //dbg(" SUSP");
            susp=1;   /* call is with /p and in C_W exists call without /p */
        }
    }

    
    if (wwl && *wwl) susp=get_susp_ambiguous_call(cw, NULL, call, wwl, NULL, 1);
    
    //dbg("   ret=%d\n", susp);
    return susp;
}

int get_susp_loc(struct cw *cw, struct dw *dw, gchar *call, gchar *wwl){
    return get_susp(cw, dw, call, wwl, 0);
}

#define ADDQSO if (!err) { \
    if (qso->time_str){ \
        g_string_append_printf(gs, "%s %s %c%c.%c%c\n", qso->callsign?qso->callsign:"?", qso->locator?qso->locator:"?", qso->time_str[0], qso->time_str[1], qso->time_str[2], qso->time_str[3]); \
    }else{ \
        g_string_append_printf(gs, "%s %s\n", qso->callsign?qso->callsign:"?", qso->locator?qso->locator:"?"); \
    }err=1;}

enum suspcall qso_info(struct qso *qso, GString *gs, GString *gs2){
    char s[40],s1[40], raw[40];
    int err, susp, k;
    enum suspcall ret;
    char *wwl0, *wwl1;
    struct band *b;
    struct qso *q;
    int i,j;


    ret=SUSP_NONE;
    if (!qso) return (enum suspcall)-1;
    if (qso->error) return (enum suspcall)-1;
    if (qso->dupe) return (enum suspcall)-1;

    err=0;

    /*if (qso->band && qso->band->stats && qso->band->stats->nqsos > 0){
        if (qso->qsop > qso->band->stats->nqsop/qso->band->stats->nqsos) if (ret < SUSP_QRB) ret = SUSP_QRB;
    } */

    if (qso->remark && *qso->remark) {
        if (ret < SUSP_REM) ret = SUSP_REM;
    }

    if (!find_name_by_call(namedb, qso->callsign) &&
        !find_wwl_by_call(cw, qso->callsign)){
		if (gs){
			ADDQSO;
			g_string_append(gs, VTEXT(T_CALLSIGN_UNKNOWN));
		}
        if (ret < SUSP_WARN) ret = SUSP_WARN;
    }

    if (qso->mode == MOD_SSB_CW || qso->mode == MOD_CW_SSB){
		if (gs){
			ADDQSO;
			g_string_append(gs, VTEXT(T_CROSSMODE_QSO_N));
		}
        if (ret < SUSP_WARN) ret = SUSP_WARN;
    }

	if (!get_dw_item_by_call(dw, qso->callsign)){
		if (gs){
			ADDQSO;
			g_string_append(gs, VTEXT(T_UNKNOWN_DXCC_PREFIXN));
		}
        if (ret < SUSP_ERR) ret = SUSP_ERR;
    }


    if (qso->locator && *qso->locator){
        if (!wwl_is_ok_by_call(dw, qso->locator, qso->callsign)){
			if (gs){
				ADDQSO;
				g_string_append(gs, VTEXT(T_PREFIX_AND_BIG_WWL));
			}
            if (ret < SUSP_ERR) ret = SUSP_ERR;
        }
    }

    wwl0=find_wwl_by_call(cw, qso->callsign);
    wwl1=find_wwl_by_call(cw, NULL); 

    susp=0;
    if (wwl0!=NULL){ /* we don't know this call, cannot be suspicious */
        susp = 1;
        if (wwl0 && qso->locator && strcasecmp(wwl0, qso->locator)==0) susp = 0; 
        if (wwl1 && qso->locator && strcasecmp(wwl1, qso->locator)==0) susp = 0; 
    }

    if (qso->locator && *qso->locator){
        if (susp){
			if (gs){
				ADDQSO;
				g_string_append(gs, VTEXT(T_KNOWN_CALL_UNK_LOC));
			}
            if (ret < SUSP_WARN) ret = SUSP_WARN;
        }
    }
    
    z_get_raw_call(raw, qso->callsign);
    strcpy(s, raw);
    if (strcmp(s, qso->callsign)==0){ /* without /p */
        strcpy(s1, s);
        strcat(s, "/P");
        if (find_wwl_by_call_newer(cw, s, cw->minstamp)) {
			if (gs){
				ADDQSO;
				g_string_append(gs, VTEXT(T_CALL_WITHOUT_P));
			}
            if (ret < SUSP_WARN) ret = SUSP_WARN;
        }
        strcat(s1, "/");
        for (k=0;k<=9;k++){
            strcpy(s, s1);
            s[strlen(s)+1]='\0';
            s[strlen(s)]='0'+k;
            if (find_wwl_by_call_newer(cw, s, cw->minstamp)) {
				if (gs){
					ADDQSO;
					g_string_append_printf(gs, VTEXT(T_CALL_WITHOUT_D), k, k);
				}
                if (ret < SUSP_WARN) ret = SUSP_WARN;
            }
        }

    }else{        /* call is with /p */
        if (find_wwl_by_call_newer(cw, s, cw->minstamp)) {
			if (gs){
				ADDQSO;
				g_string_append(gs, VTEXT(T_CALL_IS_WITH_P_AND));
			}
            if (ret < SUSP_WARN) ret = SUSP_WARN;
        }
    }

    if (qso->locator != NULL && gs2){
        g_string_truncate(gs2, 0);
        get_susp_ambiguous_call(cw, qso->band, qso->callsign, qso->locator, gs2, 1);
        if (gs2->len>0){
			if (gs){
				ADDQSO;
				g_string_append_printf(gs, VTEXT(T_OTHER_SIMILAR_CALL_KNOWN), gs2->str);
			}
            if (ret < SUSP_WARN) ret = SUSP_WARN;
        }
    }

    if (ctest){
        for (i=0; i<ctest->bands->len; i++){
            b = (struct band*)g_ptr_array_index(ctest->bands, i);
            if (b == qso->band) continue;
            for (j=0; j<b->qsos->len; j++){
                q = (struct qso *)g_ptr_array_index(b->qsos, j);
                if (q->error || q->dupe) continue;

                z_get_raw_call(s, q->callsign);

                /*if (q->callsign[0]=='D' && q->callsign[1]=='M' && q->callsign[2]=='7'){
                     dbg("call %s %s %s %d\n", q->callsign, s, raw, strcmp(s, raw));
                } */
                if (strcmp(s, raw) != 0) continue; /* not same station */
                 /*dbg("RAW match %s %s\n", s, raw);*/
                if (strcmp(q->callsign, qso->callsign) != 0){ 
					if (gs){
						ADDQSO;
						g_string_append_printf(gs, VTEXT(T_CALL_ON_BAND_S), b->bandname, q->callsign);
					}
                    if (ret < SUSP_WARN) ret = SUSP_WARN;
                }
                
                if (qso->locator != NULL && strcmp(q->locator, qso->locator) != 0){
					if (gs){
						ADDQSO;
						g_string_append_printf(gs, VTEXT(T_LOCATOR_ON_BAND), b->bandname, q->locator);
					}
                    if (ret < SUSP_WARN) ret = SUSP_WARN;
                }

            }
        }
    
    }
    return ret;
}

void menu_qso_check(void *arg){
    GString *gs, *gs2;
    struct band *band;
    struct qso *qso;
    int i,j,ret;
    FILE *f;
    char *filename;

    if (!ctest) return;

    gs=g_string_sized_new(10000);
    gs2=g_string_sized_new(1000);
    g_string_printf(gs,  "%s", VTEXT(T_QSO_CHECK_LIST));
    g_string_append_printf(gs, " *************************\n");
    g_string_append_printf(gs, "\n\n");
    g_string_append_printf(gs, VTEXT(T_CALL_SN), ctest->pcall);
    g_string_append_printf(gs, VTEXT(T_CONTEST_SN), ctest->tname);
    g_string_append_printf(gs, VTEXT(T_DATE_SN), ctest->tdate);
    g_string_append_printf(gs, "\n\n");

    for (i=0;i<ctest->bands->len;i++){
        band = (struct band *)g_ptr_array_index(ctest->bands, i);
		progress(VTEXT(T_CHECKING_S), band->bandname);
        g_string_append_printf(gs, VTEXT(T_BAND_C_SN), band->bandchar, band->pband);
        g_string_append_printf(gs, " ********************\n");
        for (j=0;j<band->qsos->len;j++){
            qso = (struct qso *)g_ptr_array_index(band->qsos, j);
            ret=qso_info(qso, gs, gs2);
            if (ret) g_string_append_printf(gs, "\n");
            if (ret>=0) qso->suspcall1=(enum suspcall)ret;
        }
    }

    g_string_append_printf(gs, "\n\nEOF\n");

    filename = g_strdup_printf("%s/qsocheck.txt", ctest->directory);
	z_wokna(filename);
    f=fopen(filename,"wt");
    if (!f) {
        log_addf(VTEXT(T_CANT_WRITE_S), filename);
        errbox(VTEXT(T_CANT_WRITE), errno);
        g_free(filename);
        g_string_free(gs, TRUE);
        g_string_free(gs2, TRUE);
		progress(NULL);
        return;
    }
    ret=fwrite(gs->str, 1, gs->len, f);
    fclose(f);
    if (ret!=gs->len){
        log_addf(VTEXT(T_CANT_WRITE_S), filename);
        errbox(VTEXT(T_CANT_WRITE), 0);
        g_free(filename);
        g_string_free(gs, TRUE);
        g_string_free(gs2, TRUE);
		progress(NULL);
        return;
    }
    log_addf(VTEXT(T_SAVED_S), filename);
    g_free(filename);
    g_string_free(gs, TRUE);
    g_string_free(gs2, TRUE);
	progress(NULL);
    return;
}


void call_info_map(struct dialog_data *dlg, struct dialog_item_data *di){
	GString *gs = g_string_new("http://k7fry.com/grid/?qth=");
	g_string_append(gs, di->item->text);
	z_browser(gs->str);
	g_string_free(gs, TRUE);
}

struct memory_list *call_info_ml = NULL;
void call_info_wwl(gpointer key, gpointer value, gpointer data){
	add_to_ml(&call_info_ml, g_strdup((char*)key), NULL);
}

void call_info(void *arg){
    struct qso *qso=NULL;
    GString *gs, *gs2;
    static char ss[1024];
    int err,freeqso,i;
    char *title, *c, *txt;
    char raw[30];    
	struct qrv_item *qi;
	struct dw_item *dwi;
	GHashTable *wwls;
	int wwlcnt;

    gs=g_string_sized_new(500);
    gs2=g_string_sized_new(500);
	wwls = g_hash_table_new(g_str_hash, g_str_equal);

	    

	err=0;
	freeqso=0;
/*    dbg("TMPQ.callsign=%s %p\n", TMPQ.callsign, aband);*/

    if (arg != NULL){
		qso = (struct qso *)arg;
        qso_info(qso, gs, gs2);
    }else if (strlen(INPUTLN(aband)->cdata) > 0){
        qso=g_new0(struct qso, 1);
        qso->callsign = INPUTLN(aband)->cdata;    /* foreign key */
        qso->locator = NULL;
        qso->rsts = "";
        qso->qsonrs = "";
        qso->rstr = "";
        qso->qsonrr = "";
		qso_info(qso, gs, gs2);
		freeqso=1;
    }else if (aband && (!TMPQ.callsign || !strlen(TMPQ.callsign))){
        if (aband->qsos->len<=0) goto x;
        qso=(struct qso *)g_ptr_array_index(aband->qsos, aband->qsos->len-1);
        qso_info(qso, gs, gs2);
    }else if (aband){
        /* tmpqso is valid */
        qso=g_new0(struct qso, 1);
        qso->callsign = TMPQ.callsign;    /* foreign keys */
        qso->locator = TMPQ.locator?TMPQ.locator:"";
        qso->rsts = TMPQ.rsts?TMPQ.rsts:"";
        qso->qsonrs = TMPQ.qsonrs?TMPQ.qsonrs:"";
        qso->rstr = TMPQ.rstr?TMPQ.rstr:"";
        qso->qsonrr = TMPQ.qsonrr?TMPQ.qsonrr:"";
		qso_info(qso, gs, gs2);
		freeqso=1;
    }else{
        goto x;
    }

    if (gs->len>0){
        title=VTEXT(T_CALL_INFO);
    }else{
		ADDQSO;
        g_string_append(gs, VTEXT(T_SEEMS_TO_BE_OK));
        safe_strncpy(ss, gs->str, sizeof(ss));
        title=VTEXT(T_OK);
    }

    /* next callsign info */
    g_string_append(gs, "\n");

    /* C_W */
    z_get_raw_call(raw, qso->callsign);
    g_string_truncate(gs2, 0);
    cwdb_call_info(gs2, qso->callsign, "", wwls);
    cwdb_call_info(gs2, qso->callsign, "/P", wwls);
    for (i=0;i<=9;i++){
        char stroke[10];
        sprintf(stroke, "/%d", i);
        cwdb_call_info(gs2, qso->callsign, stroke, wwls);
    }
    if (gs2->len>0){
        g_string_append(gs, gs2->str);
    }else{
        g_string_append_printf(gs, VTEXT(T_CW_NOT_FOUND), qso->callsign);
    }

    /* wkd */
    if (ctest){
        g_string_append(gs, "WKD on: ");
        for (i = 0; i < ctest->bands->len; i++){
            struct band *b = (struct band*) g_ptr_array_index(ctest->bands, i);
            if (get_qso_by_callsign(b, qso->callsign)) g_string_append_c(gs, toupper(b->bandchar));
        }
        g_string_append_c(gs, '\n');
    }

    /* qrv */
    qi = qrv_get(qrv, qso->callsign);
    g_string_append_printf(gs, "QRV: ");
    if (qi){
//        g_string_append_printf(gs, " %s", qi->wwl);
        if (aband){
            g_string_append_printf(gs, " %dx", qi->wkd[aband->bandchar - 'a']);
        }
        if (qi->kst_time != 0){
            struct tm utc;
            gmtime_r(&qi->kst_time, &utc);
            g_string_append_printf(gs, " %2d:%02d", utc.tm_hour, utc.tm_min);
        }
		if (qi->text != NULL){
			g_string_append_printf(gs, "  %s", qi->text);
		}
    }else{
        g_string_append(gs, VTEXT(T_0X_NOT_IN_DB));
    }
    g_string_append_c(gs, '\n');

    /* namedb */
    c=find_name_by_call(namedb, qso->callsign);
    if (c){
        g_string_append_printf(gs, VTEXT(T_NAME_IS), qso->callsign, c);
    }else{
        g_string_append_printf(gs, VTEXT(T_NAME_UNKNOWN), qso->callsign);
    }

	/* dxcc */
	dwi = get_dw_item_by_call(dw, qso->callsign);
	if (dwi){
		char dxcc[32];
		get_dxcc(dw, dxcc, qso->callsign);
		g_string_append_printf(gs, "DXCC: %s %s\n", dxcc, dwi->dxcname);
	}

	txt = g_strdup(gs->str);
	call_info_ml = getml(txt, NULL);

	/* locator itself */
	if (regcmp(qso->callsign,"^[A-R]{2}[0-9]{2}[A-X]{2}$") == 0){
		char *loc = qso->callsign;
		if (g_hash_table_lookup(wwls, loc) == NULL) {
			loc = g_strdup(loc);
			g_hash_table_insert(wwls, loc, NULL);
			add_to_ml(&call_info_ml, loc, NULL);
		}
	}
    
	g_hash_table_foreach(wwls, call_info_wwl, NULL);
	wwlcnt = g_hash_table_size(wwls);
	if (wwlcnt > 3) wwlcnt = 3;
	msg_box(call_info_ml, title, AL_LEFT, txt, NULL, 1 + wwlcnt, VTEXT(T_OK), NULL, B_ENTER | B_ESC, 
		call_info_ml->p[1 + 0], call_info_map, 0,
		call_info_ml->p[1 + 1], call_info_map, 0,
		call_info_ml->p[1 + 2], call_info_map, 0);
x:;    
    g_string_free(gs, TRUE);
    g_string_free(gs2, TRUE);
	g_hash_table_destroy(wwls);
    if (freeqso) g_free(qso);
}


gchar *find_wwl_by_dxc_or_pref(struct dw *dw, gchar *call){
    struct dw_item *dxci;

    if (!call) return dw_next;
    dw_next = NULL;
    
	dxci = get_dw_item_by_call(dw, call);
    if (!dxci) return NULL;

    dw_next = dxci->wwl1;

    return dxci->wwl0;
    
}

gchar *find_dxc_by_wwl(struct dw *dw, gchar *wwl){
    struct wd_item *wdi;
    char s[10];

    if (!wwl) return wd_next;
    wd_next = NULL;

    safe_strncpy(s, wwl, 10);
    if (strlen(s)>4) s[4]='\0';

    wdi = (struct wd_item *)g_hash_table_lookup(dw->wd, s);
    if (!wdi) return NULL;
    
    wd_next = wdi->dxc1;
    return wdi->dxc0;
    
}


char *get_dxcc(struct dw *dw, char *buf, const char *call){ 
    char *c0, *c1, *c2, *dxc;
    char s[100], *stroke = NULL;
    
    c0=c1=c2=NULL;
    safe_strncpy0(buf, call, 20);

    if (regmatch(call, "^(.*[0-9])[A-Z]*(/[A-Z])?", &c0, &c1, &c2, NULL)==0){
        
		//  dbg("\n%s '%s' '%s' '%s'\n", call, c0, c1, c2);
        safe_strncpy0(buf, c1, 20);
		if (c2 != NULL){
            stroke = c2;
        }
        while (*buf!='\0'){
            safe_strncpy0(s, buf, 20);
            if (stroke) strcat(s, stroke);
        //    dbg("trying1 '%s'\n", s);
            dxc = (gchar *)g_hash_table_lookup(dw->pd, s);
            if (dxc) {
          //      dbg("get_dxcc(%s)='%s'\n", call, s); 
                safe_strncpy0(buf, dxc, 20);
                goto found;
            }
            buf[strlen(buf)-1]='\0'; 
        }
        
        safe_strncpy0(buf, c1, 20);
        while (*buf!='\0'){
			safe_strncpy0(s, buf, 20);
           // dbg("trying2 '%s'\n", s);
            dxc = (gchar *)g_hash_table_lookup(dw->pd, s);
            if (dxc) {
             //   dbg("get_dxcc(%s)='%s'\n", call, s); 
                safe_strncpy0(buf, dxc, 20);
                goto found;
            }
            buf[strlen(buf)-1]='\0'; 
        }
        safe_strncpy0(buf, call, 20);
    }
found:;    
    if (c0) g_free(c0);
    if (c1) g_free(c1);
    if (c2) g_free(c2);

    dxc = (gchar *)g_hash_table_lookup(dw->pd, buf);
	if (dxc) safe_strncpy0(buf, dxc, 20);
//    dbg("get_dxcc(%s)='%s'\n", call, buf);

    return buf;  
}

char *get_pref(char *buf, const char *call){ 
    char *c0, *c1, *c2;
    
    c0=c1=c2=NULL;
    safe_strncpy0(buf, call, 20);
	if (regmatch(call, "^(.*)[0-9][A-Z]*\\/([0-9])", &c0, &c1, &c2, NULL) == 0){
		if (c2 != NULL && strlen(c2) == 1 && isdigit(c2[0]))
		{
			g_snprintf(buf, 20, "%s%s", c1, c2);
		}
	}
	else if (regmatch(call, "^(.*[0-9])[A-Z]*", &c0, &c1, NULL)==0){
        safe_strncpy0(buf, c1, 20);
    }
    if (c0) g_free(c0);
	if (c1) g_free(c1);
	if (c2) g_free(c2);
    //dbg("get_pref(%s)='%s'\n", call, buf);
    return buf;
}
