#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include "zlib.h"
#include "MonitorData.h"

//**********************************************************************************************
//**********************************************************************************************
//**********************************  DO NOT EDIT THIS FILE  ***********************************
//**********************************************************************************************
//**********************************************************************************************

#define BUFFER_SIZE_START		2048
#define BUFFER_RESIZE_VALUE		1024

extern void set_bit(unsigned char p[], int bit);
extern void unset_bit(unsigned char p[], int bit);
extern int  get_bit(unsigned char p[], int bit);

// ------------------   Support Routine  -----------------------------
//////////////////////////////////////////////////////////////////////
//  qsort compare routine
//////////////////////////////////////////////////////////////////////

int compar_name(const void *e1, const void *e2)
{
	MON_ELEMENT *data1, *data2;

	data1 = *(MON_ELEMENT **)e1;
	data2 = *(MON_ELEMENT **)e2;

#ifdef _WIN32
	return _stricmp(data1->label, data2->label);
#else
	return strcasecmp(data1->label, data2->label);
#endif
}

int compar_index(const void *e1, const void *e2)
{
	MON_ELEMENT *data1, *data2;
	data1 = (MON_ELEMENT *)e1;
	data2 = (MON_ELEMENT *)e2;
	return data1->id - data2->id;
}

// ------------------   Game Data Object ----------------------------
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
//CMonitorData::CMonitorData(GenericNotifier *notifier)
//: m_notifier(notifier)
CMonitorData::CMonitorData()
{
	m_sequence = 1;
	m_buffer = nullptr;
	m_nbuffer = 0;
	m_count = 0;
	m_max = ELEMENT_MAX_START;
	m_data = new MON_ELEMENT[m_max];
	for (int x = 0; x < m_max; x++)
	{
		memset(&m_data[x], 0, sizeof(MON_ELEMENT));
	}
	resize_buffer(BUFFER_SIZE_START);
}

CMonitorData::~CMonitorData()
{
	int x;

	for (x = 0; x < m_count; x++)
	{
		delete[] m_data[x].discription;

		delete[] m_data[x].label;
	}

	delete m_buffer;

	delete[] m_data;
}

void CMonitorData::setMax(int _max)
{
	delete[] m_data;

	m_max = _max;
	m_data = new MON_ELEMENT[m_max];
	for (int x = 0; x < m_max; x++)
	{
		memset(&m_data[x], 0, sizeof(MON_ELEMENT));
	}
}

void CMonitorData::resize_buffer(int new_size)
{
	char *temp;

	if (m_buffer != nullptr)
	{
		temp = m_buffer;
		m_buffer = new char[new_size];
		memcpy(m_buffer, temp, m_nbuffer);
		m_nbuffer = new_size;
		delete[] temp;
	}
	else
	{
		m_buffer = new char[new_size];
		m_nbuffer = new_size;
	}
}

void CMonitorData::send(UdpConnection *con, short & sequence, short msg, char *data)
{
	int len;
	unsigned short size;
	char *p;

	size = (unsigned short)strlen(data) + 1;
	p = (char *)malloc(size + 6);
	len = 0;
	packShort(p + len, len, msg);
	packShort(p + len, len, m_sequence);
	packShort(p + len, len, size);
	memcpy(p + len, data, size);
	con->Send(cUdpChannelReliable1, p, size + 6);
	free(p);
	sequence++;
}

void CMonitorData::send(UdpConnection *con, short & sequence, short msg, char *data, int size)
{
	unsigned long compress_len;
	int len;
	unsigned short usize;
	char *p;
	int rnt;

	if (CURRENT_API_VERSION != CURRENT_API_3)
		return;

	p = (char *)malloc(size + 6 + 1000);
	len = 0;
	memset(p, 0, size + 6 + 1000);
	packShort(p + len, len, msg);
	packShort(p + len, len, m_sequence);
	compress_len = size + 1000;
	rnt = compress2((Bytef *)p + 6, &compress_len, (Bytef *)data, (long)size, 2);
	if (rnt != Z_OK)
	{
		free(p);
		printf("CMonitor::send-compress failed, error=%d size =%d\n", rnt, size);
		return;
	}

	usize = (unsigned short)compress_len;
	packShort(p + len, len, usize);
	usize += 6;
	con->Send(cUdpChannelReliable1, p, usize);
	free(p);
	sequence++;
}

