/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
// net_adhoclan.c

#include "ds.h"

#include "quakedef.h"

#include "net_adhoc_lan.h"

//#define USE_ADHOC_LAN
#ifdef USE_ADHOC_LAN

#include "libwifi/include/lobby.h"
#include "libwifi/include/safe_malloc.h"

static int net_acceptsocket = -1;		// socket for fielding new connections
static int net_controlsocket = -1;
static int net_broadcastsocket = 0;

#define MAX_ADHOC_SOCKETS 5

struct adhoc_socket
{
	bool used;
	bool attached;
	bool broadcast;
	
	unsigned int source_port;
	unsigned int dest_port;
	LPLOBBY_USER endpoint;
} sockets[MAX_ADHOC_SOCKETS];

int maxsockets = MAX_ADHOC_SOCKETS;

void receiveText(unsigned char *data, int length, LPLOBBY_USER from);

//=============================================================================

int adhoclan_Init (void)
{
	if (COM_CheckParm ("-noadhoclan"))
			return -1;
	
	ds_memset(sockets, 0, sizeof(sockets));
	
	LOBBY_Init() ;
	LOBBY_SetStreamHandler(0x0001, receiveText) ;
	
	if ((net_controlsocket = adhoclan_OpenSocket (0)) == -1)
		Sys_Error("adhoclan_init: Unable to open control socket\n");
	
	//the mac is the first six bytes of this struct
	unsigned char *this_ds = (unsigned char *)LOBBY_GetUserByID(USERID_MYSELF);
	
	Con_Printf("Ad-hoc LAN wifi initialised\n\tMAC is %02x:%02x:%02x:%02x:%02x:%02x\n",
			this_ds[0], this_ds[1], this_ds[2], this_ds[3], this_ds[4], this_ds[5]);
	tcpipAvailable = true;
	
	return net_controlsocket;
}

//=============================================================================

void adhoclan_Shutdown (void)
{
	printf("Shutting down ad-hoc LAN driver\n");
	adhoclan_Listen (false);
	adhoclan_CloseSocket (net_controlsocket);
	memset(sockets, 0, sizeof(sockets));
}

//=============================================================================

void adhoclan_Listen (qboolean state)
{
	printf("Setting ad-hoc LAN driver listen mode to %d\n", state);
	// enable listening
	if (state)
	{
		if (net_acceptsocket != -1)
			return;
		if ((net_acceptsocket = adhoclan_OpenSocket (net_hostport)) == -1)
			Sys_Error ("adhoclan_Listen: Unable to open accept socket\n");
		return;
	}

	// disable listening
	if (net_acceptsocket == -1)
		return;
	adhoclan_CloseSocket (net_acceptsocket);
	net_acceptsocket = -1;
}

//=============================================================================

int open_socket(void)
{
	for (int count = 0; count < MAX_ADHOC_SOCKETS; count++)
		if (sockets[count].used == false)
		{
			ds_memset(&sockets[count], 0, sizeof(adhoc_socket));
			sockets[count].used = true;
			return count;
		}
	return -1;
}

int close_socket(int socket)
{
	if ((socket < 0) || (socket >= MAX_ADHOC_SOCKETS))
		Sys_Error("closing invalid socket number, %d\n", socket);
	sockets[socket].used = false;
	
	return 0;
}

int bind_socket(int socket, int port)
{
	if ((socket < 0) || (socket >= MAX_ADHOC_SOCKETS))
		Sys_Error("binding invalid socket number, %d\n", socket);
	sockets[socket].source_port = port;
	sockets[socket].endpoint = LOBBY_GetUserByID(USERID_MYSELF);
	
	return 0;
}

int adhoclan_OpenSocket (int port)
{
	int newsocket = open_socket();
	
	if (newsocket == -1)
		return -1;
	
	if (bind_socket(newsocket, port) == -1)
	{
		close_socket(newsocket);
		return -1;
	}
	
	return newsocket;
}

//=============================================================================

int adhoclan_CloseSocket (int socket)
{
	if (socket == net_broadcastsocket)
		net_broadcastsocket = 0;
	return close_socket (socket);
}

//=============================================================================

int adhoclan_Connect (int socket, struct qsockaddr *addr)
{
	return 0;
}

//=============================================================================

int adhoclan_CheckNewConnections (void)
{
	return net_acceptsocket;
}

//=============================================================================


volatile bool in_read = false;
bool data_available = false;
byte receiveMessage [NET_MAXMESSAGE];
LPLOBBY_USER receive_source;
int receive_length;

void receiveText(unsigned char *data, int length, LPLOBBY_USER from)
{
//	printf("received packet, length %d\n", length);
	
	if (in_read == false)
	{
		receive_source = from;
		receive_length = length;
		data_available = true;
		ds_memcpy(receiveMessage, data, length);
	}
}

int adhoclan_Read (int socket, byte *buf, int len, struct qsockaddr *addr)
{
	int retr = 0;
	in_read = true;
	
	if (data_available)
	{
		printf("read %d b\n", receive_length);
		int actual_length = len < receive_length ? len : receive_length;
		ds_memcpy(buf, receiveMessage, actual_length);
		memset(addr, 0xff, 16);
		memcpy(addr->sa_data, receive_source, 6);
//		printf("packet from %s\n", adhoclan_AddrToString(addr));
		
		data_available = false;
		retr = actual_length;
		
		for (int count = 4; count < 14; count++)
			printf("%c", buf[count]);
		printf("\n");
	}
	
	in_read = false;
	
	return retr;
}

