/*
 *  Network.h - Network handling
 *
 *  Frodo (C) 2009 Simon Kagstrom
 *
 *  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
 */

#include "sysdeps.h"
#include "Network.h"
#include "Display.h"
#include "Prefs.h"
#include "main.h"
#include "C64.h"

#include "utils.hh"
#include "data_store.hh"
#include "gui/gui.hh"
#include "gui/status_bar.hh"
#include "gui/network_user_menu.hh"
#include "gui/network_server_messages.hh"

#if defined(GEKKO)
# include <wiiuse/wpad.h>
#endif

#define N_SQUARES_W 16
#define N_SQUARES_H 8

#define SQUARE_W (DISPLAY_X / N_SQUARES_W)
#define SQUARE_H (DISPLAY_Y / N_SQUARES_H)

#define SQUARE_TO_X(square) ( ((square) % N_SQUARES_W) * SQUARE_W )
#define SQUARE_TO_Y(square) ( ((square) / N_SQUARES_W) * SQUARE_H )

/* Worst cases for RLE and DIFF */
#define RAW_SIZE  ( (SQUARE_W * SQUARE_H) / 2 )
#define RLE_SIZE  ( RAW_SIZE * 4 + 8)
#define DIFF_SIZE ( RAW_SIZE * 4 + 8)

#if 0
class ConnectionFSM : public TimeoutHandler
{
public:
	ConnectionFSM()
	{
		this->rese();
	}

	void connectToPeer()
	{
		this->setState(CONNECT_TO_PEER);
	}

	void peerConnected()
	{
		/* Reset the FSM */
		this->reset();
	}

	void connectToBroker()
	{
		this->setState(CONNECT_TO_BROKER);

		Gui::gui->timerController->arm(this, 4000);
	}

	void timeoutCallback()
	{
		/* New state, everything is fine */
		if (this->last_state != this->state)
			return;

		switch(state)
		{
		default:
			break;
		}
	}

	void reset()
	{
		this->state = 0;
		this->last_state = -1;
	}

	static ConnectionFSM fsm;

private:

	void setState(int state)
	{
		this->last_state = this->state;
		this->state = state;
	}

	int state, last_state;
};
#endif

Network::Network(const char *remote_host, int port)
{
	const size_t size = NETWORK_UPDATE_SIZE;

	this->InitNetwork();

	this->is_master = true; /* Assume true */
	this->connected = false;

	/* "big enough" buffer */
	this->ud = (NetworkUpdate*)xmalloc( size );
	this->receive_ud = (NetworkUpdate*)xmalloc( size );

	this->ResetNetworkUpdate();
	this->traffic = 0;
	this->last_traffic = 0;
	this->target_kbps = 160000; /* kilobit per seconds */
	this->kbps = 0;

	this->raw_buf = (Uint8*)malloc(RAW_SIZE);
	this->rle_buf = (Uint8*)malloc(RLE_SIZE);
	this->diff_buf = (Uint8*)malloc(DIFF_SIZE);
	assert(this->raw_buf && this->rle_buf && this->diff_buf);
	this->cur_joystick_data = 0;

	/* Go from lower right to upper left */
	this->refresh_square = N_SQUARES_W * N_SQUARES_H - 1;
	this->square_updated = (Uint32*)malloc( N_SQUARES_W * N_SQUARES_H * sizeof(Uint32));
	assert(this->square_updated);
	memset(this->square_updated, 0, N_SQUARES_W * N_SQUARES_H * sizeof(Uint32));

	this->screen = (Uint8 *)malloc(DISPLAY_X * DISPLAY_Y);
	assert(this->screen);

	this->sound_head = this->sound_tail = 0;
	this->sound_last_cycles = SDL_GetTicks();
	memset(this->sound_active, 0, sizeof(this->sound_active));

	/* Assume black screen */
	memset(this->screen, 0, DISPLAY_X * DISPLAY_Y);
	memset(this->screenshot, 0, sizeof(this->screenshot));

	Network::networking_started = true;
	this->peer_selected = -1;
	/* Peer addresses, if it fails we are out of luck */
	panic_if (this->InitSocket() == false,
			"Could not init the socket\n");

	/* Setup the socket addresses */
	memset(&this->peer_addr, 0, sizeof(this->peer_addr));
	memset(&this->server_addr, 0, sizeof(this->server_addr));
	panic_if (this->InitSockaddr(&this->server_addr, remote_host, port) == false,
			"Can't initialize socket address to server\n");

	this->connection_error_message = "Connection OK";
}

Network::~Network()
{
	free(this->ud);
	free(this->receive_ud);
	free(this->square_updated);
	free(this->raw_buf);
	free(this->rle_buf);
	free(this->diff_buf);
	free(this->screen);

	this->CloseSocket();
	this->ShutdownNetwork();
}

void Network::Tick(int ms)
{
	if (ms == 0)
		ms = 1;
	int last_kbps = ((this->traffic - this->last_traffic) * 8) * (1000 / ms);

	/* 1/3 of the new value, 2/3 of the old */
	this->kbps = 2 * (this->kbps / 3) + (last_kbps / 3);
	this->last_traffic = this->traffic;
}

