// Emacs style mode select -*- C++ -*-
//---------------------------------------------------------------------------
//
// $Id: swasynio.c,v 1.4.2.1 2003/06/08 18:16:38 fraggle Exp $
//
// Copyright(C) 1984-2000 David L. Clark
// Copyright(C) 2001-2003 Simon Howard
//
// 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.
//
//---------------------------------------------------------------------------
//
//        swasynio -      SW asynchrounous communications I/O
//
//---------------------------------------------------------------------------

#include <ctype.h>

#include "tcpcomm.h"
#include "timer.h"
#include "video.h"

#include "sw.h"
#include "swasynio.h"
#include "swdisp.h"
#include "swend.h"
#include "swgames.h"
#include "swinit.h"
#include "swmain.h"
#include "swmisc.h"
#include "swmove.h"
#include "swsound.h"
#include "swtitle.h"

asynmode_t asynmode;
char asynhost[128];

static int lastkey = 0;		/*  Always behind one character     */

static int timeout_time;

static void settimeout(int ms)
{
	timeout_time = Timer_GetMS() + ms;
}

static BOOL timeout()
{
	return Timer_GetMS() >= timeout_time;
}

static inline void sendshort(int s)
{
	commout(s & 0xff);
	commout((s >> 8) & 0xff);
}

static inline int readshort()
{
	int s, t;

	while ((s = commin()) < 0) {
		if (timeout()) {
			fprintf(stderr, "readshort: timeout on read\n");
			exit(-1);
		}
	}

	while ((t = commin()) < 0) {
		if (timeout()) {
			fprintf(stderr, "readshort: timeout on read\n");
			exit(-1);
		}
	}

	return (t << 8) + s;
}

static int asynin()
{
	register int c;

	settimeout(500);

        for (;;) {
		if ((c = commin()) >= 0)
			return c;
		if (ctlbreak())
			swend(NULL, NO);
		if (timeout())
			return -1;
	}
}


int asynget(OBJECTS * ob)
{
	register int key;

	if (ob->ob_index == player) {
		key = lastkey;
		lastkey = 0;
	} else {
		settimeout(1000);
		key = readshort();
	}

	return key;
}

void asynput(int movekey)
{
	static BOOL first = TRUE;
	
	if (first)
		first = FALSE;
	else 

	swflush();

	sendshort(movekey);
}

char *asynclos(BOOL update)
{
	commterm();
	return NULL;
}


// this function is called by the multiplayer planes

BOOL movemult(OBJECTS * obp)
{
	register OBJECTS *ob;
	int cmd;

	plyrplane = compplane = FALSE;

	endstat = endsts[currobx = (ob = obp)->ob_index];

	cmd = asynget(ob);
	interpret(ob, cmd);

	/*
	if (dispcnt) {
		ob->ob_flaps = 0;
		ob->ob_bombing = FALSE;
	}
	*/

	if ((ob->ob_state == CRASHED || ob->ob_state == GHOSTCRASHED)
	    && ob->ob_hitcount <= 0) {
		if (ob->ob_life > QUIT) {
			// sdh 25/10/2001: infinite lives in multiplayer
			//++ob->ob_crashcnt;
			initpln(ob);
		}
	}

	return movepln(ob);
}

#define PROTOHEADER "SDLSOPWITH" VERSION

static void synchronize()
{
	// check for header

	// send the header ourselves first

	char *buf;
	char *p;

	buf = malloc(sizeof(PROTOHEADER) + 5);
	sprintf(buf, PROTOHEADER "%i", player);

	for (p = buf; *p; ++p)
		commout(*p);

	// now listen for response

	sprintf(buf, PROTOHEADER "%i", !player);

	settimeout(2000);

	for (p = buf; *p;) {
		int c;

		if (timeout()) {
			fprintf(stderr,
				"synchronize: timeout on connect\n");
			exit(-1);
		}

		c = commin();

		if (c >= 0) {
			if (c == *p) {
				++p;
			} else {
				fprintf(stderr,
					"synchronize: invalid protocol header received!\n");
				exit(-1);
			}
		}
	}

	free(buf);

	if (player) {
		settimeout(1000);
		explseed = readshort();

		printf("random seed: %i\n", explseed);

		settimeout(1000);
		conf_missiles = readshort() != 0;
		conf_wounded = readshort() != 0;
		conf_animals = readshort() != 0;
	} else {
		// send settings
		sendshort(explseed);

		printf("random seed: %i\n", explseed);

		sendshort(conf_missiles);
		sendshort(conf_wounded);
		sendshort(conf_animals);
	}
}

// setup tcp loop

