/*
    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 "dxc.h"
#include "excdb.h"
#include "inputln.h"
#include "kbd.h"
#include "kbdbind.h"
#include "main.h"
#include "rc.h"
#include "qsodb.h"
#include "session.h"
#include "stats.h"
#include "subwin.h"
#include "terminal.h"
#include "tregex.h"
#include "trig.h"
#include "tsdl.h"
#include "qrvdb.h"
#include "qsodb.h"


//#define MIN_SPOTLEN 74
#define MIN_SPOTLEN 29
#define STR_DX_DE "DX de"
#define FROM_OFS 6
#define CALL_OFS 26
#define TEXT_OFS 39
#define ZULU_OFS 70

#define FROM_SEP ' '
#define DXC_EXPIRE_TIMER 5*60*1000

struct spotdb_t *spotdb=NULL;


struct spotdb_t *init_spotdb(void){
    struct spotdb_t *spotdb;

	progress(VTEXT(T_INIT_DXC));		

    spotdb = g_new0(struct spotdb_t, 1);
	spotdb->bands = z_ptr_array_new();
	{
		zg_ptr_array_foreach(struct config_band *, cband, cfg->bands) 
		{
			struct spotband *sband = sband = g_new0(struct spotband, 1);
			sband->bandchar = z_char_uc(cband->bandchar);
			sband->freq = z_ptr_array_new();
			sband->current = 0;
			sband->min_khz = cband->qrg_min;
			sband->max_khz = cband->qrg_max;
			z_ptr_array_add(spotdb->bands, sband);
		}
	}
    spotdb->timer_id = zselect_timer_new(zsel, DXC_EXPIRE_TIMER, dxc_timer, NULL);
    return spotdb;
}

void free_spotdb(struct spotdb_t *spotdb){
    
	progress(VTEXT(T_FREE_DXC));		

    zselect_timer_kill(zsel, spotdb->timer_id);
	{
		zg_ptr_array_foreach(struct spotband *, sband, spotdb->bands)
		{
			zg_ptr_array_foreach(struct spot *, spot, sband->freq)
			{
				free_spot(spot);
			}
			z_ptr_array_free(sband->freq, TRUE);
			g_free(sband);
		}
		z_ptr_array_free(spotdb->bands, TRUE);
    }
    g_free(spotdb);
}

struct spotband *get_spotband_by_khz(int khz){
	zg_ptr_array_foreach(struct spotband *, sband, spotdb->bands)
	{
		if (khz < sband->min_khz) continue;
		if (khz > sband->max_khz) continue;
		return sband;
	}
    return NULL;
}

struct spotband *get_actual_spotband(){
	if (aband){
		zg_ptr_array_foreach(struct spotband *, sband, spotdb->bands)
		{
			if (sband->bandchar == z_char_uc(aband->bandchar)) return sband;
		}
	}
	return NULL;

}

void dxc_timer(void *arg){
    int n;
    n = dxc_remove_expired();
    /*dbg("dxc_timer expired=%d\n",n);*/
    if (n){
        redraw_later();
    }
    
    spotdb->timer_id = zselect_timer_new(zsel, DXC_EXPIRE_TIMER, dxc_timer, NULL);
}


// "DX de 5B4AIX:   144250.0  C91KHN       USUAL ZOO..                    1732Z KM64
// "DX de F4EGZ-#:     50009.4  SV9SIX       09 dB  9 WPM  BEACON          1158Z"	char *

struct spot *dxc_parse_spot(gchar *str){
    double qrg;
    gchar *callsign,*from,*text;
    int zulu;
    struct spot *spot;
    gchar *c,*d;
    time_t now;
	char *c1=NULL, *c2=NULL;


   // dbg("dxc_parse_spot('%s')]\n", str);
	if (regmatch(str, "SFI=([0-9]{1,3})", &c1, &c2, NULL)==0){
		spotdb->sfi = atoi(c2);
	}
	if (c1) g_free(c1);
	if (c2) g_free(c2);

    
    if (strlen(str)<MIN_SPOTLEN) return NULL;
    if (strncasecmp(str, STR_DX_DE, strlen(STR_DX_DE))!=0) return NULL;

    callsign=from=text=NULL;
    
    d=str+FROM_OFS;
    c=strchr(d,FROM_SEP);
    
    if (!c) goto x;
    from=g_strndup(d, c-d);
    z_str_lc(from);

    c++;
    qrg=strtod(c, (char **)NULL);

    d=str+CALL_OFS;
    if (*d == ' ') d++;  // spots from rbn.net
    if (*d == ' ') d++;  // spots from rbn.net
	c=strchr(d,' ');
    if (!c) goto x;
    callsign=g_strndup(d, c-d);
    if (strlen(callsign)<3) goto x;

    if (strlen(str)>ZULU_OFS){
        d=str+ZULU_OFS;
        d[4]='\0';
        zulu=atoi(d);
    }else{
        time_t now;
        struct tm tm;
        now = time(NULL);
        gmtime_r(&now, &tm);
        zulu = tm.tm_hour * 100 + tm.tm_min;
    }

    if (strlen(str)>TEXT_OFS){
        d=str+TEXT_OFS;
        if (strlen(str)>ZULU_OFS){
            str[ZULU_OFS]='\0';
        }
        c=strrchr(d, ' ');
        g_strstrip(d);
        if (!c) c=d+ZULU_OFS-1;
        text=g_strndup(d, c-d);
    }else{
        text=g_strdup("");
    }

    now=time(NULL);
    