bool Network::DecodeDisplayDiff(struct NetworkUpdate *src,
		int x_start, int y_start)
{
	struct NetworkUpdateDisplay *dp = (struct NetworkUpdateDisplay *)src->data;
	int p = 0;
	int x = x_start;
	int y = y_start;
	int sz = src->size - sizeof(NetworkUpdate) - sizeof(NetworkUpdateDisplay);

	/* Something is wrong if this is true... */
	if (sz % 2 != 0)
		return false;

	while (p < sz)
	{
		Uint8 len = dp->data[p];
		Uint8 color = dp->data[p+1];
		int x_diff = (x - x_start + len) % SQUARE_W;
		int y_diff = (x - x_start + len) / SQUARE_W;

		x = x_start + x_diff;
		y = y + y_diff;
		this->screen[y * DISPLAY_X + x] = color;
		p += 2;
	}

	return true;
}

bool Network::DecodeDisplayRLE(struct NetworkUpdate *src,
		int x_start, int y_start)
{
	struct NetworkUpdateDisplay *dp = (struct NetworkUpdateDisplay *)src->data;
	int p = 0;
	int x = x_start;
	int y = y_start;
	int sz = src->size - sizeof(NetworkUpdate) - sizeof(NetworkUpdateDisplay);

	/* Something is wrong if this is true... */
	if (sz % 2 != 0)
		return false;

	while (p < sz)
	{
		Uint8 len = dp->data[p];
		Uint8 color = dp->data[p+1];

		while (len > 0)
		{
			this->screen[y * DISPLAY_X + x] = color;
			len--;
			x++;
			if ((x - x_start) % SQUARE_W == 0)
			{
				x = x_start;
				y++;
			}
		}
		p += 2;
	}

	return true;
}

bool Network::DecodeDisplayRaw(struct NetworkUpdate *src,
		int x_start, int y_start)
{
	struct NetworkUpdateDisplay *dp = (struct NetworkUpdateDisplay *)src->data;
	const int raw_w = SQUARE_W / 2;

	for (int y = y_start; y < y_start + SQUARE_H; y++)
	{
		for (int x = x_start; x < x_start + SQUARE_W; x += 2)
		{
			Uint8 v = dp->data[(y - y_start) * raw_w + (x - x_start) / 2];
			Uint8 a = v >> 4;
			Uint8 b = v & 0xf;

			this->screen[ y * DISPLAY_X + x ] = a;
			this->screen[ y * DISPLAY_X + x + 1 ] = b;
		}
	}

	return true;
}

bool Network::CompareSquare(Uint8 *a, Uint8 *b)
{
	for (int y = 0; y < SQUARE_H; y++)
	{
		for (int x = 0; x < SQUARE_W; x += 4)
		{
			Uint32 va = *((Uint32*)&a[ y * DISPLAY_X + x ]);
			Uint32 vb = *((Uint32*)&b[ y * DISPLAY_X + x ]);

			if (va != vb)
				return false;
		}
	}

	return true;
}

void Network::EncodeDisplay(Uint8 *master, Uint8 *remote)
{
	for ( int sq = 0; sq < N_SQUARES_H * N_SQUARES_W; sq++ )
	{
		Uint8 *p_master = &master[ SQUARE_TO_Y(sq) * DISPLAY_X + SQUARE_TO_X(sq) ]; 
		Uint8 *p_remote = &remote[ SQUARE_TO_Y(sq) * DISPLAY_X + SQUARE_TO_X(sq) ]; 

		/* Refresh periodically or if the squares differ */
		if ( (this->refresh_square == sq && this->kbps < this->target_kbps * 0.7) ||
				this->CompareSquare(p_master, p_remote) == false)
		{
			NetworkUpdate *dst = (NetworkUpdate *)this->cur_ud;

			/* Updated, encode this */
			this->EncodeDisplaySquare(dst, master, remote, sq,
					this->refresh_square != sq);
			this->AddNetworkUpdate(dst);

			/* This has been refreshed, move to the next one */
			if (this->refresh_square == sq)
			{
				this->refresh_square--;
				if (this->refresh_square < 0)
					this->refresh_square = N_SQUARES_H * N_SQUARES_W - 1;
			}
		}
		else
			this->square_updated[sq] = 0;
	}
	memcpy(remote, master, DISPLAY_X * DISPLAY_Y);
}