static void tcploop_connect()
{
	int time;

	clrprmpt();
	swputs("  Attempting to connect to\n  ");
	swputs(asynhost);
	swputs(" ...");
	Vid_Update();
	
	commconnect(asynhost);

	clrprmpt();
	swputs("  Connected, waiting for other player\n");
	Vid_Update();

	// for the first 5 seconds, listen to see if theres another player 
	// there

	for (time = Timer_GetMS() + 5000; Timer_GetMS() < time; ) {
		int c;

		if (ctlbreak()) {
			fprintf(stderr, "tcploop_connect: user aborted\n");
			exit(-1);
		}
		
		c = commin();

		if (c >= 0) {
			if (c == '?') {
				commout('!');
				player = 1;
				return;
			} else {
				fprintf(stderr, 
					"tcploop_connect: invalid"
					"char recieved\n");
				exit(-1);
			}
		}
	}

	// now send a ? every second until we get a ! response

	time = Timer_GetMS() + 1000;

        for (;;) {
		int c;

		if (ctlbreak()) {
			fprintf(stderr, "tcploop_connect: user aborted\n");
			exit(-1);
		}

		c = commin();

		if (c >= 0) {
			if (c == '!') {
				player = 0;
				return;
			} else {
				fprintf(stderr, 
					"tcploop_connect: invalid"
					"char recieved\n");
				exit(-1);
			}
		}
	

		if (Timer_GetMS() > time) {
			commout('?');
			time = Timer_GetMS() + 1000;
		}
	}
}

// setup connection

static void asyninit()
{
	if (asynmode == ASYN_TCPLOOP) {
		tcploop_connect();
	} else if (asynmode == ASYN_LISTEN) {
		swtitln();
		clrprmpt();
		swputs("  Listening for connection...");
		Vid_Update();
		commlisten();
		player = 0;
	} else if (asynmode == ASYN_CONNECT) {
		swtitln();
		clrprmpt();
		swputs("  Attempting to connect to \n  ");
		swputs(asynhost);
		swputs(" ...");
		Vid_Update();
		commconnect(asynhost);
		player = 1;
	} else {
		fprintf(stderr, "unknown asynmode mode\n");
		exit(-1);
	}
}

void init1asy()
{
#ifndef TCPIP
	fprintf(stderr, "TCP/IP support not compiled into binary!\n");
	return;
#else
	asyninit();
	clrprmpt();
	swputs("        Waiting for other player");
	synchronize();

	currgame = &swgames[0];
#endif
}



void init2asy()
{
	register OBJECTS *ob;
	OBJECTS *initpln();

	if (!player)
		initplyr(NULL);

	ob = initpln(NULL);
	ob->ob_drawf = dispmult;
	ob->ob_movef = movemult;
	ob->ob_clr = ob->ob_index % 2 + 1;
	ob->ob_owner = ob;
	ob->ob_state = FLYING;
	oobjects[ob->ob_index] = *ob;

//	movmem(ob, &oobjects[ob->ob_index], sizeof(OBJECTS));

	if (player)
		initplyr(NULL);
}

//---------------------------------------------------------------------------
//
// $Log: swasynio.c,v $
// Revision 1.4.2.1  2003/06/08 18:16:38  fraggle
// Fix networking and some compile bugs
//
// Revision 1.4  2003/04/05 22:55:11  fraggle
// Remove the FOREVER macro and some unused stuff from std.h
//
// Revision 1.3  2003/04/05 22:44:04  fraggle
// Remove some useless functions from headers, make them static if they
// are not used by other files
//
// Revision 1.2  2003/04/05 22:31:29  fraggle
// Remove PLAYMODE_MULTIPLE and swnetio.c
//
// Revision 1.1.1.1  2003/02/14 19:03:08  fraggle
// Initial Sourceforge CVS import
//
//
// sdh 14/2/2003: change license header to GPL
// sdh 26/03/2002: changed CGA_ to Vid_
// sdh 16/11/2001: TCPIP #define to disable TCP/IP support
// sdh 29/10/2001: harrykeys
// sdh 21/10/2001: rearranged file headers, added cvs tags
// sdh 21/10/2001: reformatted with indent, adjusted some code by hand
//                 to make more readable
// sdh 20/10/2001: rearranged netgame syncronisation, take game settings
//                 from the first (listening) player
// sdh 19/10/2001: removed all externs, this is done with headers now
// sdh 18/10/2001: converted all functions to ansi-style arguments
//
// 96-12-26        Remove IMAGINET network card address in "multaddr".
// 87-03-12        Allow asynch loopback for debugging.
// 87-03-09        Microsoft compiler.
// 85-04-03        Development
//
//---------------------------------------------------------------------------

