/*
    Tucnak - VHF contest log
    Copyright (C) 2002-2023  Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/

#include "header.h"         
#include "bfu.h"
#include "fifo.h"
#include "kbd.h"
#include "main.h"
#include "map.h"
#include "net.h"
#include "rc.h"
#include "rotar.h"
#include "sdev.h"
#include "tsdl.h"
#include "session.h"
#include "subwin.h"
#include "zstring.h"
#include "oct8tor.h"


struct rotar *rota,*rotb;
//struct hdkeyb *hdkeyb;

GPtrArray *rotars;
MUTEX_DEFINE(rotars);

struct rotar *rotar = NULL;
struct oct8tor *goct8tor;


#ifdef HAVE_HAMLIB
static int rot_print_model_list(const struct rot_caps *caps, void *data)
{
    log_addf("%-6d %-14s  %-20s  %s\n", caps->rot_model, caps->mfg_name, caps->model_name, caps->version);
    return 1;  /* !=0, we want them all ! */
}
#endif


struct rotar *init_rotar(struct config_rotar *crot, int rotchar){
    struct rotar *rot;
    if (!crot) return NULL;
    if (!crot->rot_type) return NULL;

	rot = g_new0(struct rotar, 1);
	rot->poll_ms = crot->rot_poll_ms;
	rot->last_req = ztimeout_init(rot->poll_ms);
    rot->main_thread = g_thread_self();
#ifdef HAVE_HAMLIB
	rot->rot_speed = crot->rot_speed;
#endif
	switch (crot->rot_type){
        case ROT_OK1ZIA_TTYS:
            rot->sdev=sd_open_ttys(crot->rot_saddr, crot->rot_filename, crot->rot_timeout_ms);
            dbg("init_rotar rot=%p  saddr=%d filename='%s' timeout=%d\n", rot->sdev, crot->rot_saddr, crot->rot_filename, crot->rot_timeout_ms);
            if (!rot->sdev){
                g_free(rot);
                return NULL;
            }
            rot->sdev->sdev_main = rotar_main;
            rot->sdev->arg = rot;
            break;
        case ROT_OK1ZIA_FTDI:
            rot->sdev=sd_open_ftdi(crot->rot_saddr, crot->rot_vid, crot->rot_pid, crot->rot_serial, crot->rot_timeout_ms);
            dbg("init_rotar rot=%p  saddr=%d %04x:%04x timeout=%d   sdev=%p\n", rot->sdev, crot->rot_saddr, crot->rot_vid, crot->rot_pid, crot->rot_timeout_ms, rot->sdev);
            if (!rot->sdev){
                g_free(rot);
                return NULL;
            }
            rot->sdev->sdev_main = rotar_main;
            rot->sdev->arg = rot;
            break;
        case ROT_REMOTE:
            dbg("init_rotar rot=%p  saddr=%d hostname='%s' port='%d' timeout=%d\n", rot->sdev, crot->rot_saddr, crot->rot_hostname, crot->rot_port, crot->rot_timeout_ms);
            break;
#ifdef HAVE_HAMLIB
        case ROT_HAMLIB:
            if (crot->rot_model != 1) {
                if (strlen(crot->rot_filename) == 0) {
                    log_addf("Can't init rotator, empty file name");
                    g_free(rot);
                    return NULL;
                }
            }
            rot->rot = rot_init(crot->rot_model);
			if (!rot->rot){
				log_addf(VTEXT(T_UNKNOWN_ROTNR_OR_INIT_ERROR), crot->rot_model);
				g_free(rot);

				log_addf(VTEXT(T_REGISTRATED_ROTATORS));
				rot_load_all_backends();
				rot_list_foreach(rot_print_model_list, NULL);

				return NULL;
			}
			TRROT(0, "rot_init(model %d) OK", rot->rot->caps->rot_model);

			if (crot->rot_model > 0){
#ifdef Z_MSC_MINGW_CYGWIN
				if (strchr(crot->rot_filename, '\\') == NULL && z_strcasestr(crot->rot_filename, "COM") != NULL){
					char *c = g_strdup_printf("\\\\.\\%s", crot->rot_filename);
					safe_strncpy(rot->rot->state.rotport.pathname, c, HAMLIB_FILPATHLEN - 1);
					g_free(c);
				}
				else
#endif
				{
					safe_strncpy(rot->rot->state.rotport.pathname, crot->rot_filename, HAMLIB_FILPATHLEN - 1);
				}
				if (rot->rot_speed > 0) rot->rot->state.rotport.parm.serial.rate = rot->rot_speed;

				TRROT(0, "rotar %c pathname='%s' speed=%d", rotchar, rot->rot->state.rotport.pathname, rot->rot->state.rotport.parm.serial.rate);
			}

            rot->hl_azim = -1;
			rot->hl_elev = -90;
			rot->hl_lastelev = 0;
            rot->hl_model = crot->rot_model;
			rot->hl_thread = g_thread_try_new("hamlib rotator", rot_hamlib_main, (gpointer)rot, NULL); 
			if (!rot->hl_thread) {
				log_addf(VTEXT(T_CANT_CREATE_HL_ROT_THR));
			}
            break;
#endif         
#ifdef OCT8TOR
		case ROT_OCT8TOR:
			rot->oct_azim = -1;
			rot->oct_elev = -90;
			rot->oct_hidden = true;
			break;
#endif
		default:
            log_addf(VTEXT(T_UNSUPPORTED_ROTATOR), crot->rot_type);
            g_free(rot);
            return NULL;
            break;

    }