size_t Network::EncodeDisplaySquare(struct NetworkUpdate *dst,
		Uint8 *screen, Uint8 *remote, int square,
		bool use_diff)
{
	struct NetworkUpdateDisplay *dp = (struct NetworkUpdateDisplay *)dst->data;
	const int x_start = SQUARE_TO_X(square);
	const int y_start = SQUARE_TO_Y(square);
	Uint8 rle_color = screen[ y_start * DISPLAY_X + x_start ];
	int rle_len = 0, diff_len = 0;
	size_t rle_sz = 0, diff_sz = 0;
	const int raw_w = SQUARE_W / 2;
	int type = DISPLAY_UPDATE_RAW;
	size_t out;

	for (int y = y_start; y < y_start + SQUARE_H; y++)
	{
		memset( &this->raw_buf[(y - y_start) * raw_w], 0, raw_w );

		for (int x = x_start; x < x_start + SQUARE_W; x++)
		{
			Uint8 col_s = screen[ y * DISPLAY_X + x ];
			Uint8 col_r = remote[ y * DISPLAY_X + x ];
			bool is_odd = (x & 1) == 1;
			int raw_shift = (is_odd ? 0 : 4);

			/* Every second is shifted */
			this->raw_buf[ (y - y_start) * raw_w + (x - x_start) / 2 ] |=
				(col_s << raw_shift);

			if (rle_color != col_s ||
					rle_len >= 255)
			{
				this->rle_buf[rle_sz] = rle_len;
				this->rle_buf[rle_sz + 1] = rle_color;
				rle_sz += 2;

				rle_len = 0;
				rle_color = col_s;
			}

			if (col_r != col_s || diff_len >= 255)
			{
				this->diff_buf[diff_sz] = diff_len;
				this->diff_buf[diff_sz + 1] = col_s;
				diff_sz += 2;
				diff_len = 0;
			}

			diff_len++;
			rle_len++;
		}
	}

	/* The last section for RLE */
	if (rle_len != 0)
	{
		this->rle_buf[rle_sz] = rle_len;
		this->rle_buf[rle_sz + 1] = rle_color;

		rle_sz += 2;
	}

	out = RAW_SIZE;
	if (use_diff && (diff_sz < rle_sz && diff_sz < RAW_SIZE))
	{
		memcpy(dp->data, this->diff_buf, diff_sz);
		type = DISPLAY_UPDATE_DIFF;
		out = diff_sz;
	}
	else if (rle_sz < RAW_SIZE)
	{
		memcpy(dp->data, this->rle_buf, rle_sz);
		type = DISPLAY_UPDATE_RLE;
		out = rle_sz;
	}		
	else
		memcpy(dp->data, this->raw_buf, RAW_SIZE);

	/* Setup the structure */
	dp->square = square;
	dst = InitNetworkUpdate(dst, type,
			sizeof(struct NetworkUpdate) + sizeof(struct NetworkUpdateDisplay) + out);
	this->square_updated[square] = out | (type << 16);

	return dst->size;
}

bool Network::DecodeDisplayUpdate(struct NetworkUpdate *src)
{
	struct NetworkUpdateDisplay *dp = (struct NetworkUpdateDisplay *)src->data;
	int square = dp->square;
	const int square_x = SQUARE_TO_X(square);
	const int square_y = SQUARE_TO_Y(square);

	if (src->type == DISPLAY_UPDATE_DIFF)
		return this->DecodeDisplayDiff(src, square_x, square_y);
	else if (src->type == DISPLAY_UPDATE_RAW)
		return this->DecodeDisplayRaw(src, square_x, square_y);
	else if (src->type == DISPLAY_UPDATE_RLE)
		return this->DecodeDisplayRLE(src, square_x, square_y);

	/* Error */
	return false;
}

void Network::EncodeTextMessage(const char *str, bool broadcast)
{
	NetworkUpdate *dst = (NetworkUpdate *)this->cur_ud;
	struct NetworkUpdateTextMessage *tm = (struct NetworkUpdateTextMessage*)dst->data;
	char *p = (char*)tm->data;
	size_t len = strlen(ThePrefs.NetworkName) + strlen(str) + 4;

	tm->flags = broadcast ? NETWORK_UPDATE_TEXT_MESSAGE_BROADCAST : 0;
	len += (len & 3);
	dst = InitNetworkUpdate(dst, TEXT_MESSAGE,
			sizeof(NetworkUpdate) + sizeof(struct NetworkUpdateTextMessage) + len);
	memset(p, 0, len);
	snprintf(p, len - 1, "%s: %s", ThePrefs.NetworkName, str);

	if (broadcast)
	{
		uint8_t *p_dst = (uint8_t *)dst;
		NetworkUpdate *stop = InitNetworkUpdate((NetworkUpdate*)(p_dst + dst->size),
				STOP, sizeof(NetworkUpdate));

		this->MarshalData(dst);
		this->MarshalData(stop);
		this->SendUpdateDirect(&this->server_addr, dst);
	}
	else
		this->AddNetworkUpdate(dst);
}


void Network::EnqueueSound(uint32 linecnt_diff, uint8 adr, uint8 val)
{
	NetworkUpdateSoundInfo *cur = &this->sound_active[this->sound_head];

	cur->adr = adr;
	cur->val = val;
	cur->delay_cycles = linecnt_diff;

	this->sound_head++;

	if (this->sound_head >= NETWORK_SOUND_BUF_SIZE)
		this->sound_head = 0;

	/* Head has reached tail */
	if (this->sound_head == this->sound_tail)
		this->sound_tail = (this->sound_head + 1) % NETWORK_SOUND_BUF_SIZE;
}