bool CMonitorData::processHierarchyRequestBlock(UdpConnection *con, short & sequence)
{
	int x, size, count, len;
	char temp[512];

	if (m_count == 0)
	{
		send(con, sequence, MON_MSG_REPLY_HIERARCHY, "NOTREADY");
		return 0;
	}

	memset(m_buffer, 0, 16);
	send(con, sequence, MON_MSG_REPLY_HIERARCHY_BLOCK_BEGIN, m_buffer);

	MON_ELEMENT	**me;
	me = new MON_ELEMENT *[m_count];
	for (x = 0; x < m_count; x++)
		me[x] = &m_data[x];
	qsort(me, m_count, sizeof(MON_ELEMENT *), compar_name);

	count = x = size = 0;
	while (x < m_count)
	{
		len = sprintf(temp, "%s,%X,%d|", me[x]->label, me[x]->id, me[x]->ping);
		if (len + size >= m_nbuffer) resize_buffer(size + BUFFER_RESIZE_VALUE);
		strcpy(&m_buffer[size], temp);
		size += len;
		if (size > 64000)
		{
			send(con, sequence, MON_MSG_REPLY_HIERARCHY_BLOCK, m_buffer, size + 1);
			memset(m_buffer, 0, size);
			count++;
			size = 0;
		}
		x++;
	}
	delete[] me;
	if (size > 0)
	{
		send(con, sequence, MON_MSG_REPLY_HIERARCHY_BLOCK, m_buffer, size + 1);
		memset(m_buffer, 0, 16);
		count++;
	}
	memset(m_buffer, 0, 16);
	send(con, sequence, MON_MSG_REPLY_HIERARCHY_BLOCK_END, m_buffer);
	return 1;
}

bool CMonitorData::processElementsRequest(
	UdpConnection *con, short & sequence, char * data, int /* dataLen */, long lastUpdateTime)
{
	char	tmp[200];
	int		x, id;
	int     size;
	int		count;
	char	**list;

	memset(m_buffer, 0, 16);
	if (!strcmp(data, "-1"))
	{
		size = 0;
		for (x = 0; x < m_count; x++)
		{
			sprintf(tmp, "%d %x|", m_data[x].id, m_data[x].value);
			size += (int)strlen(tmp);
			if (size >= m_nbuffer)
				resize_buffer(size + BUFFER_RESIZE_VALUE);
			strcat(m_buffer, tmp);
			if (size > 65000)
			{
				send(con, sequence, MON_MSG_REPLY_ELEMENTS, m_buffer);
				memset(m_buffer, 0, 16);
			}
		}
		if (size)
			send(con, sequence, MON_MSG_REPLY_ELEMENTS, m_buffer);

		return 1;
	}

	if (!strcmp(data, "-2"))
	{
		size = 0;
		count = 0;
		for (x = 0; x < m_count; x++)
		{
			if (lastUpdateTime <= m_data[x].ChangedTime)
			{
				sprintf(tmp, "%d %x|", m_data[x].id, m_data[x].value);
				size += (int)strlen(tmp);
				if (size >= m_nbuffer)
					resize_buffer(size + BUFFER_RESIZE_VALUE);
				strcat(m_buffer, tmp);
				count++;
			}

			if (size > 65000)
			{
				send(con, sequence, MON_MSG_REPLY_ELEMENTS, m_buffer);
				memset(m_buffer, 0, 16);
			}
		}
		if (count)
		{
			send(con, sequence, MON_MSG_REPLY_ELEMENTS, m_buffer);
			return 1;
		}
		return 0;
	}

	list = new char *[m_max];
	count = parseList(list, data, '|', m_max);
	memset(m_buffer, 0, 16);

	size = 0;
	for (x = 0; x < count; x++)
	{
		id = atoi(list[x]);

		for (int y = 0; y < m_count; y++)
			if (m_data[y].id == id)
			{
				sprintf(tmp, "%d %x|", m_data[y].id, m_data[y].value);
				size += (int)strlen(tmp);
				if (size >= m_nbuffer)
					resize_buffer(size + BUFFER_RESIZE_VALUE);
				strcat(m_buffer, tmp);
			}
	}

	delete[] list;

	if (count > 0)
		send(con, sequence, MON_MSG_REPLY_ELEMENTS, m_buffer);

	return 0;
}