    rot->desc=g_strdup(crot->rot_desc);
	rot->beamwidth=crot->rot_beamwidth;
	rot->offset = crot->rot_offset;
	rot->color=0xffffffff;  /*replaced in rot_update_colors*/
/*	rot->qtf=260+40*cfg->nr;*/
//    rot->timer_id=-1;
    rot->rotchar=rotchar;
	rot->rotstr[0]=rotchar;
	rot->rotstr[1]='\0';
    rot->type = crot->rot_type;
    rot->netid = g_strdup_printf("%s:%d", crot->rot_hostname, crot->rot_port);
    rot->rem_rotstr = g_strdup(crot->rot_rem_rotstr);

/*  if (rot->sdev){
        rot->sdev->sconn->sprotocol = SPROT_ROTAR;
        g_snprintf(s, sizeof(s), "%d", SPROT_ROTAR);
        zwrite(rot->sdev->sconn->pipe_write, 
                zconcatesc("ADD", rot->rotstr, rot->sdev->saddr_str, s, NULL));
    }*/
    return rot;
}

void free_rotar(struct rotar *rot){
#ifdef HAVE_HAMLIB
	if (rot->hl_thread){
		progress(VTEXT(T_WAIT_HL_ROT_THREAD));

        rot->hl_thread_break = 1;
        dbg("join hamlib rotar...\n");
        g_thread_join(rot->hl_thread);
        dbg("done\n");
        rot->hl_thread=0;
    }

#endif
#ifdef OCT8TOR
	// nothing allocated
#endif
	g_free(rot->desc);
	g_free(rot->netid);
	g_free(rot->rem_rotstr);
	free_sd(rot->sdev);
	g_free(rot);

}

int init_rotars(){
    struct rotar *rot;
    struct config_rotar *cfgrot;
    int i;
    char rotchar='A';

	progress(VTEXT(T_INIT_ROTARS2));

    MUTEX_INIT(rotars);

	rotars = g_ptr_array_new();
#ifdef OCT8TOR	
	goct8tor = init_oct8tor();
	if (goct8tor != NULL){
		for (int i = 0; i < cfg->oct_alloc; i++){
			struct config_rotar octcfg;
			memset(&octcfg, 0, sizeof(octcfg));
			octcfg.rot_type = ROT_OCT8TOR;
			octcfg.rot_poll_ms = 1000;
			octcfg.rot_desc = g_strdup_printf("Oct%d", i + 1);
			octcfg.rot_beamwidth = 20;
			octcfg.rot_offset = 0;
			octcfg.rot_hostname = cfg->oct_tcp_host;
			octcfg.rot_port = cfg->oct_remote_tcp_port;
			octcfg.rot_rem_rotstr = g_strdup_printf("%c", 'A' + i);
			rot = init_rotar(&octcfg, rotchar++);
			if (rot){
				rot->oct_nr = i + 1;
				MUTEX_LOCK(rotars);
				g_ptr_array_add(rotars, rot);
				MUTEX_UNLOCK(rotars);
			}
            g_free(octcfg.rot_desc);
            g_free(octcfg.rot_rem_rotstr);
		}
	}
#endif

    for (i=0;i<cfg->crotars->len;i++){
		cfgrot = (struct config_rotar*)g_ptr_array_index(cfg->crotars, i);
		if (!cfgrot) continue;
		rot = init_rotar(cfgrot, rotchar++);
		if (!rot) continue;
		MUTEX_LOCK(rotars);
        g_ptr_array_add(rotars, rot); 
        MUTEX_UNLOCK(rotars);
	}


#ifdef Z_HAVE_SDL
	rot_update_colors();
#endif
	return 0;
}