void Network::RegisterSidWrite(uint32 linecnt, uint8 adr, uint8 val)
{
	this->EnqueueSound(linecnt - this->sound_last_cycles, adr, val);

	/* Update the cycle counter */
	sound_last_cycles = linecnt;
}

void Network::FlushSound(void)
{
	NetworkUpdate *dst = this->cur_ud;
	NetworkUpdateSound *snd = (NetworkUpdateSound *)dst->data;
	NetworkUpdateSoundInfo *snd_info = snd->info;

	snd->flags = 0;
	snd->n_items = this->sound_head - this->sound_tail;

	if (this->sound_head < this->sound_tail) {
		snd->n_items = NETWORK_SOUND_BUF_SIZE - this->sound_tail + this->sound_head;
		memcpy(snd_info, &this->sound_active[this->sound_tail],
				(NETWORK_SOUND_BUF_SIZE - this->sound_tail) * sizeof(struct NetworkUpdateSoundInfo));
		memcpy(snd_info + NETWORK_SOUND_BUF_SIZE - this->sound_tail,
				&this->sound_active[0],
				this->sound_head * sizeof(struct NetworkUpdateSoundInfo));
	}
	else
	{
		memcpy(snd_info, &this->sound_active[this->sound_tail],
				(this->sound_head - this->sound_tail) * sizeof(struct NetworkUpdateSoundInfo));
	}
	this->sound_tail = this->sound_head;

	this->sound_last_send = SDL_GetTicks();

	InitNetworkUpdate(dst, SOUND_UPDATE, sizeof(NetworkUpdate) +
			sizeof(NetworkUpdateSound) + sizeof(NetworkUpdateSoundInfo) * snd->n_items);
	this->AddNetworkUpdate(dst);
	this->sound_last_cycles = TheC64->linecnt;
}

struct NetworkUpdateSoundInfo *Network::DequeueSound()
{
	struct NetworkUpdateSoundInfo *out;

	if (this->sound_tail == this->sound_head)
		return NULL;
	out = &this->sound_active[this->sound_tail];
	this->sound_tail = (this->sound_tail + 1) % NETWORK_SOUND_BUF_SIZE;

	return out;
}

void Network::EncodeJoystickUpdate(Uint8 v)
{
	struct NetworkUpdate *dst = this->cur_ud;
	struct NetworkUpdateJoystick *j = (NetworkUpdateJoystick *)dst->data;

	if (TheC64->network_connection_type == MASTER || this->cur_joystick_data == v)
		return;
	dst = InitNetworkUpdate(dst, JOYSTICK_UPDATE,
			sizeof(NetworkUpdate) + sizeof(NetworkUpdateJoystick));
	j->val = v;

	this->AddNetworkUpdate(dst);
	this->cur_joystick_data = v;
}

void Network::ResetNetworkUpdate(void)
{
	memset(this->ud, 0, NETWORK_UPDATE_SIZE);
	memset(this->receive_ud, 0, NETWORK_UPDATE_SIZE);

	this->cur_ud = InitNetworkUpdate(this->ud, STOP, sizeof(NetworkUpdate));
}

void Network::DrawTransferredBlocks(SDL_Surface *screen)
{
	const int x_border = (DISPLAY_X - FULL_DISPLAY_X / 2);
	const int y_border = (DISPLAY_Y - FULL_DISPLAY_Y / 2);

	for (int sq = 0; sq < N_SQUARES_W * N_SQUARES_H; sq++)
	{
		int x = SQUARE_TO_X(sq) * 2 - x_border;
		int y = SQUARE_TO_Y(sq) * 2 - y_border;
		int w = SQUARE_W * 2;
		int h = SQUARE_H * 2;

		if (this->square_updated[sq])
		{
			SDL_Rect l = {x,     y,     1, h};
			SDL_Rect r = {x + w, y,     1, h};
			SDL_Rect u = {x,     y,     w, 1};
			SDL_Rect d = {x,     y + h, w, 1};
			Uint32 raw = this->square_updated[sq];
			SDL_Rect size = {x,  y,  2 * ((raw & 0xffff) / 17), 4};
			Uint32 color = 4;

			if ((raw >> 16) == DISPLAY_UPDATE_RLE)
				color = 5;
			else if ((raw >> 16) == DISPLAY_UPDATE_DIFF)
				color = 6;

			SDL_FillRect(screen, &l, 19);
			SDL_FillRect(screen, &r, 19);
			SDL_FillRect(screen, &u, 19);
			SDL_FillRect(screen, &d, 19);

			SDL_FillRect(screen, &size, color);
		}
	}
}

bool Network::ReceiveUpdate()
{
	struct timeval tv;

	memset(&tv, 0, sizeof(tv));
	return this->ReceiveUpdate(this->receive_ud, NETWORK_UPDATE_SIZE, &tv);
}

bool Network::ReceiveUpdate(struct timeval *tv)
{
	return this->ReceiveUpdate(this->receive_ud, NETWORK_UPDATE_SIZE, tv);
}

bool Network::ReceiveUpdate(NetworkUpdate *dst, size_t total_sz,
		struct timeval *tv)
{
	Uint8 *p = (Uint8*)dst;
	size_t sz_left = total_sz;
	size_t received = 0;
	bool has_stop = false;