    spot=g_new0(struct spot, 1); /* LEAK */
    spot->qrg     =qrg;
    spot->callsign=callsign;
    spot->from    =from;
    spot->text    =text;
    spot->zulu    =zulu;
#if 1    
    spot->endbold =now + 15 * 60;
    spot->expire  =now + 60 * 60;
#else    
    spot->endbold =now + 1 * 60;
    spot->expire  =now + 2 * 60;
#endif    

//    dbg("call='%s' qrg='%3.1f' from='%s' text='%s' zulu='%d'\n", callsign, qrg, from, text, zulu);
    return spot;    
x:;
    zg_free0(callsign);
    zg_free0(from);    
    zg_free0(text);    
    return NULL;
}  

void free_spot(struct spot *spot){
    /*dbg("free_spot(%p)\n", spot);*/
            
    if (!spot) return;

    g_free(spot->callsign);
    g_free(spot->from);
    g_free(spot->text);
    g_free(spot);
}

/* removes spot from list, don't free it from mem */
void remove_spot(struct spotband *sband, int spot_i){
	struct spot *spot;

	spot = (struct spot *)z_ptr_array_index(sband->freq, spot_i);
	z_ptr_array_remove_index(sband->freq, spot_i);
	free_spot(spot);
	if (spot_i < sband->current) sband->current--; // can reach -1
	if (sband->current >= sband->freq->len) sband->current = sband->freq->len - 1;
}

int dxc_read_spot(gchar *str){
    struct spot *newspot;
    gchar *s;
    int newpos;
	struct spotband *sband;
    struct band *band;
	int filterspot = 0;

    /*dbg("dxc_read_spot('%s')\n", str);*/
    s=g_strdup(str);
    newspot=dxc_parse_spot(s);
    g_free(s);
    
    if (!newspot) return -3;
    
    /*dbg(" spot accepted %p\n", str, newspot);*/
	sband = get_spotband_by_khz((int)newspot->qrg);
    if (!sband){
       // dbg(" bad qrg %d\n", (int)newspot->qrg);
        free_spot(newspot);
		if (ctest) return 1; // in contest we filter unknown bands (warc)
        return -1;
    }
      
	if (ctest){
		band = find_band_by_bandchar(sband->bandchar);
		if (!band){
			filterspot = 1;
		}else{ 
			if (band->readonly) filterspot = 1;
		}
	}

	/*if (print && !filterspot)
	{
		zg_ptr_array_foreach(struct subwin *, sw, gses->subwins){
			if (sw->type != SWT_DXC) continue;
			sw_printf(sw, "%s\n", str);
		}
	} */
    
    newpos = 0;
	{
		zg_ptr_array_foreachback(struct spot *, spot, sband->freq){
			if (strcasecmp(spot->callsign, newspot->callsign) == 0 ) {
				remove_spot(sband, spot_i);
				break;
			}
		}

		{
			zg_ptr_array_foreach(struct spot *, spot, sband->freq)
			{
				if (spot->qrg > newspot->qrg) 
					break;
				newpos = spot_i + 1;  
			}
		}
		//dxc_dump(sband);
		//dbg("insert %s %9.1f\n", newspot->callsign, newspot->qrg);
		z_ptr_array_insert(sband->freq, newspot, newpos);
		if (newpos < sband->current) sband->current++;
        if (sband->current < 0) sband->current = 0;
		//dxc_dump(sband);								 
    }
	
    return filterspot;
}

int dxc_remove_expired(void){
    int n;
    time_t now;
    
    n=0;
    now=time(NULL);
	{
		zg_ptr_array_foreach(struct spotband *, sband, spotdb->bands)
		{
			zg_ptr_array_foreachback(struct spot *, spot, sband->freq)
			{
				if (spot->expire >= now) continue;
				remove_spot(sband, spot_i);
				n++;
			}
		}
	}
    return n;
}

void dxc_dump(struct spotband *sband){

    dbg("dxc_dump\n");
	{
		zg_ptr_array_foreach(struct spot *, spot, sband->freq)
		{
			dbg("%c%02d: %10s %10s %10.1f\n", sband->bandchar, spot_i, spot->from, spot->callsign, spot->qrg);
		}
	}
    dbg("----\n");
}



/*

0     6          17       26           39                             70  
DX de ja8edu:    21285.0  OK2SSD       CQ,CQ DX                       0759Z
DX de JA8EDU:    21285.0  OK2SSD       CQ,CQ DX                       0759Z
DX de HL2IFR:    24900.2  BA4ED        AS-136                         0807Z
DX de HL2IFR:    24900.2  BA4ED        AS-136                      33 0807Z 44
DX de JA9LSZ:    28008.2  8Q7VR        cq                             0815Z
DX de JA9LSZ:    28008.2  8Q7VR        cq                             0815Z
DX de OK1MZM:    144300.0 I4XCC        1116Z 55 JN63GV<---JN69QS
144370.0  SM/DJ8MS     5-Mar-2003 0801Z  cq 357 JO88 dir G            <DJ8MS>
144370.0  SM/DJ8MS     5-Mar-2003 0801Z  cq 357 JO88 dir G             <dj8ms>
 144370.0 SM/DJ8MS     0801  5.3  DJ8MS  cq 357 JO88 dir G
144370.0  SM/DJ8MS     5-Mar-2003 0801Z  cq 357 JO88 dir G            <DJ8MS>
 144370.0 SM/DJ8MS     0801  5.3  DJ8MS  cq 357 JO88 dir G
144370.0  SM/DJ8MS     5-Mar-2003 0801Z  cq 357 JO88 dir G            <DJ8MS>
144300.0 HB9QQ        1821 21.8  ON1AEN JN47 SRI TYPO 

 */


