/*
 * (c) BayCom GmbH, http://www.baycom.de, info@baycom.de
 *
 * See the COPYING file for copyright information and
 * how to reach the author.
 *
 * modified by Reel Multimedia, http://www.reel-multimedia.com, info@reel-multimedia.com
 * 01042010 DL: use a single thread for reading from network layer (uses less resources)
 *
 */

#include "headers.h"

//---------------------------------------------------------------------------------------------
void mmi_print_info (mmi_info_t * m)
{
	char str[INET6_ADDRSTRLEN];
	printf ("------------------\n");
	inet_ntop (AF_INET6, &m->ipv6, (char *) str, INET6_ADDRSTRLEN);
	printf ("IP: %s\n", str);
	printf ("UUID: %s\n", m->uuid);
	printf ("Slot: %d\n", m->slot);

	int i;
	for (i = 0; i < m->caid_num; i++) {
		caid_mcg_t *cm = m->caids + i;
		printf ("%i.SID: %d\n", i, cm->caid);
		inet_ntop (AF_INET6, &cm->mcg, (char *) str, INET6_ADDRSTRLEN);
		printf ("%i.MCG: %s\n", i, str);
	}
	printf ("TEXT:\n===================\n %s \n===================\n", m->mmi_text);

}

//---------------------------------------------------------------------------------------------
int mmi_open_menu_session (char *uuid, char *intf, int port, int cmd)
{
	int ret;
	int j, sockfd;
	struct in6_addr ipv6;
	char iface[IFNAMSIZ];

	inet_pton (AF_INET6, uuid, &ipv6);

	if (!intf || !strlen (intf)) {
		struct intnode *intn = int_find_first ();
		if (intn) {
			strcpy (iface, intn->name);
		}
	} else {
		strncpy (iface, intf, sizeof (iface));
		iface[sizeof (iface) - 1] = 0;
	}
	if (!port) {
		port = 23013;
	}

	sockfd = socket (PF_INET6, SOCK_STREAM, 0);
	j = 1;
	if (setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR, (_SOTYPE) & j, sizeof (j)) < 0) {
		err ("setsockopt REUSEADDR\n");
	}

	j = 1;
	if (setsockopt (sockfd, SOL_SOCKET, TCP_NODELAY, (_SOTYPE) & j, sizeof (j)) < 0) {
		warn ("setsockopt TCP_NODELAY\n");
	}

	dbg ("Connect To: %s\n", uuid);

	struct sockaddr_in6 addr;
	memset (&addr, 0, sizeof (struct sockaddr_in6));

	addr.sin6_family = AF_INET6;
	addr.sin6_port = htons (port);
	addr.sin6_addr = ipv6;
	addr.sin6_scope_id = if_nametoindex (iface);

	ret = connect (sockfd, (struct sockaddr *) &addr, sizeof (struct sockaddr_in6));
	if (ret < 0) {
		dbg ("Failed to access NetCeiver MMI support\n");
		return -1;
	}
	//send init cmd
	char buf[128];
	memset (buf, 0, sizeof (buf));
	dbg ("Request CAM slot %d \n", cmd);
	sprintf (buf, "%x", cmd);
	int n = send (sockfd, buf, strlen (buf) + 1, 0);
	if (n < 0) {
		dbg ("unable to sent mmi connection cmd !\n");
		closesocket (sockfd);
		return -1;
	}
	dbg ("MMI SESSION : OK\n");
	return sockfd;
}

//---------------------------------------------------------------------------------------------
void mmi_close_menu_session (int s)
{
	closesocket (s);
}

