/*
    oct8tor.c - Oct8tor support
    Copyright (C) 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"
#ifdef OCT8TOR
#include "oct8tor.h"
#include "fifo.h"
#include "main.h"
#include "rc.h"
#include "rotar.h"
#include "tsdl.h"
#include "terminal.h"
#include "qsodb.h"

#define OCT_KEEPALIVE 30000

struct oct8tor *init_oct8tor(){
	if (!cfg->oct_enable) return NULL;
	struct zserial* zser = NULL;

	switch (cfg->oct_comm){
		case OCT_COMM_TCP:
			zser = zserial_init_tcp(cfg->oct_tcp_host, cfg->oct_remote_tcp_port);
			break;
		case OCT_COMM_SERIAL:
			zser = zserial_init_serial(cfg->oct_serial_port);
			zserial_set_line(zser, 115200, 8, 'N', 1);
			break;
		default:
			log_addf(TRANSLATE("Unknown Oct8tor communication type"));
			return NULL;
	}

	struct oct8tor* oct = g_new0(struct oct8tor, 1);
	oct->zser = zser;

	oct->rdbuf = zbinbuf_init();
	oct->wrbuf = zbinbuf_init();

	oct->poll_ms = 500;
	oct->onconnect = true;

	oct->thread = g_thread_try_new("oct8tor", oct8tor_main, (gpointer)oct, NULL);
	if (!oct->thread) {
		log_addf(TRANSLATE("Can't create oct8tor thread"));//VTEXT(T_CANT_CREATE_HL_ROT_THR));
	}

	if (aband != NULL){
		struct config_band *cb = get_config_band_by_bandchar(aband->bandchar);
		oct8tor_send_band(oct, cb->adifband);
	}

    return oct;
}

void free_oct8tor(struct oct8tor *oct){
    if (!oct) return;

	if (oct->thread){
		progress("Waiting for oct8tor thread"); //VTEXT(T_WAIT_HL_ROT_THREAD));
		oct->thread_break = 1;
		dbg("join oct8tor...\n");
		g_thread_join(oct->thread);
		dbg("done\n");
		oct->thread_break = 0;
	}

	
	zbinbuf_free(oct->rdbuf);
	zbinbuf_free(oct->wrbuf);

	zserial_free(oct->zser);
	g_free(oct->adifband);

    g_free(oct);
}
				


gpointer oct8tor_main(gpointer xxx){
	struct oct8tor *oct = (struct oct8tor *)xxx;
	dbg("oct8tor_main\n");
	zg_thread_set_name("Tucnak oct8tor");

	int ret = zserial_open(oct->zser);
	oct8tor_handle_error(oct, ret != 0);

	char *adifband = NULL;
	oct->keepalive = ztimeout_init(OCT_KEEPALIVE);
	bool sendAdifBand = false;

	while (!oct->thread_break){
		oct8tor_read(oct);

		if (!zserial_is_opened(oct->zser)) {
			sleep(1);
			continue;
		}

		int nr = 0;
		int azim = -1;
		int elev = -90;



		MUTEX_LOCK(rotars);
		if (oct->onconnect) {
			sendAdifBand = true;
			oct->onconnect = false;
		}

		if (oct->adifband != NULL) {
			adifband = oct->adifband;
			oct->adifband = NULL;
			sendAdifBand = true;
		}

		if (sendAdifBand){
			sendAdifBand = false;


			struct zjson *reply = zjson_init("");
			zjson_begin_object(reply);
			zjson_add_str(reply, "name", "adifband");
			zjson_add_str(reply, "adifband", adifband != NULL ? adifband : "*");
			zjson_end(reply);
			zjson_addln(reply);
			int wr = zserial_write(oct->zser, reply->gs->str, reply->gs->len);
			oct8tor_handle_error(oct, wr <= 0);

			z_trim_end(reply->gs->str);
			TRROT(0, "oct8tor send: %s = %d", reply->gs->str, wr);
			oct->keepalive = ztimeout_init(OCT_KEEPALIVE);
			zjson_free(reply);
		}

		for (int i = 0; i < rotars->len; i++){
			struct rotar *rot = (struct rotar *)g_ptr_array_index(rotars, i);
			if (rot->type != ROT_OCT8TOR) continue;

			if (rot->oct_azim > -1 || rot->oct_elev > -90){

				nr = rot->oct_nr;

				azim = rot->oct_azim;
				rot->oct_azim = -1;

				elev = rot->oct_elev;
				rot->oct_elev = -90;
				break;
			}
		}
		MUTEX_UNLOCK(rotars);

		if (nr > 0){
			struct zjson *req = zjson_init("");
			zjson_begin_object(req);
			zjson_add_str(req, "name", "seek");
			if (adifband != NULL) {
				zjson_add_str(req, "adifband", adifband);
				zjson_add_int(req, "nr", nr);
			}else{
				char sys[4];
				sprintf(sys, "%c", 'A' + nr - 1);
				zjson_add_str(req, "sys", sys);
			}
			if (azim > -1) {
				zjson_add_int(req, "az", azim);
			}
			if (elev > -90) {
				zjson_add_int(req, "el", elev);
			}
			zjson_end(req);
			zjson_addln(req);
			int wr = zserial_write(oct->zser, req->gs->str, req->gs->len);
			oct8tor_handle_error(oct, wr <= 0);
			
			z_trim_end(req->gs->str);
			TRROT(0, "oct8tor send: %s = %d", req->gs->str, wr);
			oct->keepalive = ztimeout_init(OCT_KEEPALIVE);
		}

		if (ztimeout_occured(oct->keepalive)){
			int wr = zserial_write(oct->zser, "\r\n", 2);
			oct8tor_handle_error(oct, wr <= 0);
			
			//TRROT(1, "oct8tor send keepalive = %d", wr);
			oct->keepalive = ztimeout_init(OCT_KEEPALIVE);
		}

		/*if (oct->poll_ms > 0){
			usleep(oct->poll_ms * 1000);
		}*/
	}
	return NULL;
}


