#define MODULE_LOG_PREFIX "gbox/sms"

#include "globals.h"

#ifdef MODULE_GBOX
#include "module-gbox.h"
#include "module-gbox-sms.h"
#include "oscam-string.h"
#include "oscam-files.h"
#include "oscam-string.h"
#include "oscam-client.h"
#include "oscam-time.h"
#include "oscam-lock.h"

static int32_t poll_gsms_data(uint16_t *boxid, uint8_t *num, char *text)
{
	char *fext= FILE_GSMS_TXT;
	char *fname = get_gbox_tmp_fname(fext);

	FILE *fhandle = fopen(fname, "r");
	if(!fhandle)
	{
		//cs_log("Couldn't open %s: %s", fname, strerror(errno));
		return -2;
	}

	uint32_t length1;
	uint8_t length;
	char buffer[140];
	char *tail;

	memset(buffer, 0, sizeof(buffer));
	fseek(fhandle, 0, SEEK_END);
	length1 = ftell(fhandle);
	fseek(fhandle, 0, SEEK_SET);

	if(length1 < 13)
	{
		cs_log("GSMS failed. Min msg char in %s = 6, actual = %d", fname, length1 - 7);
		fclose(fhandle);
		unlink(fname);
		return -1;
	}

	if(fgets(buffer, 140, fhandle) != NULL)
	{
		*boxid = strtol(buffer, &tail, 16);
		*num = atoi(tail);
	}

	fclose(fhandle);
	unlink(fname);

	if(length1 > 127 + 7)
	{
		length = 127 + 7;
	}
	else
	{
		length = length1;
	}

	cs_log_dbg(D_READER, "total msg length taken from %s = %d, limited to %d", fname, length1, length);
	cs_strncpy(text, buffer + 7, sizeof(buffer));

	return 0;
}

static void write_gsms_to_osd_file(struct s_client *cli, uint8_t *gsms)
{
	char *fext= FILE_OSD_MSG;
	char *fname = get_gbox_tmp_fname(fext);

	if(file_exists(fname))
	{
		char gsms_buf[150];
		uint8_t i;

		// allow only alphanumerical characters in osd gsms due to safety reasons
		for(i = 0; i < cs_strlen((char *)gsms); i++)
		{
			if(!isalnum(gsms[i]) && gsms[i] != ' ')
			{
				gsms[i] = '_';
			}
		}

		memset(gsms_buf, 0, sizeof(gsms_buf));
		snprintf(gsms_buf, sizeof(gsms_buf), "%s %s:%s %s", fname, username(cli), cli->reader->device, gsms);
		cs_log_dbg(D_READER, "found OSD 'driver' %s - write gsms to OSD", fname);

		char *cmd = gsms_buf;
		FILE *p;
		if((p = popen(cmd, "w")) == NULL)
		{
			cs_log("Error %s", fname);
			return;
		}

		pclose(p);
	}

	return;
}

void write_gsms_ack(struct s_client *cli)
{
	char tsbuf[28];
	time_t walltime = cs_time();
	cs_ctime_r(&walltime, tsbuf);
	struct gbox_peer *peer = cli->gbox;
	char *fext = FILE_GSMS_ACK;
	char *fname = get_gbox_tmp_fname(fext);

	FILE *fhandle = fopen(fname, "a+");
	if(!fhandle)
	{
		cs_log("Couldn't open %s: %s", fname, strerror(errno));
		return;
	}

	fprintf(fhandle, "Peer %04X (%s) confirmed receipt of GSMS on %s", peer->gbox.id, cli->reader->device, tsbuf);
	fclose(fhandle);
	return;
}

static void write_gsms_nack(struct s_client *cl, uint8_t inf)
{
	char tsbuf[28];
	time_t walltime = cs_time();
	cs_ctime_r(&walltime, tsbuf);
	struct gbox_peer *peer = cl->gbox;
	char *fext = FILE_GSMS_NACK;
	char *fname = get_gbox_tmp_fname(fext);

	FILE *fhandle = fopen(fname, "a+");
	if(!fhandle)
	{
		cs_log("Couldn't open %s: %s", fname, strerror(errno));
		return;
	}

	if(inf)
	{
		fprintf(fhandle, "INFO: GSMS to all: Peer %04X (%s) was OFFLINE %s",
			peer->gbox.id, cl->reader->device,tsbuf);
	}
	else
	{
		fprintf(fhandle, "WARNING: Private GSMS to Peer %04X (%s) failed - was OFFLINE %s",
			peer->gbox.id, cl->reader->device,tsbuf);
	}

	fclose(fhandle);
	return;
}