//=============================================================================

int adhoclan_MakeSocketBroadcastCapable (int socket)
{
	printf("setting socket %d to broadcast capable\n", socket);
	sockets[net_broadcastsocket].broadcast = false;
	net_broadcastsocket = socket;
	sockets[socket].broadcast = true;
}

//=============================================================================

int adhoclan_Broadcast (int socket, byte *buf, int len)
{
	int ret;

	if (socket != net_broadcastsocket)
	{
		if (net_broadcastsocket != 0)
			Sys_Error("Attempted to use multiple broadcasts sockets\n");
		
		ret = adhoclan_MakeSocketBroadcastCapable (socket);
		if (ret == -1)
		{
			Con_Printf("Unable to make socket broadcast capable\n");
			return ret;
		}
	}

	return adhoclan_Write (socket, buf, len, NULL);
}

//=============================================================================

int adhoclan_Write (int socket, byte *buf, int len, struct qsockaddr *addr)
{
	if (addr == NULL)
	{
		printf("sending %d bytes to broadcast (%d users)\n", len, LOBBY_GetNumberOfKnownUsers());
		
		for (int count = 0; count < LOBBY_GetNumberOfKnownUsers(); count++)
			LOBBY_SendToUser(LOBBY_GetUserByID(count), 1, buf, len);
	}
	else
	{
		printf("write %d bytes\n", len);
		for (int count = 4; count < 14; count++)
			printf("%c", buf[count]);
		printf("\n");
		LOBBY_SendToUser(LOBBY_GetUserByMAC(addr->sa_data), 1, buf, len);
	}
	
	return len;
}

//=============================================================================
int fake_addrtostring = 0;
char *adhoclan_AddrToString (struct qsockaddr *addr)
{
	static char buffer[22];
	ds_memset(buffer, 0, 22);
	
	sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x",
			addr->sa_data[0], addr->sa_data[1], addr->sa_data[2], addr->sa_data[3], addr->sa_data[4], addr->sa_data[5]);
	return buffer;
}

//=============================================================================

int adhoclan_StringToAddr (char *string, struct qsockaddr *addr)
{
	printf("ad hoc string to addr, string is %s\n", string);
	while(1);
	return 0;
}

int adhoclan_NumToAddr (unsigned long ipaddr, unsigned int port, struct qsockaddr *addr)
{
	printf("adhoc num to addr, ip is %016x, port %d port\n");
	while(1);
	return 0;
}

//=============================================================================

int adhoclan_GetSocketAddr (int socket, struct qsockaddr *addr)
{
	unsigned char *mac_address = (unsigned char *)sockets[socket].endpoint;
	
//	printf("socket addr for socket %d, handle is %08x\n", socket, mac_address);
//	printf("addr is %02x:%02x:%02x:%02x:%02x:%02x\n",
//			mac_address[0], mac_address[1], mac_address[2], mac_address[3], mac_address[4], mac_address[5]);
	
	
	ds_memcpy(addr->sa_data, mac_address, 6);
	return 0;
}

//=============================================================================

int adhoclan_GetNameFromAddr (struct qsockaddr *addr, char *name)
{
	printf("ad hoc get name from addr, %s\n", adhoclan_AddrToString(addr));
	Q_strcpy(name, adhoclan_AddrToString(addr));
	return 0;
}

//=============================================================================

int adhoclan_GetAddrFromName(char *name, struct qsockaddr *addr)
{
	unsigned int bytes[6];
	//printf("ad hoc get addr from name, %s\n", name);
	sscanf(name, "%x:%x:%x:%x:%x:%x",
			&bytes[0], &bytes[1], &bytes[2], &bytes[3], &bytes[4], &bytes[5]);
	
	for (int count = 0; count < 6; count++)
		addr->sa_data[count] = bytes[count];
	
	/*printf("address is %02x:%02x:%02x:%02x:%02x:%02x\n",
			addr->sa_data[0], addr->sa_data[1], addr->sa_data[2], 
			addr->sa_data[3], addr->sa_data[4], addr->sa_data[5]);*/
	return 0;
}

//=============================================================================

int adhoclan_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
{
	register unsigned int lr_r asm ("lr");
	volatile unsigned int lr = lr_r;
	/*printf("adhoc addr compare from %08x\n", lr_r);
	
	for (int outer = 0; outer < 4; outer++)
	{
		for (int count = 0; count < 4; count++)
			printf("%02x ", ((unsigned char *)addr1)[outer * 4 + count]);
		
		printf("\n");
	}
	printf("\n");
	for (int outer = 0; outer < 4; outer++)
	{
		for (int count = 0; count < 4; count++)
			printf("%02x ", ((unsigned char *)addr2)[outer * 4 + count]);
		
		printf("\n");
	}*/
	
	for (int count = 0; count < 6; count++)
		if (addr1->sa_data[count] != addr2->sa_data[count])
			return -1;
	return 0;
}

//=============================================================================

int adhoclan_GetSocketPort (struct qsockaddr *addr)
{
	printf("ad hoc get socket port for\n");
	/*for (int outer = 0; outer < 4; outer++)
	{
		for (int count = 0; count < 4; count++)
			printf("%02x ", ((unsigned char *)addr)[outer * 4 + count]);
		
		printf("\n");
	}
	while(1);*/
	return 0;
}


int adhoclan_SetSocketPort (struct qsockaddr *addr, int port)
{
	printf("ad hoc setting socket port to %d\n", port);
//	while(1);
	return 0;
}

//=============================================================================

#endif		//adhoclan