bool CMonitorData::processDescriptionRequest(UdpConnection *con, short & sequence, char * userData, int, unsigned char *mark)
{
	char line[4096];
	char tmp[400];
	int	 x, id, size;
	int flag;
	char *p;

	size = 0;
	memset(m_buffer, 0, 16);
	strcpy(line, userData);
	p = strtok(line, "|");
	flag = 0;
	if (strstr(p, "next"))
	{
		for (x = 0; x < m_count; x++)
		{
			if (get_bit(mark, x) == 0)
			{
				set_bit(mark, x);
				if (m_data[x].discription != nullptr)
				{
					sprintf(tmp, "%d,%s|", m_data[x].id, m_data[x].discription);
					size += (int)strlen(tmp);
					if (size >= m_nbuffer)
						resize_buffer(size + BUFFER_RESIZE_VALUE);
					strcat(m_buffer, tmp);
					send(con, sequence, MON_MSG_REPLY_DESCRIPTION, m_buffer);
					return 1;
				}
			}
		}
	}
	else
	{
		if (p)
		{
			size = 0;
			flag = 0;
			id = atoi(p);
			for (x = 0; x < m_count; x++)
			{
				if (flag == 0 && id == m_data[x].id)
					flag = 1;

				if (flag)
				{
					flag = 2;
					set_bit(mark, x);
					if (m_data[x].discription != nullptr)
					{
						sprintf(tmp, "%d,%s|", m_data[x].id, m_data[x].discription);
						size += (int)strlen(tmp);
						if (size >= m_nbuffer)
							resize_buffer(size + BUFFER_RESIZE_VALUE);
						strcat(m_buffer, tmp);
						if (size >= 2000 || m_data[x].discription)
						{
							send(con, sequence, MON_MSG_REPLY_DESCRIPTION, m_buffer);
							return 1;
						}
					}
				}
			}
		}
	}

	if (flag == 2)
		send(con, sequence, MON_MSG_REPLY_DESCRIPTION, m_buffer);
	send(con, sequence, MON_MSG_REPLY_DESCRIPTION, "none");
	return 0;
}

int CMonitorData::add(const char *label, int id, int ping, const char *des)
{
	int x;

	if (strlen(label) >= 127)
	{
		printf("MonAPI Error: add() label exceeds length of 127. ->%s\n", label);
		return 0;
	}

	for (x = 0; x < m_count; x++)
	{
		if (id == m_data[x].id)
		{
			printf("MonAPI: assign new Id (%d) %s -> %s\n", id, m_data[x].label, label);
			m_data[x].ping = pingValue(ping);
			m_data[x].value = 0;
			m_data[x].id = id;

			//*******  Label  ***********
			delete[] m_data[x].label;
			m_data[x].label = new char[strlen(label) + 1];
			strcpy(m_data[x].label, label);

			//*******  Discription ******
			delete[] m_data[x].discription;
			m_data[x].discription = nullptr;
			if (des)
			{
				m_data[x].discription = new char[strlen(des) + 1];
				strcpy(m_data[x].discription, des);
			}
			qsort(m_data, m_count, sizeof(MON_ELEMENT), compar_index);
			return 1;
		}

		if (!strcmp(label, m_data[x].label))
		{
			printf("MonAPI ERROR: Label already found %s  Id: old(%d) new(%d).\n", label, m_data[x].id, id);
			return 0;
		}
	}

	if (m_max == m_count + 1)
	{
		printf("MonitorObject: max element reached [%d].\n%s\nContact David Taylor (858) 577-3155.\n", m_max, label);
		return 0;
	}
	m_data[m_count].ping = pingValue(ping);
	m_data[m_count].value = 0;
	m_data[m_count].id = id;
	delete[] m_data[m_count].label;
	m_data[m_count].label = new char[strlen(label) + 1];
	strcpy(m_data[m_count].label, label);
	if (des)
	{
		delete[] m_data[x].discription;
		m_data[x].discription = new char[strlen(des) + 1];
		strcpy(m_data[x].discription, des);
	}
	m_count++;
	qsort(m_data, m_count, sizeof(MON_ELEMENT), compar_index);
	return 1;
}

int CMonitorData::setDescription(int Id, const char *Description, int & mode)
{
	int x;

	for (x = 0; x < m_count; x++)
	{
		if (Id == m_data[x].id)
		{
			if (Description == nullptr)
			{
				delete[] m_data[x].discription;
				m_data[x].discription = nullptr;
				mode = 0;
				return x;
			}
			if (m_data[x].discription && !strcmp(m_data[x].discription, Description))
				return -1;

			delete[] m_data[x].discription;
			m_data[x].discription = new char[strlen(Description) + 1];
			strcpy(m_data[x].discription, Description);
			mode = 1;
			return x;
		}
	}
	return -1;
}

int CMonitorData::pingValue(int p)
{
	switch (p)
	{
	case MON_HISTORY: return (60 * 5);
	case MON_ONLY_SHOW:	return 0;
	default:
		printf("MonAPI ERROR: add() function is not using defined constances.\n");
		printf("Please use one of the following:\n");
		printf("\tMON_PING_15\n\tMON_PING_30\n\tMON_PING_60\n\tMON_PING_5MIN\n");
		break;
	}
	return 0;
}

void CMonitorData::set(int Id, int value)
{
	int high, i, low;

	for (low = (-1), high = m_count; high - low > 1; )
	{
		i = (high + low) / 2;
		if (Id <= m_data[i].id)
			high = i;
		else
			low = i;
	}

	if (high < m_count &&  Id == m_data[high].id)
	{
		if (m_data[high].ChangedTime == 0 || m_data[high].value != value) {
			m_data[high].ChangedTime = (long)time(nullptr);
			m_data[high].value = value;
		}
	}
}