	if (sz_left <= 0)
		return false;

	/* Receive the header */
	do {
		if (this->Select(this->sock, tv) == false)
			return false;
		/* Only timeout the first run */
		tv = NULL;

		ssize_t actual_sz = this->ReceiveFrom(p, this->sock,
				4096, NULL);
		if (actual_sz <= 0)
			return false;

		received += actual_sz;
		if (ntohs(dst->magic) != FRODO_NETWORK_MAGIC) {
			printf("Packet with wrong magic received\n");
			return false;
		}
		if (this->ScanDataForStop(dst, received) == true)
			break;

		sz_left -= actual_sz;
		p = p + actual_sz;
	} while (!has_stop);

	if (this->DeMarshalAllData(dst, received) == false) {
		printf("Demarshal error\n");
		return false;
	}

	return true;
}


bool Network::SendUpdateDirect(struct sockaddr_in *addr, NetworkUpdate *src)
{
	uint8_t *p = (uint8_t *)src;
	size_t sz;
	ssize_t v;

	sz = ntohl(src->size) + sizeof(NetworkUpdate); /* stop */
	if (sz <= 0)
		return false;

	v = this->SendTo((void*)p, this->sock,
			sz, addr);
	if (v <= 0 || (size_t)v != sz)
		return false;
	this->traffic += sz;

	return true;
}

bool Network::SendUpdate(struct sockaddr_in *addr)
{
	NetworkUpdate *src = this->ud;
	NetworkUpdate *stop = InitNetworkUpdate(this->cur_ud, STOP, sizeof(NetworkUpdate));
	size_t sz;

	/* Nothing to send, that's OK */
	if ( src == stop )
		return true;

	/* Add a stop at the end of the update */
	this->AddNetworkUpdate(stop);

	if (this->MarshalAllData(src) == false)
		return false;

	sz = this->GetNetworkUpdateSize();
	if (sz <= 0)
		return false;
	size_t cur_sz = 0;
	Uint8 *p = (Uint8*)src; 
	do
	{
		size_t size_to_send = this->FillNetworkBuffer((NetworkUpdate*)p);
		ssize_t v;

		v = this->SendTo((void*)p, this->sock,
				size_to_send, addr);
		if (v <= 0 || (size_t)v != size_to_send)
			return false;
		cur_sz += size_to_send;
		p += size_to_send;
	} while (cur_sz < sz);
	this->traffic += cur_sz;

	return true;
}

size_t Network::FillNetworkBuffer(NetworkUpdate *cur)
{
	size_t sz = 0;
	size_t cur_sz;
	int cnt = 0;

	while(1)
	{
		cur_sz = ntohl(cur->size);

		if (sz + cur_sz >= 4096)
			break;

		cnt++;
		sz += cur_sz;
		if (ntohs(cur->type) == STOP)
			break;
		cur = (NetworkUpdate*)((Uint8*)cur + cur_sz);
	}
	assert(sz <= 4096);

	return sz;
}


void Network::AddNetworkUpdate(NetworkUpdate *update)
{
	Uint8 *next = (Uint8*)this->cur_ud + update->size;

	this->cur_ud = (NetworkUpdate*)next;
}

bool Network::MarshalData(NetworkUpdate *p)
{
	switch (p->type)
	{
	case DISPLAY_UPDATE_RAW:
	case DISPLAY_UPDATE_RLE:
	case DISPLAY_UPDATE_DIFF:
	case JOYSTICK_UPDATE:
	case DISCONNECT:
	case CONNECT_TO_PEER:
	case TEXT_MESSAGE:
	case STOP:
		break;
	case BANDWIDTH_PING:
	case BANDWIDTH_ACK:
	case PING:
	case ACK:
	{
		NetworkUpdatePingAck *pa = (NetworkUpdatePingAck *)p->data;
		pa->seq = htonl(pa->seq);
	} break;
	case SELECT_PEER:
	{
		NetworkUpdateSelectPeer *sp = (NetworkUpdateSelectPeer *)p->data;
		sp->server_id = htonl(sp->server_id);
	} break;
	case REGISTER_DATA:
	{
		NetworkUpdateRegisterData *ds = (NetworkUpdateRegisterData *)p->data;

		ds->key = htonl(ds->key);
		ds->metadata = htonl(ds->metadata);
	} break;
	case LIST_PEERS:
	{
		NetworkUpdateListPeers *lp = (NetworkUpdateListPeers *)p->data;
		for (unsigned int i = 0; i < lp->n_peers; i++)
		{
			NetworkUpdatePeerInfo *peer = &lp->peers[i];

			peer->key = htons(peer->key);
			peer->private_port = htons(peer->private_port);
			peer->public_port = htons(peer->public_port);
			peer->is_master = htons(peer->is_master);
			peer->server_id = htonl(peer->server_id);
			peer->version = htonl(peer->version);
			peer->avatar = htons(peer->avatar);
			peer->screenshot_key = htonl(peer->screenshot_key);
		}
		lp->n_peers = htonl(lp->n_peers);
		lp->your_port = htons(lp->your_port);
	} break;
	case CONNECT_TO_BROKER:
	{
		NetworkUpdatePeerInfo *pi = (NetworkUpdatePeerInfo *)p->data;

		/* The rest is simply ignored */
		pi->is_master = htons(pi->is_master);
		pi->key = htons(pi->key);
		pi->version = htonl(pi->version);
	} break;
	case SOUND_UPDATE:
	{
		NetworkUpdateSound *snd = (NetworkUpdateSound *)p->data;
		NetworkUpdateSoundInfo *info = (NetworkUpdateSoundInfo *)snd->info;
		int items = snd->n_items;

		snd->flags = htons(snd->flags);
		snd->n_items = htons(snd->n_items);

		for (int i = 0; i < items; i++)
		{
			NetworkUpdateSoundInfo *cur = &info[i];

			cur->delay_cycles = htons(cur->delay_cycles);
		}
	} break;
	default:
		/* Unknown data... */
		fprintf(stderr, "Got unknown data %d while marshalling. Something is wrong\n",
				p->type);
		return false;
	}

	p->size = htonl(p->size);
	p->magic = htons(p->magic);
	p->type = htons(p->type);

	return true;
}

