/*	$Id: play.c,v 1.20 2005/06/22 06:24:47 jlanawalt Exp $	*/
/***********************************************************************
	play.c
	Funcion que se encarga de poner las reglas al juego y todo eso
************************************************************************/
#include <config.h>
#include <gnome.h>
#include <unistd.h>
#include <errno.h>
#include "protocol.h"	
#include "server.h"
#include "g_interface.h"
#include "parser.h"
#include "version.h"
#include "gbnserver.h"
#include "bnwrite.h"
#include "net.h"
#include "batnavggz.h"

/* state names */
struct {
	char* nombre;
} st_nombres[] =
{
	{ N_("Disconnected") },
	{ N_("Connected") },
	{ N_("Ready to play") },
	{ N_("Playing") },
	{ N_("Playing & Turn") },
	{ N_("Game Over") }
};

/* that player */
/* find player by file descriptor (socket) */
static int quejugador( int fd )
{
	int i ;
	for(i=0;i<MAXPLAYER;i++) {
		if(usuario.nro_fd[i]==fd )
			break;
	}
	if( i>=MAXPLAYER ) {
		printf(_("gbnserver: Error in quejugador().\n"));
		return -1;
	}
	return i;
}


/************************************************************************
			token_* funciones
*************************************************************************/
static void token_numjug( int fd )
{
	bnwrite(fd,
		BN_REM":"BNVERSION" (c) 1995,2001 Ricardo Calixto Quesada\n"
		BN_REM":Escriba 'help' para mas ayuda\n"
		BN_NUMJUG"=%i",quejugador(fd)
	);
}
static void token_message( int fd, char *str )
{
	int j;
	j=quejugador(fd);
	if(j<0 || j>=MAXPLAYER)
		return;
	broadcast( BN_MESSAGE"=<%s> %s",usuario.names[j],str);
}
static void token_robot( int fd, char *str )
{
	/* deprecated. the robot is never launched in server anymore */
#if 0
	int j;

	j=quejugador(fd);
	if(j<0 || j>=MAXPLAYER)
		return;

	gnome_execute_shell(NULL,"gbnrobot");
#endif
}
static int token_fire_aux( int nj, char *str )
{
	int i,j,x,y,z;
	PARSER p;
	DELIM igualador={ '=','=','=' };
	DELIM separador={ '/',',','/' };
	
	p.igualador = &igualador;
	p.separador = &separador;
	strncpy(p.sig,str,sizeof(p.sig));
	j=0;
	do{
		i=parser_init( &p );
		if(p.status && j==0)		/* coordenada X */
			x=atoi(p.token);

		else if(p.status && j==1)	/* coordenada Y */
			y=atoi(p.token);
		j++;
	} while(i);
	if(j!=2) {
		return -1;
	}
	if(x<0 || x>9 || y<0 || y>9 ) {
		return -1;
	}

	for(i=0;i<MAXPLAYER;i++) {
		if(usuario.nro_tot[i]==PLAY || usuario.nro_tot[i]==PERDIO) { 
			/* if user.state is play or whatever perdio is
			 * (stopped/waiting?)
			 */
			if(!eshundido(i,x,y)) {
				/* if it's not sunk */
				if(usuario.table[i].p[x][y]>=BARCO) {
					z=TOCADO;
					usuario.table[i].p[x][y]=TOCADO;
				} else {
					z=AGUA;
					usuario.table[i].p[x][y]=AGUA;
				}

				/* send to all clients an echo of the BN_FIRE
				 * with a status of touched or water
				 */
				bnwrite(usuario.nro_fd[i],BN_FIRE"=%i,%i,%i",x,y,z);
			}
		}
	}
	return 0;
}
static void token_fire( int fd, char *str )
{
	int j,i;
	j=quejugador(fd);
	if(j<0 || j>=MAXPLAYER)
		return;

	if(usuario.nro_tot[j]!=TURN) {
		return;
	}
	if( token_fire_aux( j, str )!=0 )
		return;	

	usuario.nro_tot[j]=PLAY;
	say_in_clist( j, C_STATUS, _(st_nombres[PLAY].nombre) );
	
	for(i=j+1;i<MAXPLAYER;i++) {
		if(usuario.nro_tot[i]==PLAY)
			break;
	}
	if(i==MAXPLAYER) {
		for(i=0;i<j+1;i++) {
			if(usuario.nro_tot[i]==PLAY)
				break;
		}
	}
	if( i==j ) { /* si el turno es para el mismo, entonces gano*/
		/* if the turn is for the same one, then I win */
		broadcast(BN_WIN"=%i",i);
		broadcast(BN_GAME_OVER);

		for(i=0;i<MAXPLAYER;i++) {
			if(usuario.nro_tot[i]>=CONNEC) { 
				usuario.nro_tot[i]=CONNEC;
				say_in_clist( i, C_STATUS, _(st_nombres[CONNEC].nombre) );
				usuario.hits[i]=0;
				ctable(i);
			}
		}
	} else { /* el turno no es para el mismo */
		/* the turn is not for the same one */
		usuario.nro_tot[i]=TURN; /* siguiente turno / following turn */
		say_in_clist( i, C_STATUS, _(st_nombres[TURN].nombre) );
		broadcast(BN_TURN"=%i",i);
	}
}
static void token_read_aux( char *outbuf, int jug ) 
{
	int i,x,y;
	x=0;y=0;
	for(i=0;i<100;i++) {
		if(usuario.table[jug].p[x][y]==BARCO)
			outbuf[i]=NOBARCO;
		else
			outbuf[i]=usuario.table[jug].p[x][y];
		x++;
		if(x>=10) {
			x=0;
			y++;
		}
	}
}
static void token_read( int fd, char *str)
{
	int j,i;
	char outbuf[10*10+1];

	j=quejugador(fd);
	if(j<0 || j>=MAXPLAYER)
		return;

	i=atoi(str);

	if(!(i>=0 && i<MAXPLAYER)) {
		bnwrite(fd,BN_READ"=-1,-1");
		return;
	}

	if( usuario.nro_tot[i]>=PLAY || usuario.nro_tot[i]<PERDIO ) {
		token_read_aux(outbuf,i);
		outbuf[100]=0;
		bnwrite(fd,BN_READ"=%i,%s",i,outbuf);
	}
	/*
	Me pidio leer por un pibe que no esta jugando con board
	De esta manera le digo (sobretodo a los robots) que me pidio leer
	por uno que no va.
	 *
	 * Ordered to read to me by pibe that this playing with board 
	 * This way I do not say to him (coverall to robots) that 
	 * ordered to read to me by which it does not go.
	*/
	else {
		bnwrite(fd,BN_READ"=-1,-1");
	}	
}
static void token_status( int fd )
{
	int i,j;
	char outbuf[PROT_MAX_LEN]="";
	char out2[PROT_MAX_LEN];

	j=quejugador(fd);
	if(j<0 || j>=MAXPLAYER)
		return;

	for(i=0;i<MAXPLAYER;i++) {
		if(i==MAXPLAYER-1)
			sprintf(out2,"%i,%i,%s",i,usuario.nro_tot[i],usuario.names[i]);
		else
			sprintf(out2,"%i,%i,%s/",i,usuario.nro_tot[i],usuario.names[i]);
		strcat(outbuf,out2);
	}
		
	bnwrite(usuario.nro_fd[j],BN_STATUS"=%s",outbuf);
}