//---------------------------------------------------------------------------------------------
int mmi_cam_reset (char *uuid, char *intf, int port, int slot)
{
	int cmd = (slot << 12) | 0xfff;
	printf ("Reseting slot %d (cmd %x)...\n", slot, cmd);
	int sock = mmi_open_menu_session (uuid, intf, port, cmd);
	if (sock < 1) {
		printf ("Unable to reset slot %d on netceiver %s...\n", slot, uuid);
	}
	closesocket (sock);
	return 0;
}
//---------------------------------------------------------------------------------------------
int mmi_cam_reinit (char *uuid, char *intf, int port, int slot)
{
	int cmd = (slot << 12) | 0xeee;
	printf ("Reinitializing slot %d (cmd %x)...\n", slot, cmd);
	int sock = mmi_open_menu_session (uuid, intf, port, cmd);
	if (sock < 1) {
		printf ("Unable to reset slot %d on netceiver %s...\n", slot, uuid);
	}
	closesocket (sock);
	return 0;
}
//---------------------------------------------------------------------------------------------
int mmi_get_menu_text (int sockfd, char *buf, int buf_len, int timeout)
{
	int n = -1;
	struct pollfd p;
	memset (buf, 0, buf_len);
	p.fd = sockfd;
	p.events = POLLIN;
	if (poll (&p, 1, (timeout+999)>>10) > 0) {
		n = recv (sockfd, buf, buf_len, 0);	//MSG_DONTWAIT);
	}
	if (n > 0) {
		dbg ("recv:\n%s \n", buf);
	}
	return n;
}

//---------------------------------------------------------------------------------------------
int mmi_send_menu_answer (int sockfd, char *buf, int buf_len)
{
	dbg ("send: %s len %d \n", buf, buf_len);
	int n;
	n = send (sockfd, buf, buf_len, 0);
	if (n < 0) {
		dbg ("mmi_send_answer: error sending !\n");
	}
	return n;
}

//---------------------------------------------------------------------------------------------
UDPContext *mmi_broadcast_client_init (int port, char *intf)
{
	UDPContext *s;
	char mcg[1024];
	char iface[IFNAMSIZ];
	//FIXME: move to common
	strcpy (mcg, "ff18:6000::");
	if (!intf || !strlen (intf)) {
		struct intnode *intn = int_find_first ();
		if (intn) {
			strcpy (iface, intn->name);
		}
	} else {
		strncpy (iface, intf, sizeof (iface));
		iface[sizeof (iface) - 1] = 0;
	}
	if (!port) {
		port = 23000;
	}

	s = client_udp_open_host (mcg, port, iface);
	if (!s) {
		dbg ("client udp open host error !\n");
	}

	return s;
}

void mmi_broadcast_client_exit (UDPContext * s)
{
	udp_close (s);
}

//---------------------------------------------------------------------------------------------
typedef struct
{
	xmlDocPtr doc;
	xmlChar *str, *key;
} xml_parser_context_t;

static void clean_xml_parser_thread (void *arg)
{
	xml_parser_context_t *c = (xml_parser_context_t *) arg;
	if (c->str) {
		xmlFree (c->str);
	}
	if (c->key) {
		xmlFree (c->key);
	}
	if (c->doc) {
		xmlFreeDoc (c->doc);
	}
	dbg ("Free XML parser structures!\n");
}