bool Network::MarshalAllData(NetworkUpdate *ud)
{
	NetworkUpdate *p = ud;

	/* Already marshalled? */
	if (ntohs(p->magic) == FRODO_NETWORK_MAGIC) {
		return true;
	}

	while (p->type != STOP)
	{
		NetworkUpdate *nxt = this->GetNext(p);

		if (this->MarshalData(p) == false)
			return false;
		p = nxt;
	}

	/* The stop tag */
	return this->MarshalData(p);
}

bool Network::DeMarshalData(NetworkUpdate *p)
{
	p->size = ntohl(p->size);
	p->magic = ntohs(p->magic);
	p->type = ntohs(p->type);

	if (p->magic != FRODO_NETWORK_MAGIC)
		return false;

	switch (p->type)
	{
	case DISPLAY_UPDATE_RAW:
	case DISPLAY_UPDATE_RLE:
	case DISPLAY_UPDATE_DIFF:
	case JOYSTICK_UPDATE:
	case DISCONNECT:
	case CONNECT_TO_PEER:
	case TEXT_MESSAGE:
	case STOP:
		/* Nothing to do, just bytes */
		break;
	case BANDWIDTH_PING:
	case BANDWIDTH_ACK:
	case PING:
	case ACK:
	{
		NetworkUpdatePingAck *pa = (NetworkUpdatePingAck *)p->data;
		pa->seq = ntohl(pa->seq);
	} break;
	case SELECT_PEER:
	{
		NetworkUpdateSelectPeer *sp = (NetworkUpdateSelectPeer *)p->data;
		sp->server_id = ntohl(sp->server_id);
	} break;
	case REGISTER_DATA:
	{
		NetworkUpdateRegisterData *ds = (NetworkUpdateRegisterData *)p->data;

		ds->key = ntohl(ds->key);
		ds->metadata = ntohl(ds->metadata);
	} break;
	case LIST_PEERS:
	{
		NetworkUpdateListPeers *lp = (NetworkUpdateListPeers *)p->data;

		lp->n_peers = ntohl(lp->n_peers);
		for (unsigned int i = 0; i < lp->n_peers; i++)
		{
			NetworkUpdatePeerInfo *peer = &lp->peers[i];

			peer->key = ntohs(peer->key);
			peer->private_port = ntohs(peer->private_port);
			peer->public_port = ntohs(peer->public_port);
			peer->is_master = ntohs(peer->is_master);
			peer->server_id = ntohl(peer->server_id);
			peer->version = ntohl(peer->version);
			peer->avatar = ntohs(peer->avatar);
			peer->screenshot_key = ntohl(peer->screenshot_key);
		}
		lp->your_port = ntohs(lp->your_port);
	} break;
	case SOUND_UPDATE:
	{
		NetworkUpdateSound *snd = (NetworkUpdateSound *)p->data;
		NetworkUpdateSoundInfo *info = (NetworkUpdateSoundInfo *)snd->info;

		snd->flags = ntohs(snd->flags);
		snd->n_items = ntohs(snd->n_items);
		for (unsigned int i = 0; i < snd->n_items; i++)
		{
			NetworkUpdateSoundInfo *cur = &info[i];

			cur->delay_cycles = ntohs(cur->delay_cycles);
		}
	} break;
	default:
		/* Unknown data... */
		printf("Got unknown data: %d\n", p->type);
		return false;
	}

	return true;
}

bool Network::DeMarshalAllData(NetworkUpdate *ud, size_t max_size)
{
	NetworkUpdate *p = ud;
	int cnt = 0;
	size_t sz = 0;

	while (ntohs(p->type) != STOP &&
			sz + ntohl(p->size) < max_size)
	{
		if (this->DeMarshalData(p) == false)
			return false;
		sz += p->size;
		cnt++;
		p = this->GetNext(p);
	}

	return this->DeMarshalData(p);
}