/* si fd==-1, es un mensaje directo del server */
/* it is a direct message of the server */
void token_start( int fd )
{
	int i,j,r,cant;
	char outbuf[PROT_MAX_LEN]="";
	char out2[PROT_MAX_LEN];

	if( fd!=-1) {
		j=quejugador(fd);
		if(j<0 || j>=MAXPLAYER)
			return;
	}

	/* Solo puede empezar el juego un BOARD */
	/* Single a BOARD can begin the game */
	if(fd !=-1 && usuario.nro_tot[j]!=BOARD) return;

	cant=0; 
	for(i=0;i<MAXPLAYER;i++) {
		if(usuario.nro_tot[i]==BOARD)
			cant++;
	}

	if(cant<2)  { /* Se requieren al menos 2 jugadores */
		/* At least 2 players require themselves */
		if(fd!=-1)
			bnwrite(usuario.nro_fd[j],BN_SOL);
		return;
	}
		
	for(i=0;i<MAXPLAYER;i++) {
		if(i==MAXPLAYER-1)
			sprintf(out2,"%i,%i,%s",i,usuario.nro_tot[i],usuario.names[i]);
		else
			sprintf(out2,"%i,%i,%s/",i,usuario.nro_tot[i],usuario.names[i]);
		strcat(outbuf,out2);
	}
		
	for(i=0;i<MAXPLAYER;i++) { /* Cambia el estado de BOARD a PLAY */
		/* It changes the state of BOARD to PLAY */
		if(usuario.nro_tot[i]==BOARD) { 
			bnwrite(usuario.nro_fd[i],BN_START"=%s",outbuf);
			usuario.nro_tot[i]=PLAY; /* started */
			say_in_clist( i, C_STATUS, _(st_nombres[PLAY].nombre) );
		}
	}

	r = (int) (((float)cant)*rand() / (RAND_MAX+1.0));
	j=0;
	for(i=0;i<MAXPLAYER;i++) { /*  Asigna el turno al siguiente */
		/* It assigns the turn the following one */
		if(usuario.nro_tot[i]==PLAY) {
			if(r == j++) {
				bnwrite(usuario.nro_fd[i],BN_TURN"=%i",i);
				usuario.nro_tot[i]=TURN;
				say_in_clist( i, C_STATUS, _(st_nombres[TURN].nombre) );
				break; /* started */
			}
		}
	}
}