int free_rotars(void){
    int i;
    struct rotar *rot;

	progress(VTEXT(T_INIT_ROTARS2));		

#ifdef OCT8TOR
	free_oct8tor(goct8tor);
#endif

    MUTEX_LOCK(rotars);
	rotar = NULL;
	for (i = 0; i<rotars->len; i++){
		rot = (struct rotar *) g_ptr_array_index(rotars, i);
        free_rotar(rot);
	}
	g_ptr_array_free(rotars,1);
    rotars=NULL;
	MUTEX_UNLOCK(rotars);
	return 0;
}

#ifdef Z_HAVE_SDL
int rot_update_colors(){
	int i;
	struct rotar *rot;
	
    if (!sdl || !rotars) return -1;
    MUTEX_LOCK(rotars);
    for (i=0;i<rotars->len;i++){
        rot = (struct rotar *)g_ptr_array_index(rotars, i);
	
		switch(i%4){
			case 0:
				rot->color=z_makecol(200,0,0);
                rot->termcolor=COL_RED;
				break;
			case 1:
				rot->color=z_makecol(200,0,200);
                rot->termcolor=COL_MAGENTA;
				break;
			case 2:
				rot->color=z_makecol(0,200,0);
                rot->termcolor=COL_GREEN;
				break;
			case 3:
				rot->color=z_makecol(0,200,200);
                rot->termcolor=COL_CYAN;
				break;
		}
	}
    MUTEX_UNLOCK(rotars);
	return 0;
}
#endif

int rot_seek(struct rotar *rot, int uhel, int elev){
    char *c;
    struct sconn_job *job;

    if (!rot) return 0;

	int uo = rot_normalize(uhel - rot->offset);
    
    TRROT(0, "rot_seek  az=%d  el=%d  (rottype %d)", uo, elev, rot->type);

    switch (rot->type){
        case ROT_REMOTE:
            if (g_thread_self() == rot->main_thread) {
                c = g_strdup_printf("RS %s;%s;%d;%d\n",
                    rot->netid, rot->rem_rotstr, uo, elev);
                rel_write_all(c); // not thread safe
                g_free(c);
            }else {
                c = g_strdup_printf("ROT;%s;SEEK;%d;%d\n", rot->rotstr, uhel, elev);
                zselect_msg_send(zsel, c);
                g_free(c);
            }
            break;
#ifdef HAVE_HAMLIB            
        case ROT_HAMLIB:
			rot->hl_azim = uo;
			rot->hl_elev = elev;
            break;
#endif
#ifdef OCT8TOR
		case ROT_OCT8TOR:
			//if (!aband) break;
			MUTEX_LOCK(rotars);
			rot->oct_azim = uo;
			rot->oct_elev = elev;
			MUTEX_UNLOCK(rotars);
			break;
#endif
        default:
            job = g_new0(struct sconn_job, 1);
            job->cmd = SCONN_ROT_AZIM;     
            job->sdev = rot->sdev;
			job->azim = uo;
			job->elev = elev;
            sconn_job_add(rot->sdev->sconn, job);
            break;
    }
    return 0;
}