void CMonitorData::remove(int Id)
{
	int x;

	if (m_count == 1)
	{
		delete[] m_data[0].label;

		delete[] m_data[0].discription;

		m_data[0].label = 0;
		m_data[0].id = 0;
		m_data[0].value = 0;;
		m_data[0].ping = 0;;
		m_count--;
		return;
	}

	for (x = 0; x < m_count; x++)
	{
		if (Id == m_data[x].id)
		{
			delete[] m_data[x].label;

			delete[] m_data[x].discription;

			m_data[x].label = 0;
			if (x < m_count - 1)
			{
				memcpy(&m_data[x], &m_data[m_count - 1], sizeof(MON_ELEMENT));
			}
			memset(&m_data[m_count - 1], 0, sizeof(MON_ELEMENT));
			m_count--;
			return;
		}
	}
}

int CMonitorData::parseList(char **list, char *data, char tok, int max)
{
	int count;
	int cnt;

	if (data == nullptr)
		return 0;

	list[0] = data;
	count = 1;
	cnt = 0;
	while (cnt < max && *data > 0)
	{
		if (*data == tok)
		{
			*data = 0;
			data++;
			cnt++;
			list[count] = data;
			if (*data)	count++;
		}
		cnt++;
		if (*data)	data++;
	}
	return count;
}

void CMonitorData::dump()
{
	printf("********** Monitor API Dump *******************\n");
	printf("  Version: %d\n", CURRENT_API_VERSION);
	printf("\ncount: %d\n", m_count);
	printf("\t%-40s %8s\t%s\t%s\n", "Label", "Id", "Ping", "Value");
	for (int x = 0; x < m_count; x++)
	{
		printf("\t%-40s % 8d\t%d\t%d\n",
			m_data[x].label,
			m_data[x].id,
			m_data[x].value,
			m_data[x].ping);
	}
	printf("***********************************************\n");
}

/*
int CMonitorData::compress(char *dest,unsigned long *destLen,const char *source,long sourceLen,int level)
{
z_stream stream;
int err;

	stream.next_in = (Bytef*)source;
	stream.avail_in = (uInt)sourceLen;
	stream.next_out = (Bytef *)dest;
	stream.avail_out = (uInt)*destLen;
	if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;

	stream.zalloc = (alloc_func)0;
	stream.zfree = (free_func)0;
	stream.opaque = (voidpf)0;

	err = deflateInit(&stream, level);
	if (err != Z_OK) return err;

	err = deflate(&stream, Z_FINISH);
	if (err != Z_STREAM_END) {
		deflateEnd(&stream);
		return err == Z_OK ? Z_BUF_ERROR : err;
	}
	*destLen = stream.total_out;

	err = deflateEnd(&stream);
	return err;
}
*/

//////////////////////////////////////////////////////////////////////////////
// ------------------   Pack and unpack Routine  -----------------------------
///////////////////////////////////////////////////////////////////////////////

int packString(char *buffer, int & len, char * value)
{
	int size;
	size = (int)strlen(value) + 1;
	memcpy(buffer, value, size);
	len += size;
	return size;
}

int packByte(char *buffer, int & len, char value)
{
	buffer[0] = value;
	len++;
	return 1;
}

int packShort(char *buffer, int & len, short value)
{
	char *p;

	p = (char *)&value;

#ifdef  _sun
	buffer[0] = p[1];
	buffer[1] = p[0];
#else
	buffer[0] = p[0];
	buffer[1] = p[1];
#endif

	len += 2;
	return 2;
}

int packShort(char *buffer, int & len, unsigned short value)
{
	char *p;

	p = (char *)&value;

#ifdef  _sun
	buffer[0] = p[1];
	buffer[1] = p[0];
#else
	buffer[0] = p[0];
	buffer[1] = p[1];
#endif

	len += 2;
	return 2;
}

int unpackShort(char *buffer, int & len, short & value)
{
	char *p;
	p = (char *)&value;

#ifdef _sun
	p[1] = buffer[0];
	p[0] = buffer[1];

#else
	p[0] = buffer[0];
	p[1] = buffer[1];
#endif

	len += 2;
	return 2;
}

int unpackShort(char *buffer, int & len, unsigned short & value)
{
	char *p;
	p = (char *)&value;

#ifdef _sun
	p[1] = buffer[0];
	p[0] = buffer[1];

#else
	p[0] = buffer[0];
	p[1] = buffer[1];
#endif

	len += 2;
	return 2;
}

int unpackByte(char *buffer, int & len, char &value)
{
	value = buffer[0];
	len++;
	return 1;
}