void token_exit( int fd )
{
	int i,j,k,temp;
	int num_jug;

	num_jug=quejugador(fd);
	if( num_jug<0 || num_jug >= MAXPLAYER)
		return;


	temp=usuario.nro_tot[num_jug];

	borrar_jugador( num_jug );

	if( temp >= PLAY && temp < PERDIO )
		broadcast(BN_DISCON"=%i",num_jug); 
	
	if(temp<=BOARD)
		return;
	
	j=0;
	for(k=0;k<MAXPLAYER;k++) {
		if( usuario.nro_tot[k]==PLAY || usuario.nro_tot[k]==TURN ) {
			j++;
			i=k;
		}
	}
	if(j==1) {
		/* Entonces solo queda un jugador... por ende el ganador */
		/* Then single a player has left... therefore the winner */
		broadcast(BN_WIN"=%i",i);
		broadcast(BN_GAME_OVER);

		/* Se acabo el juego, limpiar tableros... */
		/* I finish the game, to clean boards... */
		for(i=0;i<MAXPLAYER;i++) {
			if(usuario.nro_tot[i]>=CONNEC) {  
				/* Si el usuario estaba conectado limpiar todo */
				/* If the user were connected to clean everything */
				usuario.nro_tot[i]=CONNEC;
				say_in_clist( i, C_STATUS, _(st_nombres[CONNEC].nombre) );
				usuario.hits[i]=0;
				ctable(i);
			}
		}
		return;
	}

	/* El turno al siguiente */
	/* The turn to the following one */
	if(temp==TURN) {         
		for(i=num_jug;i<MAXPLAYER;i++) { 
			if(usuario.nro_tot[i]==PLAY )
				break;
		}
		if(i==MAXPLAYER) { 
			for(i=0;i<MAXPLAYER;i++) {
				if(usuario.nro_tot[i]==PLAY)
				break;
			}
		}
		if(i!=MAXPLAYER) {       
			usuario.nro_tot[i]=TURN;                     
			say_in_clist( i, C_STATUS, _(st_nombres[TURN].nombre) );
			bnwrite(usuario.nro_fd[i],BN_TURN"=%i",i);
		}
	}
}

static void token_send( int fd, char *str )
{
	int i,j;

	j=quejugador(fd);
	if(j<0 || j>=MAXPLAYER)
		return;

	if( usuario.nro_tot[j]!=CONNEC ) {
		bnwrite(fd,BN_CANT_MODIFY_BOARD);
		return;
	}

	for(i=0;i<MAXPLAYER;i++) {
		if( usuario.nro_tot[i]==PLAY || usuario.nro_tot[i]==TURN ) {
			bnwrite(fd,BN_WAIT);
			return;
		}
	}

	if( wtable(j,str)==TRUE)  {
		usuario.nro_tot[j]=BOARD;
		say_in_clist( j, C_STATUS, _(st_nombres[BOARD].nombre) );
		bnwrite(fd,BN_BOARD_OK);
		broadcast(BN_READY_TO_PLAY"=%i",j);
	} else {
		bnwrite(fd,BN_BOARD_NOT_OK);
	}
}