void rotars_stop(){
	int i;
	struct rotar *rot = NULL;

	if (!rotars) return;
	MUTEX_LOCK(rotars);
	for (i = 0; i<rotars->len; i++){
		rot = (struct rotar *)g_ptr_array_index(rotars, i);
		rotar_stop(rot);
	}
	MUTEX_UNLOCK(rotars);
}

void rotar_stop(struct rotar *rot){
	char *c;
	//struct sconn_job *job;

	switch (rot->type){
		case ROT_REMOTE:
			c = g_strdup_printf("RP %s;%s\n", rot->netid, rot->rem_rotstr);
			rel_write_all(c);
			g_free(c);

			break;
#ifdef HAVE_HAMLIB            
		case ROT_HAMLIB:
			rot->hl_stop = 1;
			break;
#endif
#ifdef OCT8TOR
		case ROT_OCT8TOR:
			if (!aband) break;
			MUTEX_LOCK(rotars);
			rot->oct_stop = 1;
			MUTEX_UNLOCK(rotars);
			break;
#endif
		default:
			//job = g_new0(struct sconn_job, 1);  no support
			//job->cmd = SCONN_ROT_AZIM;
			//job->sdev = rot->sdev;
			//sconn_job_add(rot->sdev->sconn, job);
			break;
	}
}


struct config_rotar *get_config_rotar_by_number(GPtrArray *crots, int nr){
    struct config_rotar *crot;
    int i;

    for (i=0; i<cfg->crotars->len; i++){
        crot = (struct config_rotar *)g_ptr_array_index(crots, i);
        if (crot->nr==nr) return crot;
    }
    return NULL;   
}

struct rotar *get_rotar(int nr){
    struct rotar *rot;
    MUTEX_LOCK(rotars);

    if (!rotars || nr >= rotars->len) {
        MUTEX_UNLOCK(rotars);
        return NULL;
    }

    rot = (struct rotar*) g_ptr_array_index(rotars, nr);
    MUTEX_UNLOCK(rotars);
    return rot;
}

struct rotar *get_rotar_by_rotstr(char *rotstr){
    int i;
    struct rotar *rot = NULL;

    if (!rotars) return NULL;
    MUTEX_LOCK(rotars);
    for (i=0;i<rotars->len;i++){
        rot=(struct rotar *)g_ptr_array_index(rotars, i);
        if (rot->rotchar!=rotstr[0]) continue;
        break;
    }
    MUTEX_UNLOCK(rotars);
    return rot;
}

struct rotar *get_rotar_by_netid_rotstr(char *netid, char *rotstr){
    int i;
    struct rotar *ret = NULL;

    if (!rotars) return NULL;
    MUTEX_LOCK(rotars);
    for (i=0;i<rotars->len;i++){
        struct rotar *rot=(struct rotar *)g_ptr_array_index(rotars, i);
        //dbg("  rot->type=%d rotchar=%s netid=%s\n", rot->type, rot->rem_rotstr, rot->netid);
        if (rot->type != ROT_REMOTE) continue;
        if (strcmp(rot->netid, netid)!=0) continue;
        if (strcmp(rot->rem_rotstr, rotstr)!=0) continue;
		ret = rot;
        break;
    }
    MUTEX_UNLOCK(rotars);
    return ret;
}