void write_gsms_msg(struct s_client *cli, uint8_t *gsms, uint16_t type, uint16_t UNUSED(msglen))
{
	char tsbuf[28];
	time_t walltime = cs_time();
	cs_ctime_r(&walltime, tsbuf);
	struct gbox_peer *peer = cli->gbox;
	struct s_reader *rdr = cli->reader;
	char *fext = FILE_GSMS_MSG;
	char *fname = get_gbox_tmp_fname(fext);

	FILE *fhandle = fopen(fname, "a+");
	if(!fhandle)
	{
		cs_log("Couldn't open %s: %s", fname, strerror(errno));
		return;
	}

	if(type == 0x30)
	{
		fprintf(fhandle, "Normal message received from %04X %s on %s%s\n\n", peer->gbox.id, cli->reader->device, tsbuf, gsms);
		rdr->gbox_gsms_peer = peer->gbox.id;
		snprintf(rdr->last_gsms, sizeof(rdr->last_gsms), "%s %s", gsms, tsbuf); // for easy handling of gsms by webif
	}
	else if(type == 0x31)
	{
		fprintf(fhandle, "OSD message received from %04X %s on %s%s\n\n", peer->gbox.id, cli->reader->device, tsbuf, gsms);
		write_gsms_to_osd_file(cli, gsms);
		rdr->gbox_gsms_peer = peer->gbox.id;
		snprintf(rdr->last_gsms, sizeof(rdr->last_gsms), "%s %s", gsms, tsbuf); // for easy handling of gsms by webif
	}
	else
	{
		fprintf(fhandle, "Corrupted message received from %04X %s on %s%s\n\n", peer->gbox.id, cli->reader->device, tsbuf, gsms);
	}

	fclose(fhandle);
	return;
}

void gsms_unavail(void)
{
	cs_log("INFO: GSMS feature disabled by conf");
}

static void gbox_send_gsms2peer(struct s_client *cl, char *gsms, uint8_t msg_type, int8_t gsms_len)
{
	uint8_t outbuf[150];
	struct gbox_peer *peer = cl->gbox;
	uint16_t local_gbox_id = gbox_get_local_gbox_id();
	uint32_t local_gbox_pw = gbox_get_local_gbox_password();
	struct s_reader *rdr = cl->reader;

	gbox_message_header(outbuf, MSG_GSMS, peer->gbox.password, local_gbox_pw);

	outbuf[10] = (peer->gbox.id >> 8) & 0xff;
	outbuf[11] = peer->gbox.id & 0xff;
	outbuf[12] = (local_gbox_id >> 8) & 0xff;
	outbuf[13] = local_gbox_id & 0xff;
	outbuf[14] = msg_type;
	outbuf[15] = gsms_len;
	memcpy(outbuf + 16, gsms, gsms_len);
	outbuf[16 + gsms_len] = 0;

	cs_log("<-[gbx] send GSMS to %s:%d id: %04X", rdr->device, rdr->r_port, peer->gbox.id);
	gbox_send(cl, outbuf, gsms_len + 17);
	return;
}
int gbox_direct_send_gsms(uint16_t boxid, uint8_t num, char *gsms)
{
	uint8_t msg_type = 0, gsms_len = 0;
	int peer_found = 0;
	char text[GBOX_MAX_MSG_TXT + 1];

	memset(text, 0, sizeof(text));

	if(cfg.gsms_dis)
	{
		gsms_unavail();
		return 0;
	}

	gsms_len = cs_strlen(gsms);
	if(gsms_len < 6)
	{
		cs_log("GBOX: message to send to peer is too short 6 chars expected and %d received text[%s]", gsms_len, gsms);
	}
	else if(gsms_len > GBOX_MAX_MSG_TXT)
	{
		gsms_len = GBOX_MAX_MSG_TXT;
		cs_log("GBOX message is too long so it will be truncated to max. [%d]", GBOX_MAX_MSG_TXT);
	}

	cs_strncpy(text, gsms, sizeof(text));

	switch(num)
	{
		case 0:
			msg_type = 0x30;
			break;

		case 1:
			msg_type = 0x31;
			break;

		//case 2:
		//	gsms_prot = 2;
		//	msg_type = 0x30;
		//	break;

		//case 3:
		//	gsms_prot = 2;
		//	msg_type = 0x31;
		//	break;

		default:
			cs_log("ERROR unknown gsms protocol");
			return 0;
	}

	cs_log_dbg(D_READER,"init gsms_length=%d msg_type=%02X ", gsms_len, msg_type);
	struct s_client *cl;
	cs_readlock(__func__, &clientlist_lock);

	for(cl = first_client; cl; cl = cl->next)
	{
		peer_found=0;
		if(cl->gbox && cl->typ == 'p')
		{
			struct gbox_peer *peer = cl->gbox;
			if(peer->online && boxid == 0xFFFF) // send gsms to all peers online
			{
				gbox_send_gsms2peer(cl, text, msg_type, gsms_len);
				peer_found=1;
			}

			if(!peer->online && boxid == 0xFFFF)
			{
				cs_log("GBOX Info: peer %04X is OFFLINE", peer->gbox.id);
				write_gsms_nack( cl, 1);
			}

			if(peer->online && boxid == peer->gbox.id)
			{
				gbox_send_gsms2peer(cl, text, msg_type, gsms_len);
				peer_found=1;
			}

			if(!peer->online && boxid == peer->gbox.id)
			{
				cs_log("GBOX WARNING: send GSMS failed - peer %04X is OFFLINE", peer->gbox.id);
				write_gsms_nack( cl, 0);
			}
		}
	}

	cs_readunlock(__func__, &clientlist_lock);
	return peer_found;
}