/*#define dxc_highlight(text, ho, needle, color){\
    gchar *cc, *d, *fd; \
    int rx, fx, fl; \
    \
    if ((needle) && strlen(needle)>=1){ \
        cc=(text); \
        while ((d=z_strcasestr((cc), (needle)))!=NULL){ \
            fl=MIN(sw->w-(d-(text))+(ho) , strlen(needle)); \
            fx=rx=d-(text)-(ho); \
            fd=d; \
            if (rx<0) { \
                fl+=rx; \
                fx=0; \
                fd=d-rx; \
            } \
            if (fl>0) print_text(sw->x+fx, sw->y+i, fl, fd, (color)); \
            cc=d+strlen(needle); \
        } \
    }\
}  */

int sw_dxc_kbd_func(struct subwin *sw, struct event *ev, int fw){
    /*dbg("sw_dxc_kbd_func [%d,%d,%d,%d]\n",ev->ev,ev->x,ev->y,ev->b);*/
	if (zselect_profiling(zsel)) zselect_hint(zsel, __FUNCTION__);

    

    if (sw->il && (
        sw->il->wasctrlv ||    
		(ev->x!='[' && ev->x!=']') )){
			int ret;

            ret = inputln_func(sw->il, ev);

			if (ev->x!=KBD_LEFT &&
                ev->x!=KBD_RIGHT &&
                ev->x!=KBD_HOME &&
                ev->x!=KBD_END){

                
				if (strlen(sw->il->cdata) < 1){
                    zg_free0(sw->pattern);
                }else{
                    char *c;

                    c = strchr(sw->il->cdata, ' ');
                    if (c){
                        zg_free0(sw->pattern);
                    }else{
                        zg_free0(sw->pattern);
                        sw->pattern = g_strdup(sw->il->cdata);
                    }
		            sw->offset = 0;
                }
                redraw_later();
                if (ret) return ret;
			}
	        //dbg("sw->pattern('%s')\n", sw->pattern);
		
    }else{
	    //dbg("sw->pattern('%s')\n", sw->pattern);
    }

	if (ev->y & KBD_SHIFT){
		switch (ev->x){
			case KBD_UP:
				if (sw->side_bott) return 1;
				sw->dxc_offset -= 1;
				//if (sw->dxc_offset < 0) sw->dxc_offset = 0;
				//dbg("dxc_offset=%d\n", sw->dxc_offset);
				redraw_later();
				return 1;
			case KBD_DOWN:
				if (sw->side_top) return 1;
				sw->dxc_offset += 1;
				//dbg("dxc_offset=%d\n", sw->dxc_offset);
				redraw_later();
				return 1;
			case KBD_PGUP:
				if (sw->side_bott) return 1;
				sw->dxc_offset -= sw->h / 2;
				//if (sw->dxc_offset < 0) sw->dxc_offset = 0;
				redraw_later();
				return 1;
			case KBD_PGDN:
				if (sw->side_top) return 1;
				sw->dxc_offset += sw->h / 2;
				redraw_later();
				return 1;
			case KBD_HOME:
				sw->dxc_offset = 0;
				redraw_later();
				return 1;

		}
	}

	if (ev->y & KBD_CTRL){
		struct spot *spot;
		struct spotband *sband = get_actual_spotband();
		if (sband == NULL) return 1;
		if (sband->current < 0 || sband->current >= sband->freq->len) return 1;

		switch (ev->x){
			case KBD_UP:
				if (sband->current + 1 >= sband->freq->len) return 1;
				sband->current++;
				spot = (struct spot *)z_ptr_array_index(sband->freq, sband->current);
				if (spot == NULL) return 1;
#ifdef HAVE_HAMLIB                
				trigs_set_qrg(gtrigs, spot->qrg * 1000.0);
#endif
				redraw_later();
				return 1;
			case KBD_DOWN:
				if (sband->current - 1 < 0) return 1;
				sband->current--;
				spot = (struct spot *)z_ptr_array_index(sband->freq, sband->current);
				if (spot == NULL) return 1;
#ifdef HAVE_HAMLIB                
				trigs_set_qrg(gtrigs, spot->qrg * 1000.0);
#endif
				redraw_later();
				return 1;
		}
	}

    switch(kbd_action(KM_MAIN,ev)){
        case ACT_ESC:
            return 0;
            break;
        case ACT_DOWN:
            sw->offset--;
            sw->check_bounds(sw);
            redraw_later();
            return 1;
        case ACT_UP:
            sw->offset++;
            sw->check_bounds(sw);
            redraw_later();
            return 1;
        case ACT_PAGE_DOWN:
            sw->offset -= sw->h - 1;;
            sw->check_bounds(sw);
            redraw_later();
            return 1;
        case ACT_PAGE_UP:
            sw->offset += sw->h - 1;;
            sw->check_bounds(sw);
            redraw_later();
            return 1;
        case ACT_HOME:
            sw->offset = sw->lines->len - sw->h; 
            sw->check_bounds(sw);
            redraw_later();
            return 1;
        case ACT_END:
            sw->offset = 0;
            sw->check_bounds(sw);
            redraw_later();
            return 1;
        case ACT_SCROLL_LEFT:
            if (sw->lines)
    			if (sw->ho>0) sw->ho--;
            redraw_later();
            return 1;
        case ACT_SCROLL_RIGHT:
            if (sw->lines)
			    sw->ho++;
            redraw_later();
    }
    return 0;
}