void rotar_read_handler(int n, char **items){
    struct zstring *zs2;
    char *cmd, *rotstr, *c;
	struct rotar *rot;
    
//    dbg("rotar_read_handler('%s')  gses=%p\n", line, gses);

	rotstr = items[1];

	rot = get_rotar_by_rotstr(rotstr);
	if (!rot){
		log_addf(VTEXT(T_UNKNOWN_ROTAR), rotstr); 
		//dbg("Unknown rotar '%s'\n", rotstr); 
		goto x;
	}
	cmd = items[2];
	
//    dbg("rotar_read_handler   rcvd: '%s'  cmd='%s' rotstr='%s'\n", line, cmd, rotstr);
    if (strcmp(cmd, "!")==0){  /* error */
        log_addf(VTEXT(T_ROTAR_ERROR_S), items[3]);
    }
    if (strcasecmp(cmd, "QTF")==0){
		char *qtfstr = g_strdup(items[3]);
		z_trim_end(qtfstr);
		if (strlen(qtfstr) > 0){
			char *qtf = qtfstr;
			rot->qtf = rot_normalize(atoi(qtf) + rot->offset);
		}
		g_free(qtfstr);

		char *elevstr = g_strdup(items[4]);
		z_trim_end(elevstr);
		if (strlen(elevstr) > 0){
			char *elev = elevstr;
			rot->elev = atoi(elev);
		}
		g_free(elevstr);
        
        zs2 = zconcatesc(gnet->myid, rotstr, items[3], items[4], NULL);
        c = g_strconcat("RU ", zs2->str, "\n", NULL);
        rel_write_all(c);
        g_free(c);
        zfree(zs2);

#ifdef Z_HAVE_SDL
        //if (!gses->redraw_timer_id)
        //    gses->redraw_timer_id = zselect_timer_new(zsel, DELAY_AFTER_UPDATE_ROTAR, timer_redraw, NULL);
        if (gses && gses->ontop->type == SWT_MAP){
            if (!gses->update_rotar_timer_id)
                gses->update_rotar_timer_id = zselect_timer_new(zsel, 2*DELAY_AFTER_UPDATE_ROTAR, timer_update_rotar, NULL);
        }
#endif
        redraw_later();
        goto x;
    }
    if (strcasecmp(cmd, "SEEK") == 0) {
        int qtf = atoi(items[3]);
        int ele = atoi(items[4]);
        rot_seek(rot, qtf, ele);
    }

x:;	
    redraw_later();
}

void timer_update_rotar(void *arg){
#ifdef Z_HAVE_SDL
    if (gses && gses->ontop->type == SWT_MAP){
        sw_map_update_rotar(gses->ontop);
    }
    gses->update_rotar_timer_id = 0;
#endif
}

// called from sconn thread
int rotar_main(struct sdev *sdev){
    struct rotar *rot = (struct rotar *)sdev->arg;
	char data[256];
	int len, ret, qtf, elev;
  //  static int xx = 0;

    //dbg("rotar_main rot=%p\n", rot);
    if (!rot) return -1;
    
	if (!ztimeout_occured(rot->last_req)) return 113;
	rot->last_req = ztimeout_init(rot->poll_ms);
    
    len=0;

//    dbg("\nsd_prot\n");
    ret = sd_prot(rot->sdev->sconn, rot->sdev->saddr, 64, data, &len, rot->sdev->timeout_ms);
    //dbg("sd_prot ret=%d\n", ret);
    if (ret != 0) return ret;

	qtf = (unsigned char)data[0] + 256 * (unsigned char)data[1];
	elev = (int16_t)((unsigned char)data[2] + 256 * (unsigned char)data[3]);
  //  qtf = (xx++ % 360);//FIXME
	trace(cfg->trace_sdev, "qtf=%d old=%d  elev=%d old=%d\n", qtf, rot->oldqtf, elev, rot->oldelev);
    if (qtf == rot->oldqtf && elev == rot->oldelev) return 0;
    
    rot->oldqtf = qtf;
	rot->oldelev = elev;
    //g_snprintf(s, sizeof(s), "%d", qtf);
    //ret = zwrite(tpipe->threadpipe_write, zconcatesc("ROT", rot->rotstr, "QTF", s, NULL));
	zselect_msg_send(zsel, "%s;%s;%s;%d;%d", "ROT", rot->rotstr, "QTF", qtf, elev);
//    dbg("zwrite ret=%d\n", ret);
    return 0;
}


void rotar_remote_update(char *c){
    char *netid, *rotstr, *qtf, *elev;
    struct zstring *zs;
    struct rotar *rot;

    //dbg("rotar_remote_update('%s')\n", c);
    zs = zstrdup(c);

    netid = ztokenize(zs, 1);
    if (!netid) goto x;
    rotstr = ztokenize(zs, 0);
    if (!rotstr) goto x;
    qtf = ztokenize(zs, 0);
    if (!qtf) goto x;
    elev = ztokenize(zs, 0);
    if (!elev) goto x;

    rot = get_rotar_by_netid_rotstr(netid, rotstr);
    //dbg("rotar_remote_update id='%s' rotstr='%s'   rot=%p\n", netid, rotstr, rot);

    if (!rot) goto x;

	rot->qtf = rot_normalize(atoi(qtf) + rot->offset);
    rot->elev = atoi(elev);
	redraw_later();
#ifdef Z_HAVE_SDL
	if (gses->ontop->type == SWT_MAP){
		gses->ontop->gdirty = 1;
	}
#endif

x:;
    zfree(zs);
    
}