bool Network::ScanDataForStop(NetworkUpdate *ud, size_t max_size)
{
	NetworkUpdate *p = ud;
	size_t sz = 0;

	while (ntohs(p->type) != STOP &&
			sz + ntohl(p->size) < max_size)
	{
		size_t cur_sz = ntohl(p->size);

		sz += cur_sz;
		p = (NetworkUpdate*)((Uint8*)p + cur_sz);
	}

	/* The stop tag (maybe) */
	return ntohs(p->type) == STOP;
}


bool Network::DecodeUpdate(C64Display *display, uint8 *js, MOS6581 *dst)
{
	NetworkUpdate *p = this->receive_ud;
	bool out = true;

	while (p->type != STOP)
	{
		if (p->magic != FRODO_NETWORK_MAGIC)
			break;
		switch(p->type)
		{
		case SOUND_UPDATE:
		{
			/* No sound updates _to_ the master */
			if (TheC64->network_connection_type == MASTER)
				break;
			NetworkUpdateSound *snd = (NetworkUpdateSound *)p->data;
			NetworkUpdateSoundInfo *info = (NetworkUpdateSoundInfo *)snd->info;

			for (unsigned int i = 0; i < snd->n_items; i++)
			{
				NetworkUpdateSoundInfo *cur = &info[i];

				this->EnqueueSound(cur->delay_cycles, cur->adr, cur->val);
			}
		} break;
		case DISPLAY_UPDATE_RAW:
		case DISPLAY_UPDATE_RLE:
		case DISPLAY_UPDATE_DIFF:
			/* No screen updates _to_ the master */
			if (TheC64->network_connection_type == MASTER)
				break;
			if (this->DecodeDisplayUpdate(p) == false)
				out = false;
			break;
		case JOYSTICK_UPDATE:
			/* No joystick updates _from_ the master */
			if (js && TheC64->network_connection_type == MASTER)
			{
				NetworkUpdateJoystick *j = (NetworkUpdateJoystick *)p->data;
				*js = j->val;
			}
			break;
		case TEXT_MESSAGE:
		{
			NetworkUpdateTextMessage *tm = (NetworkUpdateTextMessage*)p->data;

			Gui::gui->status_bar->queueMessage((const char*)tm->data);
			/* Queue up text message */
			if (tm->flags & NETWORK_UPDATE_TEXT_MESSAGE_BROADCAST)
				Gui::gui->server_msgs->queueMessage((const char *)tm->data);
		} break;
		case REGISTER_DATA:
		{
			NetworkUpdateRegisterData *rd = (NetworkUpdateRegisterData *)p->data;

			DataStore::ds->registerNetworkData(rd->key, rd->metadata, rd->data,
					p->size - (sizeof(NetworkUpdateRegisterData) + sizeof(NetworkUpdate)));
		} break;
		case BANDWIDTH_PING:
		case PING:
		{
			NetworkUpdatePingAck *ping = (NetworkUpdatePingAck *)p->data;
			uint16 type = ACK;

			if (p->type == BANDWIDTH_PING)
				type = BANDWIDTH_ACK;
			this->SendPingAck(&this->server_addr, ping->seq, type, p->size);
		} break;
		case LIST_PEERS:
		{
			NetworkUpdateListPeers *lp = (NetworkUpdateListPeers *)p->data;

			if (lp->n_peers == 1 && (lp->flags & NETWORK_UPDATE_LIST_PEERS_IS_CONNECT))
			{
				NetworkUpdatePeerInfo *pi = &lp->peers[0];
				const char *hostname;

				Gui::gui->status_bar->queueMessage("Connecting to peer...");
				hostname = ip_to_str(pi->public_ip);
				this->InitSockaddr(&this->peer_addr, hostname, pi->public_port);
				free((void*)hostname);

				/* Twice for luck (network equipment might drop these) */
				this->ConnectToPeer();
				SDL_Delay(500);
				this->ConnectToPeer();

				this->is_master = true;
				TheC64->network_connection_type = MASTER;
				break;
			}

			for (unsigned i = 0; i < lp->n_peers; i++)
			{
				if (lp->peers[i].version != FRODO_NETWORK_PROTOCOL_VERSION)
				{
					warning("Peer %d has wrong version: %d vs %d\n",
							i, lp->peers[i].version, FRODO_NETWORK_PROTOCOL_VERSION);
					break;
				}
			}
			if (lp->n_peers == 0)
			{
				Gui::gui->status_bar->queueMessage("No peers, waiting for connection...");
				this->is_master = true;
				break;
			}
			/* FIXME! Not necessarily true! */
			this->is_master = false;

			Gui::gui->status_bar->queueMessage("Got list of peers");
			Gui::gui->nuv->setPeers(lp);
			Gui::gui->activate();
			Gui::gui->pushView(Gui::gui->nuv);
		} break;
		case CONNECT_TO_PEER:
			Gui::gui->status_bar->queueMessage("Connected to peer!");
			if (this->is_master)
				TheC64->network_connection_type = MASTER;
			else
				TheC64->network_connection_type = CLIENT;
			break;
		case BANDWIDTH_ACK:
		case ACK:
			/* We won't receive this, but it also doesn't really matter */
			break;
		case DISCONNECT:
			printf("Got disconnect\n");
			TheC64->network_connection_type = NONE;
			this->Disconnect();
			return false;
		default:
			break;
		}
		p = this->GetNext(p);
	}

	return out;
}