int sw_dxc_mouse_func(struct subwin *sw, struct event *ev, int fw){
    struct menu_item *mi = NULL;
	int y, items = 0;
    
	if (zselect_profiling(zsel)) zselect_hint(zsel, __FUNCTION__);

	if (ev->b & B_DRAG){
		int dy = sw_accel_dy(sw, ev);
		if (dy == 0) return 1;

		if (ev->x > sw->w){
			//dbg("dy=%d\n", dy);
			if (dy > 0){
				if (sw->side_bott) return 1;
				if (dy > sw->h / 2) 
					dy = sw->h / 2;
			}else{
				if (sw->side_top) return 1;
				if (- dy > sw->h / 2) 
					dy = - sw->h / 2;
			}
			//dbg("fixed dy=%d\n", dy);

			sw->dxc_offset -= dy;

			//if (sw->dxc_offset < 0) sw->dxc_offset = 0;
			redraw_later();
			return 1;
		}

        sw->offset += dy;
        sw->check_bounds(sw);
        redraw_later();

		return 1;
	}

    //if ((ev->b & BM_ACT)!=B_DOWN) return 0;
//    dbg("ev->b=%x BM_ACT=%x B_DOWN=%x BM_EBUTT=%x  \n", ev->b, BM_ACT, B_DOWN, BM_EBUTT);
	if (cfg->usetouch && (ev->b & BM_EBUTT) == B_LEFT) ev->b = (ev->b & ~BM_EBUTT) | B_RIGHT;
    switch (ev->b & BM_BUTCL){
		case B_LEFT:
		case B_RIGHT:
			sw->olddragx = ev->x;
			sw->olddragy = ev->my;
			return 1;
        case B_LEFT | B_CLICK:
            if (!sw->il) break;
            if (ev->y==sw->y+sw->h){
                inputln_func(sw->il, ev);
                return 1;
            }
			g_free(sw->callunder);
			g_free(sw->refunder);
			sw->refunder = NULL;
			sw->callunder = sw_dxc_call_under(sw, ev->x - sw->x + sw->ho, ev->y - sw->y, &sw->qrg, &(sw->refunder));
			if (!sw->callunder) break;
			sw_dxc_toggle_highlight(sw, sw->callunder, 1);
            return 1;
        case B_MIDDLE:
/*            dbg("middle\n");*/
            break;
        case B_RIGHT | B_CLICK:
			if (ev->y==sw->y+sw->h){
                inputln_func(sw->il, ev);
                return 1;
            }
			g_free(sw->callunder);
			g_free(sw->refunder);
			sw->refunder = NULL;
			sw->callunder = sw_dxc_call_under(sw, ev->x - sw->x + sw->ho, ev->y - sw->y, &sw->qrg, &(sw->refunder));
			if (!sw->callunder) break;
			mi = new_menu(1);
			mi->rtext = sw->callunder;
	        add_to_menu(&mi, CTEXT(T_SELECT), "", CTEXT(T_HK_SELECT), sw_dxc_select, NULL, 0); items++;    
	        add_to_menu(&mi, CTEXT(T_HIDE),   "", CTEXT(T_HK_HIDE), sw_dxc_hide, NULL, 0); items++;
	        add_to_menu(&mi, CTEXT(T_INFO),   "", CTEXT(T_HK_INFO), sw_dxc_info, NULL, 0); items++;
	        if (aband) {
				add_to_menu(&mi, CTEXT(T_USE),    "", CTEXT(T_HK_USE), sw_dxc_use, NULL, 0);
				items++;
			}
			y = ev->y - 1;
			if (cfg->usetouch) y = ev->y - items - 1;
		    set_window_ptr(gses->win, ev->x - 3, y);
		    do_menu(mi, sw);

            break;
        case B_WHUP:
/*            dbg("wheel up\n");*/
			if (ev->x > sw->w){
				if (sw->side_bott) return 1;
				sw->dxc_offset -= 3;
				//if (sw->dxc_offset < 0) sw->dxc_offset = 0;
				//dbg("dxc_offset=%d\n", sw->dxc_offset);
				redraw_later();
				return 1;
			}
            sw->offset+=3;
            sw->check_bounds(sw);
            redraw_later();
            return 1;
        case B_WHDOWN:
/*            dbg("wheel down\n");  */
			if (ev->x > sw->w){
				if (sw->side_top) return 1;
				sw->dxc_offset += 3;
				//dbg("kst_offset=%d\n", sw->kst_offset);
				redraw_later();
				return 1;
			}
            sw->offset-=3;
            sw->check_bounds(sw);
            redraw_later();
            return 1;
    }
    return 0;
}