static void token_name( int fd, char *str )
{
	int j;
	
	j=quejugador(fd);
	if(j<0 || j>=MAXPLAYER)
		return;

	strncpy(usuario.names[j],str,MAXNAMELEN-1);
#ifdef WITH_GGZ
	if(usuario.with_ggz)
		batnavggz_find_ggzname(fd,usuario.names[j],sizeof(usuario.names[j])-1);
#endif /* WITH_GGZ */

	say_in_clist(j,C_NAME,usuario.names[j]);
}

static void token_client_version( int fd, char *str )
{
	int j;
	
	j=quejugador(fd);
	if(j<0 || j>=MAXPLAYER)
		return;

	say_in_clist(j,C_CLIVER,str);
}

static void token_server_version( int fd )
{
	int j;
	
	j=quejugador(fd);
	if(j<0 || j>=MAXPLAYER)
		return;

	bnwrite(fd,BN_SER_VER"=%s",BATVER);
}

static void token_help ( int fd )
{
	int j;

	j=quejugador(fd);
	if(j<0 || j>=MAXPLAYER)
		return;

	bnwrite(fd,
		BN_REM":"BNVERSION"\n"
		BN_REM":Available commands:\n"
		BN_REM":"BN_READ"\n"
		BN_REM":"BN_NUMJUG"\n"
		BN_REM":"BN_STATUS"\n"
		BN_REM":"BN_START"\n"
		BN_REM":"BN_ROBOT"\n"
		BN_REM":"BN_SEND"\n"
		BN_REM":"BN_NAME
	);
}

/*************************************************************************
			fin de token
			codigo de interpretacion
**************************************************************************/
static int lookup_funcion( int fd,PARSER *p )
{
	int i;
	int j;

	struct {
		char *label;
		void (*func) ();
	} tokens[] = 
	{
		{ BN_READ,		token_read },
		{ BN_SEND,		token_send },
		{ BN_START,		token_start },
		{ BN_STATUS,		token_status },
		{ BN_MESSAGE,		token_message },
		{ BN_EXIT,		token_exit },
		{ BN_NAME,		token_name },
		{ BN_FIRE,		token_fire },
		{ BN_ROBOT,		token_robot },
		{ BN_TEST,		NULL },
		{ BN_SCORES,		NULL },
		{ BN_CLI_VER,		token_client_version },
		{ BN_SER_VER,		token_server_version },
		{ BN_NUMJUG,		token_numjug },
		{ BN_HELP,		token_help },
		{ BN_PROTOCOL,		NULL },
		{ BN_REM,		NULL },
		{ BN_QUMM,		NULL }
	};
	int ntokens = sizeof (tokens) / sizeof (tokens[0]);

	j=quejugador(fd);
	if(j<0 || j>=MAXPLAYER)
		return;

	for (i = 0; i < ntokens; i++) {
		if (strcmp( p->token, tokens[i].label )==0 ){
			if (tokens[i].func)
				(tokens[i].func)(fd,p->value);
			say_in_clist(j,C_LASTTOKEN,tokens[i].label);
			return TRUE;
		}
	}
	return FALSE;
}


void
play_batnav( gpointer data, int fd, GdkInputCondition GDK_INPUT_READ)
{
	int i,j;
	PARSER p;
	char str[PROT_MAX_LEN];
	DELIM igualador={ '=', ':', '=' };
	DELIM separador={ ';', ';', ';' };

	p.igualador = &igualador;
	p.separador = &separador;

	str[0]=0;

	j=net_readline( fd, str, PROT_MAX_LEN );

	if( j<1 ) {
		token_exit(fd);
		return;
	}
	
	j=quejugador( fd );
	if(j<0 || j>=MAXPLAYER)
		return;

	strncpy(p.sig,str,sizeof(p.sig));
	
	do{
		i=parser_init( &p );
		if(p.status) 
			lookup_funcion( fd,&p );
	} while(i);
	return ;
}