void gbox_get_online_peers(void)
{
	int n = 0, i;
	struct s_client *cl;

	for(i = 0; i < GBOX_MAX_DEST_PEERS; i++)
	{
		cfg.gbox_dest_peers[i] = '\0';
	}

	cfg.gbox_dest_peers_num = 0;
	cs_readlock(__func__, &clientlist_lock);

	for(cl = first_client; cl; cl = cl->next)
	{
		if(cl->gbox && cl->typ == 'p' && n < GBOX_MAX_DEST_PEERS)
		{
			struct gbox_peer *peer = cl->gbox;
			if(peer->online) // peer is online
			{
				cfg.gbox_dest_peers[n++] = peer->gbox.id;
			}
		}
	}

	cs_readunlock(__func__, &clientlist_lock);
	cfg.gbox_dest_peers_num = n;
	return;
}

void gbox_init_send_gsms(void)
{
	uint16_t boxid = 0;
	uint8_t num = 0;
	uint8_t msg_type = 0;
	int32_t poll_result = 0;
	char text[150];
	memset(text, 0, sizeof(text));
	char *fext = FILE_GSMS_TXT;
	char *fname = get_gbox_tmp_fname(fext);

	if(cfg.gsms_dis)
	{
		unlink(fname);
		gsms_unavail();
		return;
	}

	poll_result = poll_gsms_data( &boxid, &num, text);
	if(poll_result)
	{
		if(poll_result != -2)
		{
			cs_log("ERROR polling file %s", fname);
		}

		return;
	}

	int8_t gsms_len = cs_strlen(text);
	cs_log_dbg(D_READER,"got from %s: box_ID = %04X num = %d gsms_length = %d txt = %s", fname, boxid, num, gsms_len, text);

	switch(num)
	{
		case 0:
			msg_type = 0x30;
			break;

		case 1:
			msg_type = 0x31;
			break;

		//case 2:
		//	gsms_prot = 2;
		//	msg_type = 0x30;
		//	break;

		//case 3:
		//	gsms_prot = 2;
		//	msg_type = 0x31;
		//	break;

		default:
			cs_log("ERROR unknown gsms protocol");
			return;
	}

	cs_log_dbg(D_READER,"init gsms to boxid= %04X length= %d msg_type= %02X ", boxid, gsms_len, msg_type);

	uint8_t id_valid = 0;
	struct s_client *cl;
	cs_readlock(__func__, &clientlist_lock);

	for(cl = first_client; cl; cl = cl->next)
	{
		if(cl->gbox && cl->typ == 'p')
		{
			struct gbox_peer *peer = cl->gbox;

			if(peer->online && boxid == 0xFFFF) // send gsms to all peers online
			{
				gbox_send_gsms2peer(cl, text, msg_type, gsms_len);
				id_valid = 1;
			}

			if(!peer->online && boxid == 0xFFFF)
			{
				cs_log("Info: peer %04X is OFFLINE", peer->gbox.id);
				write_gsms_nack( cl, 1);
				id_valid = 1;
			}

			if(peer->online && boxid == peer->gbox.id)
			{
				gbox_send_gsms2peer(cl, text, msg_type, gsms_len);
				id_valid = 1;
			}

			if(!peer->online && boxid == peer->gbox.id)
			{
				cs_log("WARNING: send GSMS failed - peer %04X is OFFLINE", peer->gbox.id);
				write_gsms_nack( cl, 0);
				id_valid = 1;
			}
		}
	}

	cs_readunlock(__func__, &clientlist_lock);

	if(!id_valid)
	{
		cs_log("WARNING: send GSMS failed - peer_id unknown");
	}

	return;
}

void gbox_send_gsms_ack(struct s_client *cli)
{
	uint8_t outbuf[20];
	struct gbox_peer *peer = cli->gbox;
	uint16_t local_gbox_id = gbox_get_local_gbox_id();
	uint32_t local_gbox_pw = gbox_get_local_gbox_password();
	struct s_reader *rdr = cli->reader;

	if(peer->online)
	{
		gbox_message_header(outbuf, MSG_GSMS_ACK, peer->gbox.password, local_gbox_pw);

		outbuf[10] = 0;
		outbuf[11] = 0;
		outbuf[12] = (local_gbox_id >> 8) & 0xff;
		outbuf[13] = local_gbox_id & 0xff;
		outbuf[14] = 0x1;
		outbuf[15] = 0;

		cs_log_dbg(D_READER,"<-[gbx] send GSMS_ACK to %s:%d id: %04X", rdr->device, rdr->r_port, peer->gbox.id);
		gbox_send(cli, outbuf, 16);
	}
}

#endif