void sw_dxc_redraw(struct subwin *sw, struct band *band, int flags){
    int i, index, col;
    gchar *line, *pcall, *ccall;
    int ccol;
	struct spotband *sband;
	int ofs = 0;

	sband = get_actual_spotband();

    //dbg("sw->il = %p\n", sw->il);
	if (ctest){
		pcall = ctest->pcall;
	}else{
		pcall = cfg->pcall;
	}

    //dbg("sw_dxc_redraw: offset=%d pattern='%s'\n", sw->offset, sw->pattern);
    //dbg("sw_dxc_redraw: term->x=%d ww=%d w=%d\n", term->x, sw->ww, sw->w);
    if (sw->pattern){
        index = sw->lines->len;
		ofs = sw->offset;
    }else{
        index = sw->lines->len - sw->offset;
    }

    for (i = sw->h - 1; i >= 0; i--){
		if (sw->pattern){
			for (index--; index >= 0; index--){
				char *x;
                if (index < 0 || index >= sw->lines->len)
                   line = "~"; 
                else
                   line = (char *)g_ptr_array_index(sw->lines, index);
                x = z_strcasestr(line, sw->pattern);
                //dbg("i=%d index=%d x=%p line=%s\n", i, index, x, line);
                if (x != 0) {
					if (ofs)
						ofs--;
					else
						goto found;
				}
            }
            continue;
		}else{
			index--; 
            if (index < 0 || index >= sw->lines->len)
               line = "~"; 
            else
               line = (char *)g_ptr_array_index(sw->lines, index);
        }
found:;
        if (!line || strlen(line) <= sw->ho) continue;
		
        col = sw_dxc_line_color(sw, line);
        ccall = sw_dxc_counterp(sw, line, &ccol);
        if (sw->pattern){
			set_color(sw->x - 1, sw->y + i, COL_DARKCYAN * 8 | COL_NORM);
            //print_text(sw->x, sw->y+i, 1, " ", COL(6 * 8));
            print_text(sw->x, sw->y+i, sw->w, line + sw->ho, col);
            if (ccall) sw_highlight(sw, line, ccall, ccol, i, 1);
	    	//dxc_highlight(line, sw->ho, pcall, COL_RED);
			sw_highlight(sw, line, sw->pattern, COL_RED, i, 0);
        }else{
            print_text(sw->x, sw->y+i, sw->w, line + sw->ho, col);
            if (ccall) sw_highlight(sw, line, ccall, ccol, i, 1);
	    	sw_highlight(sw, line, pcall, COL_RED, i, 1);
        }
    }

	if (sband){
		int y, idx;
        time_t now;
		//char raw[20];
		int col;
		int maxw = 0;
		char s[1000];

		for (y = 0; y < sw->hh; y++){
			int lll;
			struct spot *spot;

			idx = sband->current + sw->h / 2 - y; 
			if (idx < 0 || idx >= sband->freq->len) continue;
			spot = (struct spot *)z_ptr_array_index(sband->freq, idx);
			sprintf(s, "%1.1f", spot->qrg);
			lll = strlen(s);
			if (lll > maxw) maxw = lll;
		}

        now = time(NULL);
		sw->side_top = sw->side_bott = 0;
		for (y = 0; y < sw->hh; y++){
			idx = sband->current + sw->h / 2 - y - sw->dxc_offset; 

			if (idx < 0 || idx >= sband->freq->len){
				col = COL_NORM;
			    if (idx == sband->current) col = COL_INV;
				clip_printf(sw, sw->w, y, col, "%-10s %-10s ", "~", "");
				if (idx < 0) sw->side_top = 1;
				else sw->side_bott = 1;
            }else{
				struct spot *spot = (struct spot *)z_ptr_array_index(sband->freq, idx);
				col = COL_NORM;
                if (now < spot->endbold) col = COL_WHITE;

				if (idx == sband->current) clip_printf(sw, sw->w, y, COL_NORM, "[");
				sprintf(s, "%1.1f", spot->qrg);
				clip_printf(sw, sw->w+1, y, col, "%*s", maxw, s);
				//z_get_raw_call(raw, spot->callsign);

				col = sw_dxc_color(spot->callsign);					// color from raw call but print whole call
				clip_printf(sw, sw->w + maxw + 1, y, col, " %s", spot->callsign);
				if (dxc_new_mult(spot->callsign)){
					clip_printf(sw, sw->w + maxw + 1, y, col, "!"); 
				}
				if (idx == sband->current) clip_printf(sw, sw->w + 21, y, COL_NORM, "]");
			}
		}


	}

}

void sw_dxc_check_bounds(struct subwin *sw){
    
    if (sw->offset < 0) {
        sw->offset=0;
        sw_shake(sw, 0);
    }
    if (sw->offset > sw->lines->len - 1) {
        sw->offset = sw->lines->len - 1;
        if (sw->offset >= 0) sw_shake(sw, 1);
    }
    if (sw->offset < 0) {
        sw->offset=0;
        sw_shake(sw, 0);
    }
}

void sw_dxc_enter(void *enterdata, gchar *str, int cq){
	struct subwin *sw;

	sw = (struct subwin *)enterdata;

    //dbg("sw_dxc_enter('%s', %d)\n", str, sw->sock);
    if (sw->sock < 0){
	    if (strcmp(str, "") == 0){
            dxc_open_connection(sw, NULL);
            return;
        }
		return;
	}else{
        if (strcmp(str, "\x03") == 0){
            sw_dxc_disconnect(sw);
            return;
        }
        send(sw->sock, str, strlen(str), 0);
        send(sw->sock, "\r\n", 2, 0);
    }
}

void sw_dxc_addrinfo(struct zasyncdns *adns, int n, int *family, int *socktype, int *protocol, int *addrlen, union zsockaddr *addr, char *errorstr){
	struct subwin *sw;
	int i, ret;
    GString *gs;
    char errbuf[100];

	sw = (struct subwin *)adns->arg;
    if (cfg->dxc_port < 1 || cfg->dxc_port > 65535){
        sw_printf(sw, VTEXT(T_BAD_DXC_PORT), cfg->dxc_port);
	    sw_printf(sw, VTEXT(T_ENTER_TO_DXC));
        return;
    }
    if (errorstr != NULL){
        sw_printf(sw, VTEXT(T_CANT_RESOLVE), cfg->dxc_host, errorstr);
	    sw_printf(sw, VTEXT(T_ENTER_TO_DXC));
        return;
    }
    gs = g_string_new(VTEXT(T_HOST_RESOLVED));
	for (i = 0; i < n; i++){
		z_sock_ntoa(gs, family[i], addr + i); 
		g_string_append_c(gs, ' ');
	}
	g_string_append(gs, "***\n"); 
    sw_add_block(sw, gs->str);
    g_string_free(gs, TRUE);	 

	for (i = 0; i < n; i++){
        //dbg("socket(%d, %d, %d)\n", family[i], socktype[i], protocol[i]);
		//sw->sock = socket(family[i], socktype[i], protocol[i]);
		sw->sock = socket(family[i], SOCK_STREAM, 0);
		int err = z_sock_errno;
		if (sw->sock < 0) {
            sw_printf(sw, VTEXT(T_CANT_CREATE_SOCKET), z_sock_strerror());
            continue;
        }
		if (z_sock_nonblock(sw->sock, 1)) {
			closesocket(sw->sock);
			continue;
		}

        switch (family[i]){
            case AF_INET:
		        ((struct sockaddr_in *)(addr + i))->sin_port = htons(cfg->dxc_port);
                break;
#ifdef AF_INET6
            case AF_INET6:
		        ((struct sockaddr_in6 *)(addr + i))->sin6_port = htons(cfg->dxc_port);
                break;
#endif
            default:       
				closesocket(sw->sock);
                continue;       // unsupported protocol family
        }
		ret = connect(sw->sock, (struct sockaddr *)(addr + i), addrlen[i]);
		if (ret < 0){
			int err = z_sock_errno;
			GString *gs2 = g_string_new("");
			if (z_sock_wouldblock(err)){
                sw_printf(sw, VTEXT(T_CONNECTING_TO), z_sock_ntoa(gs2, family[i], addr + i));
                zselect_set(zsel, sw->sock, NULL, sw_dxc_connected_handler, NULL, sw); 
			}else{
                sw_printf(sw, VTEXT(T_CANT_CONNECT_TO), z_sock_ntoa(gs2, family[i], addr + i), z_sock_strerror());
                closesocket(sw->sock);
                dbg("sw_dxc_addrinfo -1\n");
                sw->sock = -1;
			}
			g_string_free(gs2, TRUE);
		}
        else{
            sw_dxc_connected_handler(sw);
        }
        break;
	}
}