void oct8tor_read(struct oct8tor *oct){	 // thread
	char s[1024];
	int ret;

	ret = zserial_read(oct->zser, s, 1000, 100);
	oct8tor_handle_error(oct, ret < 0);
	if (ret <= 0) {
		return;
	}

	zbinbuf_append_bin(oct->rdbuf, s, ret);

	while (1){
		char *line = zbinbuf_readline(oct->rdbuf);
		if (line == NULL) break;
		if (strlen(line) == 0) continue; // keepalive
		TRROT(1, "oct8tor recv: %s", line);
		zselect_msg_send(zsel, "%s;%s", "OCT", line);
		g_free(line);
	}
}

void oct8tor_handle_error(struct oct8tor* oct, bool isErrror) {
	if (isErrror) {
		oct->onconnect = true;
	}
}

//--------  main thread -------------------------------------
void oct8tor_read_handler(int n, char **line){ 
	struct zjson *json = zjson_init(line[1]);
	oct8tor_onreceived(goct8tor, json);
	zjson_free(json);
}


void oct8tor_onreceived(struct oct8tor *oct, struct zjson *json){
	//dbg("oct8tor_onreceived: '%s'\n", json->gs->str);
	TRROT(0, "oct8tor_onreceived: '%s'\n", json->gs->str);
	char *name = zjson_get_str(json, "name", NULL);
	if (name == NULL) return;

	if (strcmp(name, "rotars") == 0){
		oct8tor_rcvd_rotars(oct, json);
	}else if (strcmp(name, "angle") == 0){
		oct8tor_rcvd_angle(oct, json);
	}

	g_free(name);
	redraw_later();
}

void oct8tor_rcvd_rotars(struct oct8tor *oct, struct zjson *json){
	// {"name":"rotars", "band":"70cm", "rotars":[{"nr":1, "sys":"B", "az":10, "el":1}, {"nr":2, "sys":"C", "az":20, "el":2}]}
	//char *band = zjson_get_str(json, "band", NULL);

	MUTEX_LOCK(rotars);
	for (int i = 0; i < rotars->len; i++){
		struct rotar *rot = (struct rotar *)g_ptr_array_index(rotars, i);
		if (rot->type == ROT_OCT8TOR) rot->oct_hidden = true;
	}

	struct zjson *array = zjson_get_array(json, "rotars");
	while (array != NULL){
		struct zjson *rotar = zjson_get_object(array, NULL); // {"nr":1, "sys":"B", "az":10, "el":1}
		if (rotar == NULL) break;							  // {"nr":2, "sys":"C", "az":20, "el":2}

		int nr = zjson_get_int(rotar, "nr", 0);
		if (nr > 0 && nr <= cfg->oct_alloc){
			struct rotar *rot = (struct rotar *)g_ptr_array_index(rotars, nr - 1);
			rot->oct_hidden = false;
			g_free(rot->desc);
			char *sys = zjson_get_str(rotar, "sys", "?");
			rot->desc = g_strdup_printf("Oct%s", sys);
			rot->oct_sys = sys[0];

			float azAngle = zjson_get_float(rotar, "az", NAN);
			float elAngle = zjson_get_float(rotar, "el", NAN);
			char rotchar = rot_get_rotchar_by_sys(sys);
			g_free(sys);

			if (isfinite(azAngle) && isfinite(elAngle)){
				zselect_msg_send(zsel, "%s;%c;%s;%d;%d", "ROT", rotchar, "QTF", (int)round(azAngle), (int)round(elAngle));
			}else if (isfinite(azAngle)){
				zselect_msg_send(zsel, "%s;%c;%s;%d; ", "ROT", rotchar, "QTF", (int)round(azAngle));
			}else if (isfinite(elAngle)){
				zselect_msg_send(zsel, "%s;%c;%s; ;%d", "ROT", rotchar, "QTF", (int)round(elAngle));
			}

			if (isfinite(elAngle)){
				rot->elev = (int)round(elAngle);
			}

		}
		zjson_free(rotar);
	}
	zjson_free(array);
	MUTEX_UNLOCK(rotars);

	redraw_later();
}

void oct8tor_rcvd_angle(struct oct8tor *oct, struct zjson *json){
	
	char *ch = zjson_get_str(json, "ch", NULL);
	if (*ch != '\0'){
		float angle = zjson_get_float(json, "angle", NAN);
		char rotchar = rot_get_rotchar_by_ch(ch);
		if (isnan(angle) == false && rotchar != '\0'){
			if (strlen(ch) >= 2 && tolower(ch[1]) == 'e'){
				zselect_msg_send(zsel, "%s;%c;%s; ;%d", "ROT", rotchar, "QTF", (int)round(angle));
			}else{
				zselect_msg_send(zsel, "%s;%c;%s;%d; ", "ROT", rotchar, "QTF", (int)round(angle));
			}

		}
		g_free(ch);
	}

}

void oct8tor_send_band(struct oct8tor *oct8tor, const char *adifband){
	if (!oct8tor) return;

	MUTEX_LOCK(rotars);
	g_free(oct8tor->adifband);
	if (adifband != NULL){
		oct8tor->adifband = g_strdup(adifband);
	}else{
		for (int i = 0; i < rotars->len; i++){
			struct rotar *rot = (struct rotar *)g_ptr_array_index(rotars, i);
			if (rot->type == ROT_OCT8TOR) rot->oct_hidden = true;
		}
		oct8tor->adifband = NULL;
	}
	MUTEX_UNLOCK(rotars);
}



#endif