//---------------------------------------------------------------------------------------------
int mmi_get_data (xmlChar * xmlbuff, int buffersize, mmi_info_t * mmi_info)
{
	xml_parser_context_t c;
	xmlNode *root_element = NULL, *cur_node = NULL;

	xmlKeepBlanksDefault (0);	//reomve this f. "text" nodes
	c.doc = xmlParseMemory ((char *) xmlbuff, buffersize);
        c.str = NULL;
        c.key = NULL;
	root_element = xmlDocGetRootElement (c.doc);
	pthread_cleanup_push (clean_xml_parser_thread, &c);


	if (root_element != NULL) {
		cur_node = root_element->children;
		if (!xmlStrcmp (cur_node->name, (xmlChar *) "Description")) {
			root_element = cur_node->children;
			while (root_element != NULL) {
				c.key = NULL;
				c.str = NULL;
				if ((xmlStrcmp (root_element->name, (const xmlChar *) "component"))) {
					warn ("Cannot parse XML data\n");
					root_element = root_element->next;
					continue;
				}
				cur_node = root_element->children;
				if ((!xmlStrcmp (cur_node->name, (const xmlChar *) "Description"))) {
					c.str = xmlGetProp (cur_node, (unsigned char *) "about");
					//fprintf(stdout,"\n%s:\n",c.str);
					//fprintf(stdout,"-----------------------------------------------------------\n");
				} else {
					warn ("Cannot parse XML data\n");
					root_element = root_element->next;
					continue;
				}
				if (c.str && (!xmlStrcmp (c.str, (const xmlChar *) "MMIData"))) {
					cur_node = cur_node->children;
					while (cur_node != NULL) {
						if ((!xmlStrcmp (cur_node->name, (const xmlChar *) "IP"))) {
							c.key = xmlNodeListGetString (c.doc, cur_node->xmlChildrenNode, 1);
							if (c.key) {
								dbg ("IP: %s\n", c.key);
								inet_pton (AF_INET6, (char *) c.key, &mmi_info->ipv6);
								xmlFree (c.key);
							}
						} else if ((!xmlStrcmp (cur_node->name, (const xmlChar *) "UUID"))) {
							c.key = xmlNodeListGetString (c.doc, cur_node->xmlChildrenNode, 1);
							if (c.key) {
								dbg ("UUID: %s\n", c.key);
								strcpy (mmi_info->uuid, (char *) c.key);
								xmlFree (c.key);
							}
						} else if ((!xmlStrcmp (cur_node->name, (const xmlChar *) "Slot"))) {
							c.key = xmlNodeListGetString (c.doc, cur_node->xmlChildrenNode, 1);
							if (c.key) {
								dbg ("Slot: %s\n", c.key);
								mmi_info->slot = atoi ((char *) c.key);
								xmlFree (c.key);
							}
						} else if ((!xmlStrcmp (cur_node->name, (const xmlChar *) "TEXT"))) {
							c.key = xmlNodeListGetString (c.doc, cur_node->xmlChildrenNode, 1);
							if (c.key) {
								dbg ("TEXT: %s\n", c.key);
								int olen = MMI_TEXT_LENGTH, ilen = strlen ((char *) c.key);

								UTF8Toisolat1 ((unsigned char *) mmi_info->mmi_text, &olen, c.key, &ilen);

								xmlFree (c.key);
							}
						}
						cur_node = cur_node->next;
					}
				} else if (c.str && (!xmlStrcmp (c.str, (const xmlChar *) "ProgramNumberIDs"))) {
					cur_node = cur_node->children;
					while (cur_node != NULL) {
						if ((!xmlStrcmp (cur_node->name, (const xmlChar *) "MCG"))) {
							c.key = xmlNodeListGetString (c.doc, cur_node->xmlChildrenNode, 1);
							if (c.key) {
								dbg ("MCG: %s\n", c.key);
								struct in6_addr mcg;
								inet_pton (AF_INET6, (char *) c.key, &mcg);
								int sid;
								mcg_get_id (&mcg, &sid);
								mcg_set_id (&mcg, 0);
								mmi_info->caids = (caid_mcg_t *) realloc (mmi_info->caids, sizeof (caid_mcg_t) * (mmi_info->caid_num + 1));
								if (!mmi_info->caids)
									err ("mmi_get_data: out of memory\n");
								caid_mcg_t *cm = mmi_info->caids + mmi_info->caid_num;
								cm->caid = sid;
								cm->mcg = mcg;
								mmi_info->caid_num++;
								xmlFree (c.key);
							}
						}
						cur_node = cur_node->next;
					}
				}
				xmlFree (c.str);
				root_element = root_element->next;
			}
		}
	}

	xmlFreeDoc (c.doc);
	pthread_cleanup_pop (0);
	return 1;
}

//---------------------------------------------------------------------------------------------
int mmi_poll_for_menu_text (UDPContext * s, mmi_info_t * m, int timeout)
{
	char buf[8192];
	int n = 0;
	if (s) {
		n = udp_read (s, (unsigned char *) buf, sizeof (buf), timeout, NULL);
		if (n > 0) {
			dbg ("recv:\n%s \n", buf);
			memset (m, 0, sizeof (mmi_info_t));
			mmi_get_data ((xmlChar *) buf, n, m);
		}
	}
	return n;
}
//---------------------------------------------------------------------------------------------