void sw_dxc_connected_handler(void *xxx){
    struct subwin *sw = (struct subwin *)xxx;

    if (z_sock_error(sw->sock)){
        sw_dxc_disconnect(sw);
        return;
    }

    sw_printf(sw, VTEXT(T_CONNECTED_SOCKET), sw->sock);
    zselect_set(zsel, sw->sock, sw_dxc_read_handler, NULL, NULL, sw); 
	sw->chat = 1;
}

void sw_dxc_read_handler(void *xxx){
    char buf[1030], *d, *last, errbuf[100];
    int i, ret;
    struct subwin *sw = (struct subwin *)xxx;

    ret = recv(sw->sock, buf, 1024, 0);
	int err = z_sock_errno;
	if (ret <= 0){
		if (z_sock_errno != 0){ // zero is handled by sw_dxc_disconnect()
		    sw_printf(sw, VTEXT(T_ERROR_READING_SOCKET), z_sock_strerror());
		}
        sw_dxc_disconnect(sw);
        return;
    }

    for (i = 0, d = buf; i < ret; i++){
        if (buf[i] == '\0') continue;
        if (buf[i] == '\r') continue;
		if (buf[i] == '\xff'){
			if (i + 2 < ret){
				if ((buf[i+1] & 0xf0) == 0xf0){
					i+=2;
					continue; // i++
				}
			}
		}
        *d = buf[i];
        d++;
    }
    *d = '\0';
    buf[ret] = '\0';

	sw_add_block(sw, buf);
    if (!sw->ontop && !sw->unread){
        //sw->unread = 1;
        redraw_later();
    }

	if (!sw->chat) return;
	if (sw->lines->len <= 0) return;
	last = (char *)g_ptr_array_index(sw->lines, sw->lines->len - 1);
	if (last == NULL) return;
	if (!regcmpi(last, "^login:")){
		if (cfg->dxc_user && *cfg->dxc_user){
			sw_printf(sw,           "%s\r\n", cfg->dxc_user);
			z_sock_printf(sw->sock, "%s\r\n", cfg->dxc_user);
		}
	}
	if (!regcmpi(last, "^password:")){
		if (cfg->dxc_pass && *cfg->dxc_pass){
			sw_printf(sw,           "%s\r\n", cfg->dxc_pass);
			z_sock_printf(sw->sock, "%s\r\n", cfg->dxc_pass);
		}
        sw->chat = 0;
	} 

}

void sw_dxc_disconnect(struct subwin *sw){
    if (sw->sock < 0) return;

    sw_printf(sw, VTEXT(T_DISCONNECTED));
    zselect_set(zsel, sw->sock, NULL, NULL, NULL, NULL); 
    closesocket(sw->sock);
    dbg("sw_dxc_disconnect -1\n");
    sw->sock = -1;

	sw_printf(sw, VTEXT(T_ENTER_TO_DXC));
}

static void dxc_parse_qrg(char *s, double *out_qrg){
	if (out_qrg != NULL){
		double qrg;
		char *qrgstr = s + 14;
		if (strlen(qrgstr) > 11) qrgstr[10] = '\0';
		qrg = atof(qrgstr);
		if (qrg > 0) *out_qrg = qrg;
	}
}

#define FREE_SW_CX if (c1) {g_free(c1); c1=NULL;} if (c2) {g_free(c2); c2=NULL;}
#define REF_MAX_LEN 12

static int dxc_parse_ref(char *s, char *out_ref){
	int match, ret = 0;
	char *c1 = NULL, *c2 = NULL;
	if (out_ref != NULL){
		c1 = NULL;
		c2 = NULL;
		match = regmatch(s, "[A-Z0-9]{1,4}/[A-Z]{2}-[0-9]{3}", &c1, &c2, NULL);
		if (match == 0 && c1){
			safe_strncpy0(out_ref, c1, REF_MAX_LEN);
			ret = 1;
		}
		FREE_SW_CX;
	}
	return ret;
}