bool Network::AppendScreenshot(NetworkUpdatePeerInfo *pi)
{
	NetworkUpdateRegisterData *dsu;
	NetworkUpdate *ud;
	SDL_Surface *scr;
	void *png;
	size_t sz;
	bool out = NULL;

	scr = Gui::gui->screenshot;
	if (!scr)
		goto out;

	png = sdl_surface_to_png(scr, &sz);
	if (!png)
		goto out;
	if ((sz & 3) != 0)
		sz += 4 - (sz & 3);

	ud = InitNetworkUpdate(this->cur_ud, REGISTER_DATA,
			sizeof(NetworkUpdate) + sizeof(NetworkUpdateRegisterData) + sz);
	dsu = (NetworkUpdateRegisterData *)ud->data;
	dsu->key = DataStore::ds->getNextKey();
	dsu->metadata = 0;
	memcpy(dsu->data, png, sz);
	this->AddNetworkUpdate(ud);

	out = true;
	free(png);

out:
	return out;
}

bool Network::ConnectToBroker()
{
	NetworkUpdate *ud = InitNetworkUpdate(this->cur_ud, CONNECT_TO_BROKER,
			sizeof(NetworkUpdate) + sizeof(NetworkUpdatePeerInfo));
	NetworkUpdatePeerInfo *pi = (NetworkUpdatePeerInfo *)ud->data;
	bool out;

	Gui::gui->status_bar->queueMessage("Connecting to broker...");
	/* Reset peer selection */
	this->peer_selected = -1;

	pi->key = ThePrefs.NetworkKey;
	pi->version = FRODO_NETWORK_PROTOCOL_VERSION;
	pi->avatar = ThePrefs.NetworkAvatar;
	pi->region = ThePrefs.NetworkRegion;
	pi->screenshot_key = 0;

	strcpy((char*)pi->name, ThePrefs.NetworkName);
	this->AddNetworkUpdate(ud);
	out = this->AppendScreenshot(pi);
	if (out)
		out = this->SendServerUpdate();
	this->ResetNetworkUpdate();

	return out;
}

void Network::SendPingAck(struct sockaddr_in *addr, int seq, uint16 type, size_t size_to_send)
{
	NetworkUpdate *ud = InitNetworkUpdate(this->ud, type,
			sizeof(NetworkUpdate) + sizeof(NetworkUpdatePingAck) + size_to_send);
	NetworkUpdatePingAck *p = (NetworkUpdatePingAck*)ud->data;
	uint8_t *p_next = (uint8_t *)ud + sizeof(NetworkUpdate) + sizeof(NetworkUpdatePingAck) + size_to_send;
	NetworkUpdate *stop = InitNetworkUpdate((NetworkUpdate *)p_next, STOP, sizeof(NetworkUpdate));

	p->seq = seq;

	this->MarshalData(ud);
	this->MarshalData(stop);

	this->SendUpdateDirect(addr, ud);
}

bool Network::SelectPeer(uint32 id)
{
	NetworkUpdate *ud = InitNetworkUpdate(this->ud, SELECT_PEER,
			sizeof(NetworkUpdate) + sizeof(NetworkUpdateSelectPeer));
	NetworkUpdateSelectPeer *p = (NetworkUpdateSelectPeer*)ud->data;
	bool out;

	p->server_id = id;
	this->AddNetworkUpdate(ud);
	out = this->SendServerUpdate();
	this->ResetNetworkUpdate();

	return out;		
}

bool Network::SelectPeer(const char *hostname, uint16_t port, uint32_t server_id)
{
	if (!hostname)
	{
		this->peer_selected = 0;
		return true;
	}

	this->SelectPeer(server_id);
	this->InitSockaddr(&this->peer_addr, hostname, port);
	this->is_master = false;
	this->peer_selected = 1;

	return true;
}


bool Network::ConnectToPeer()
{
	NetworkUpdate *ud = InitNetworkUpdate(this->ud, CONNECT_TO_PEER,
			sizeof(NetworkUpdate));
	bool out;

	this->AddNetworkUpdate(ud);
	out = this->SendPeerUpdate();
	this->ResetNetworkUpdate();

	return out;
}

void Network::Disconnect()
{
	Gui::gui->status_bar->queueMessage("Disconnecting");

	this->ResetNetworkUpdate();
	NetworkUpdate *disconnect = InitNetworkUpdate(this->cur_ud, DISCONNECT,
			sizeof(NetworkUpdate));

	/* Add a stop at the end of the update */
	this->AddNetworkUpdate(disconnect);

	this->SendPeerUpdate();
	this->SendServerUpdate();
}


bool Network::networking_started = false;

#if defined(GEKKO)
#include "NetworkWii.h"
#else
/* BSD-style sockets */
#include "NetworkUnix.h"
#endif