/****************************** rotar dialog ***************************/

static char rotar_qtf_str[EQSO_LEN], rotar_elev_str[EQSO_LEN];
static char rotar_desc1[MAX_STR_LEN], rotar_desc2[MAX_STR_LEN];

void refresh_rotar(void *xxx){
	if (strlen(rotar_qtf_str) <= 0) return;
	int elev = -90;
	if (strlen(rotar_elev_str) > 0) elev = atoi(rotar_elev_str);
	ac_track(gacs, NULL, -10);
	rot_seek(rotar, atoi(rotar_qtf_str), elev);
}

char *rotar_msg[] = {
    CTEXT(T_AZIMUTH),
    CTEXT(T_ELEVATION),
};

void rotar_fn(struct dialog_data *dlg)
{
    struct terminal *term = dlg->win->term;
    int max = 0, min = 0;
    int w, rw;
    int y = -1;

    max_text_width(term, rotar_desc1, &max);
    min_text_width(term, rotar_desc1, &min);
    max_text_width(term, rotar_desc2, &max);
    min_text_width(term, rotar_desc2, &min);
    max_group_width(term, rotar_msg + 0, dlg->items + 0, 1, &max);
    min_group_width(term, rotar_msg + 0, dlg->items + 0, 1, &min);
    max_group_width(term, rotar_msg + 1, dlg->items + 1, 1, &max);
    min_group_width(term, rotar_msg + 1, dlg->items + 1, 1, &min);
    
    max_buttons_width(term, dlg->items + 2, 2, &max);
    min_buttons_width(term, dlg->items + 2, 2, &min);
    
    w = dlg->win->term->x * 9 / 10 - 2 * DIALOG_LB;
    if (w > max) w = max;
    if (w < min) w = min;
    if (w > dlg->win->term->x - 2 * DIALOG_LB - 8 ) w = dlg->win->term->x - 2 * DIALOG_LB - 8;
    if (w < 1) w = 1;
    
    rw = 0;
    y ++;
    dlg_format_text (NULL, term, rotar_desc1, dlg->x+6, &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
    dlg_format_text (NULL, term, rotar_desc2, dlg->x+6, &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
    dlg_format_group(NULL, term, rotar_msg + 0, dlg->items + 0, 1, 0, &y, w, &rw);
    dlg_format_group(NULL, term, rotar_msg + 1, dlg->items + 1, 1, 0, &y, w, &rw);
    y++;
    dlg_format_buttons(NULL, term, dlg->items + 2, 2, 0, &y, w, &rw, AL_LEFT);
    
    
    w = rw;
    dlg->xw = w + 2 * DIALOG_LB;
    dlg->yw = y + 2 * DIALOG_TB;

    
    center_dlg(dlg);
    draw_dlg(dlg);
    y = dlg->y + DIALOG_TB;
    y++;
    dlg_format_text (term, term, rotar_desc1, dlg->x+6, &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
    dlg_format_text (term, term, rotar_desc2, dlg->x+6, &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
    dlg_format_group(term, term, rotar_msg + 0, dlg->items + 0, 1, dlg->x + DIALOG_LB, &y, w, AL_LEFT);
    dlg_format_group(term, term, rotar_msg + 1, dlg->items + 1, 1, dlg->x + DIALOG_LB, &y, w, AL_LEFT);
    y++;
    dlg_format_buttons(term, term, dlg->items + 2, 2, dlg->x + DIALOG_LB, &y, w, NULL, AL_LEFT);
    
}   

void rotar_set_desc(void){
	if (!rotar) return;

    switch(rotar->type){
#ifdef HAVE_HAMLIB
        case ROT_HAMLIB:
            g_snprintf(rotar_desc1, MAX_STR_LEN, "%s %c (hamlib %u)", VTEXT(T_ROTAR), rotar->rotchar, rotar->hl_model);
            break;
#endif            
        case ROT_REMOTE:
            g_snprintf(rotar_desc1, MAX_STR_LEN, "%s %c (%s %s)", VTEXT(T_ROTAR), rotar->rotchar, rotar->netid, rotar->rem_rotstr);
            break;
#ifdef OCT8TOR
		case ROT_OCT8TOR:
			g_snprintf(rotar_desc1, MAX_STR_LEN, "%s %c (oct8tor %s)", VTEXT(T_ROTAR), rotar->rotchar, rotar->netid);
			break;
#endif
        default:
            g_snprintf(rotar_desc1, MAX_STR_LEN, "%s %c (%u)", VTEXT(T_ROTAR), rotar->rotchar, (unsigned char)rotar->sdev->saddr);
            break;
    }
    g_snprintf(rotar_desc2, MAX_STR_LEN, "%s", rotar->desc);
}

int rotar_func(struct dialog_data *data, struct event *ev){
    char rotchar;
      /*  dbg("mouse ev=%d x=%d y=%d b=%d\n", ev->ev, ev->x, ev->y, ev->b); */
    
    switch(ev->ev){
        case EV_KBD:
            if (ev->y & KBD_ALT) break;
            if (ev->y & KBD_CTRL) break;
            rotchar=z_char_uc(ev->x);
            if (rotchar<'A' || rotchar>'Z') {
                return -1;
            }
            if (rotchar<'A' || rotchar-'A'>=rotars->len) {
                return EVENT_PROCESSED;
            }
            /*dbg("rotar %c\n", rotchar);*/

            rotar=get_rotar(rotchar-'A');
            rotar_set_desc();
            resize_terminal(NULL);
            return EVENT_PROCESSED;
        
    }
    return -1; /* !EVENT_PROCESSED */
}
        
        

void menu_rotar(void *arg){

    struct dialog *d;
    int i;

    if (!rotar) rotar=get_rotar(0);
    if (!rotar) return;
    
    rotar_set_desc();
    /*g_snprintf(rotar_qtf_str,  EQSO_LEN, "%d", rotar->qtf);
    g_snprintf(rotar_elev_str, EQSO_LEN, "%d", rotar->elev);*/
    strcpy(rotar_qtf_str, "");
    strcpy(rotar_elev_str, "");

    
    if (!(d = (struct dialog *) g_malloc(sizeof(struct dialog) + 55 * sizeof(struct dialog_item)))) return;
    memset(d, 0, sizeof(struct dialog) + 55 * sizeof(struct dialog_item));
    d->title = VTEXT(T_ROTAR);
    d->fn = rotar_fn;
    d->refresh = (void (*)(void *))refresh_rotar;
    d->refresh_data = (void *)rotar;
    d->handle_event = rotar_func;
    
    d->items[i=0].type = D_FIELD; /* 0 */
    d->items[i].dlen = EQSO_LEN;
    d->items[i].data = rotar_qtf_str;
    
    d->items[++i].type = D_FIELD; 
    d->items[i].dlen = EQSO_LEN;
    d->items[i].data = rotar_elev_str;
    
   

    d->items[++i].type = D_BUTTON; /* 2 */
    d->items[i].gid = B_ENTER;
    d->items[i].fn = ok_dialog;
    d->items[i].text = VTEXT(T_OK);
    
    d->items[++i].type = D_BUTTON;
    d->items[i].gid = B_ESC;
    d->items[i].fn = cancel_dialog;
    d->items[i].text = VTEXT(T_CANCEL);
    
    
    d->items[++i].type = D_END;
    do_dialog(d, getml(d, NULL));
                               
}

#ifdef HAVE_HAMLIB
gpointer rot_hamlib_main(gpointer xxx){
    int ret;
    struct rotar *rot = (struct rotar*)xxx;
    azimuth_t azimuth;
    elevation_t elevation;
    int qtf, elev;
	int tmo;

    dbg("rot_hl_main\n");
	zg_thread_set_name("Tucnak hamlib rotator");


    ret = rot_open(rot->rot);
    if (ret != RIG_OK){
		TRROT(1, "rot_open(model %d) failed, code %d", rot->rot->caps->rot_model, ret);
        return NULL;
    }
	TRROT(1, "rot_open(model %d) OK", rot->rot->caps->rot_model);

    while(!rot->hl_thread_break){
        if (rot->hl_give_me_chance) {
            usleep(10000);
            continue;
        }
        
        if (rot->hl_azim >= 0){
            azimuth_t az = rot->hl_azim;
            elevation_t el = rot->hl_elev;
			if (el <= -90) el = rot->hl_lastelev;

            ret = rot_set_position(rot->rot, az, el);
			TRROT(1, "rot_set_position(az %3.1f, el %3.1f) = %d", (double)az, (double)el, ret);
			rot->hl_azim = -1;
        }

		if (rot->hl_stop){
			ret = rot_stop(rot->rot);
			TRROT(1, "rot_stop() = %d", ret);
			rot->hl_stop = 0;
		}

		ret = rot_get_position(rot->rot, &azimuth, &elevation);
		if (ret == RIG_OK){
			TRROT(1, "rot_get_position() = az %3.1f, el %3.1f", (double)azimuth, (double)elevation);
			rot->hl_lastelev = elevation;
			qtf = (int)roundf((float)azimuth);
			elev = (int)roundf((float)elevation);
			if (qtf != rot->oldqtf || elev != rot->elev){
				rot->oldqtf = qtf;
				rot->oldelev = elev;
				// g_snprintf(s, sizeof(s), "%d", qtf);
				zselect_msg_send(zsel, "ROT;%s;QTF;%d;%d", rot->rotstr, qtf, elev);
				// ret = zwrite(tpipe->threadpipe_write, zconcatesc("ROT", rot->rotstr, "QTF", s, NULL));
				//    dbg("zwrite ret=%d\n", ret);
			}
		}else{
			TRROT(1, "rot_get_position() failed, ret=%d", ret);
        }



		if (rot->poll_ms > 0){
			for (tmo = ztimeout_init(rot->poll_ms); !ztimeout_occured(tmo); )
			{
				if (rot->hl_azim >= 0) break;
				usleep(10000);
			}
		}
    }
    TRROT(1, "rot_hl_main exiting\n");
    rot_close(rot->rot);

    return NULL;

}
#endif

int rot_beamwidth(){
	int beamwidth = 20;
	struct config_rotar *rot;

	if (!cfg->crotars->len) return beamwidth;
	rot = (struct config_rotar *)g_ptr_array_index(cfg->crotars, 0);
	return rot->rot_beamwidth;
}

int rot_normalize(int az){
	az = az % 360;
	if (az < 0) az += 360;
	return az;
}

#ifdef OCT8TOR
char rot_get_rotchar_by_oct_nr(int oct_nr){

	MUTEX_LOCK(rotars);
	for (int i = 0; i<rotars->len; i++){
		struct rotar *rot = (struct rotar *) g_ptr_array_index(rotars, i);
		if (rot->oct_nr == oct_nr){
			MUTEX_UNLOCK(rotars);
			return rot->rotchar;
		}
	}
	MUTEX_UNLOCK(rotars);
	return '\0';
}

// can fail before "rotars" reply
char rot_get_rotchar_by_ch(const char *ch){

	MUTEX_LOCK(rotars);
	for (int i = 0; i<rotars->len; i++){
		struct rotar *rot = (struct rotar *) g_ptr_array_index(rotars, i);
		if (rot->oct_sys == ch[0]){
			MUTEX_UNLOCK(rotars);
			return rot->rotchar;
		}
	}
	MUTEX_UNLOCK(rotars);
	return '\0';
}

char rot_get_rotchar_by_sys(const char *sys){

	//MUTEX_LOCK(rotars); called from locked code
	for (int i = 0; i<rotars->len; i++){
		struct rotar *rot = (struct rotar *) g_ptr_array_index(rotars, i);
		if (rot->oct_sys == sys[0]){
			MUTEX_UNLOCK(rotars);
			return rot->rotchar;
		}
	}
	//MUTEX_UNLOCK(rotars);
	return '\0';
}
#endif