char *sw_dxc_call_under(struct subwin *sw, int x, int y, double *out_qrg, char** out_ref){
	int index;
    int ret, len, i1, i2;
    char *c1 = NULL, *c2 = NULL, *s, call[25], ref[REF_MAX_LEN] = {0};

	//dbg("sw_dxc_call_under(sw, %d, %d)\n", x, y);
	if (out_qrg != NULL) *out_qrg = 0.0;

	if (x >= sw->w){
		struct spotband *sband = get_actual_spotband();
		//dbg("bandmapa\n");

		if (sband){						// bandmap area
			struct spot *spot;
			int idx = sband->current + sw->h / 2 - y - sw->dxc_offset;
			if (idx < 0 || idx >= sband->freq->len) return NULL;
			spot = (struct spot *)g_ptr_array_index(sband->freq, idx);
			x = 0;						// call begins at zero offset
			s = spot->callsign;
			if (out_qrg != NULL) *out_qrg = spot->qrg;
		}else{
			return NULL;
		}
	}else{								// click in spot area
		if (sw->pattern){
			int yy = sw->h - 1;
			int ofs = sw->offset;
			if (sw->pattern)
				index = sw->lines->len;
			else
				index = sw->lines->len - sw->offset;

			for (index--; 1; index--){
				char *x, *line;
				if (index < 0) return NULL;
				line = (char *)g_ptr_array_index(sw->lines, index);
				x = z_strcasestr(line, sw->pattern);
				if (!x) continue;
				if (ofs > 0) { ofs--; continue; }
				if (yy == y) {
					if (ofs > 0){
						ofs--;
						continue;
					}else{
						break;
					}
				}
    			yy--;
				if (!yy) return NULL;
			}
			//x--;
		}else{
			index = sw->lines->len - sw->offset - sw->h + y;
			if (index < 0 || index >= sw->lines->len) return NULL;
		}
		s = (char*)g_ptr_array_index(sw->lines, index);
		if (x < 0 || x >= strlen(s)) return NULL;
	}
    //dbg("line='%s'\n", s);

    s = g_strdup(s);
    z_str_uc(s);

	for (i1 = x; i1 >= 0; i1--){
		if (s[i1] >= 'A' && s[i1] <= 'Z') continue;
		if (s[i1] >= '0' && s[i1] <= '9') continue;
		if (s[i1] == '-') continue;
		if (s[i1] == '/') continue;
		break;
	}
	i1++; // line cannot begin with call

	len = strlen(s);
	for (i2 = x; i2 < len; i2 ++){
		if (s[i2] >= 'A' && s[i2] <= 'Z') continue;
		if (s[i2] >= '0' && s[i2] <= '9') continue;
		if (s[i2] == '-') continue;
		if (s[i2] == '/') continue;
		break;
	}
	if (i2 > i1 && i2 - i1 < 20){
		if (out_ref && dxc_parse_ref(s, ref))
		{
			*out_ref = g_strdup(ref);
		}			
		g_strlcpy(call, s + i1, Z_MIN(i2 - i1 + 1, 20));
		z_strip_from(call, '-'); //rbn
		if (z_can_be_call(call, ZCBC_SLASH | ZCBC_MINUS)){
			dxc_parse_qrg(s, out_qrg);
			g_free(s);
			return g_strdup(call);
		}
	}


    /**** DXC ****/
/*
0     6          17       26           39                             70  
DX de ja8edu:    21285.0  OK2SSD       CQ,CQ DX                       0759Z
but here in upcase
*/
    c1 = NULL;
    c2 = NULL;
    ret = regmatch(s, "^DX\\ DE\\ ....................([0-9A-Z\\/]+)", &c1, &c2, NULL);
    if (ret == 0){
        safe_strncpy0(call, c2, 19);
        goto doit;
    }
    FREE_SW_CX;
    /**/
doit:;    
   /* dbg("ret=%d  c1='%s'  c2='%s'\n", ret, c1, c2);*/
    FREE_SW_CX;
	dxc_parse_qrg(s, out_qrg);
	if (out_ref && dxc_parse_ref(s, ref))
	{
		*out_ref = g_strdup(ref);
	}
    g_free(s);
    if (ret) return NULL;


	//dbg("dxc_call_under is '%s'\n", call);
	return g_strdup(call);
}

// value: -2 hide, -1=hide+qrv 1=highlight
void sw_dxc_toggle_highlight(struct subwin *sw_unused, char *call, int val){
    char t[25];
    gchar *key;
	int *value;
    
    //z_get_raw_call(t, call);
	g_strlcpy(t, call, sizeof(t));

    if (g_hash_table_lookup_extended(gses->hicalls, (gpointer)t, (gpointer*)&key, (gpointer*)&value)){
		if (*value != val){
			*value = val;
			redraw_later();
			return;
		}
        g_hash_table_remove(gses->hicalls, key);
        g_free(key);
        //dbg("call='%s' removed\n", t);
    }else{
		value = g_new(int, 1);
		*value = val;
        g_hash_table_insert(gses->hicalls, g_strdup(t), value);
        if (aband){
            int wkd[32];
			char *wwl = NULL;

			if (val == -1){
				qrv_delete(t, aband->bi);
			}
			if (val >= 0){
			    wwl = find_wwl_by_call(cw, t);
		        if (wwl == NULL) wwl = "";
	            memset(wkd, 0, sizeof(wkd));
				qrv_add(t, wwl, (1<<aband->bi), wkd, "@DXC", time(NULL));  
			}
        }
    	//dbg("call='%s' inserted\n", t);
    }
    
/*    dbg("%d\n", g_hash_table_size(gses->hicalls));*/
    redraw_later();
}


void sw_dxc_select(void *itdata, void *menudata){
	struct subwin *sw = (struct subwin*)menudata;
	sw_dxc_toggle_highlight(sw, sw->callunder, 1);
}

void sw_dxc_hide(void *itdata, void *menudata){
	struct subwin *sw = (struct subwin*)menudata;
	sw_dxc_toggle_highlight(sw, sw->callunder, -1);
}

void sw_dxc_info(void *itdata, void *menudata){
	struct subwin *sw = (struct subwin*)menudata;
	struct qso *q = g_new0(struct qso, 1);
	q->callsign = sw->callunder;
	call_info(q);
	g_free(q);
}

void sw_dxc_use(void *itdata, void *menudata){
	struct subwin *sw = (struct subwin*)menudata;
	if (!aband) return;
	if (aband->readonly) return;
	process_input(aband, sw->callunder, 0);
	if (sw->refunder) process_input(aband, sw->refunder, 0);
	il_add_to_history(INPUTLN(aband), sw->callunder);
	sw_unset_focus();
    il_set_focus(INPUTLN(aband));
#ifdef HAVE_HAMLIB
	trigs_set_qrg(gtrigs, sw->qrg * 1000.0);
#endif
}
/*
0     6          17       26           39                             70  
DX de ja8edu:    21285.0  OK2SSD       CQ,CQ DX                       0759Z
*/
int sw_dxc_line_color(struct subwin *sw, char *line){
	char call[20];//, raw[20];
	char *c;

	if (strlen(line) < 29) return COL_NORM;
	g_strlcpy(call, line + 26, Z_MIN(sizeof(call), 39-26));
	c = call;
	if (*c == ' ') c++;	// spot from rbn is shifted
	if (*c == ' ') c++;
	z_strip_from(c, ' ');
	//z_get_raw_call(raw, c);
	return sw_dxc_color(c);

}

// called with sw == NULL
char *sw_dxc_counterp(struct subwin *sw, char *line, int *color){ // thread unsafe!
	static char call[20];
	char raw[20];

    *color = 0;
	if (strlen(line) < 9) return 0;
	g_strlcpy(call, line + 6, Z_MIN(sizeof(call), 17-6));
	z_strip_from(call, ' ');
	z_strip_from(call, ':');
	z_strip_from(call, '-'); // rbn
	z_get_raw_call(raw, call);
    
	*color = sw_dxc_color(raw);
	return call;
}

int sw_dxc_color(char *call){
	int *pval;

	if (!call) return COL_NORM;

	pval = (int *)g_hash_table_lookup(gses->hicalls, call);
	if (!pval) {
		char raw[25];
		z_get_raw_call(raw, call);
		pval = (int *)g_hash_table_lookup(gses->hicalls, raw);
		if (!pval) return COL_NORM;
	}

    switch (*pval){
		case -2:
        case -1:
            return COL_DARKGREY;
		case 0:
			return COL_NORM;
    }

	if (!aband) return COL_YELLOW;
    //if (get_qso_by_callsign(aband, call) == NULL) return COL_YELLOW;
    if (worked_on_all_rw(call)) 
    	return COL_DARKYELLOW;
    else
        return COL_YELLOW;
}

#ifdef HAVE_HAMLIB
void dxc_qrg_changed(freq_t qrg){
	struct spotband *sband;
	struct spot *spot, *sp;
	double min, m;
	int cur;

	sband = get_actual_spotband();
	if (!sband) return;
	if (!sband->freq->len) return;
		
	spot = (struct spot *)z_ptr_array_index(sband->freq, sband->current);
	min = Z_ABS(spot->qrg * 1000 - qrg);
	if (min == 0.0) return;		// already on correc frequency. Maybe different call which allows Ctrl+Arrows
		
	for (cur = sband->current; cur < sband->freq->len; cur++){
		sp = (struct spot *)z_ptr_array_index(sband->freq, cur);
		m = Z_ABS(sp->qrg * 1000 - qrg);
		if (m > min) break;
		sband->current = cur;
		min = m;
	}
	for (cur = sband->current; cur >= 0; cur--){
		sp = (struct spot *)z_ptr_array_index(sband->freq, cur);
		m = Z_ABS(sp->qrg * 1000 - qrg);
		if (m > min) break;
		sband->current = cur;
		min = m;
	}
}
#endif

void sw_dxc_spot_from_net(struct subwin *sw, char *str){
    int i;

    if (sw->sock != -1) return;	 // have own connection
    if (strlen(str) >= 2 && str[0] == 'D' && str[1] == 'X'){ 
        str[0] = 'd'; 
        str[1] = 'x'; 
    }
    
    for (i = sw->lines->len - 1; i > sw->lines->len - term->y; i--){
        if (i < 0) break;
        if (strcasecmp(str, (char*)g_ptr_array_index(sw->lines, i)) == 0) return;
    }
    sw->dxc_from_net = 1;
    sw_printf(sw, "%s\n", str);
    sw->dxc_from_net = 0;
}

void dxc_open_connection(void *arg, void *arg2){
	struct subwin *sw = (struct subwin *)arg;

    sw_printf(sw, VTEXT(T_RESOLVING), cfg->dxc_host);
    zasyncdns_getaddrinfo(sw->adns, zsel, sw_dxc_addrinfo, cfg->dxc_host, AF_INET, sw);
}

void dxc_export_text(void *itdata, void *menudata)
{
	struct subwin *sw = (struct subwin *)itdata;
	sw_export_lines(sw, "dxc");
}

int dxc_new_mult(const char *callsign){
   if (!ctest) return 0;
   

   if (ctest->excused && excdb != NULL && aband != NULL && aband->stats != NULL && aband->stats->excs != NULL){
		char *exc = find_exc_by_call(excdb, callsign);
		if (exc && *exc){
			struct cntpts *c = (struct cntpts*)g_hash_table_lookup(aband->stats->excs, exc);
			if (c == NULL) return 1;
		}
   }

   return 0;
}
