/*	$Id: robot_proceso.c,v 1.18 2002/04/19 04:12:40 riq Exp $	*/
/****************************************************************************
 	Funcion basada en la del cliente.
	Comportamiento del Robot a nivel BNP
 ****************************************************************************/

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>
#include <netdb.h>
#include <stdio.h>
#include <syslog.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

#include <config.h>
#include <gnome.h>

#include "robot_cliente.h"
#include "robot_random_board.h"
#include "robot_ai.h"
#include "bnwrite.h"
#include "protocol.h"
#include "parser.h"
#include "net.h"
#include "common.h"


/* This describes all the arguments we understand.  */

static char *server_name=NULL;
static const struct poptOption options[] =
{
	{ "server", 's', POPT_ARG_STRING, &server_name, 0,
		N_("Server name. Default is localhost"),"SERVER"},
	{ "port", 'p', POPT_ARG_INT, &cliente.port, 0,
		N_("Port number. Default is 1995"),"PORT"},
#ifdef WITH_GGZ
	{ "ggz", 0, POPT_ARG_NONE, &cliente.with_ggz, 0,
		N_("Enable GGZ mode"),NULL },
#endif /* WITH_GGZ */
	{ "nogui", 0, POPT_ARG_NONE, &cliente.with_nogui, 0,
		N_("Disable the GUI"),NULL },
	{ NULL, '\0',  0, NULL }
};

/* nombre y skill del robot */

struct { 
	char *nombre;
	robotai_t ai;
} robot_nombres[] = {
	{ "homer", ROBOT_AI_AVERAGE },
	{ "bart", ROBOT_AI_AVERAGE },
	{ "marge", ROBOT_AI_AVERAGE },
	{ "lisa", ROBOT_AI_HI },

	{ "albondiga" ,ROBOT_AI_AVERAGE },
	{ "clorofila", ROBOT_AI_AVERAGE },
	{ "maceta", ROBOT_AI_AVERAGE },
	{ "cebolla", ROBOT_AI_AVERAGE },

	{ "edu", ROBOT_AI_AVERAGE },
	{ "gonza", ROBOT_AI_AVERAGE },
	{ "pitufo", ROBOT_AI_AVERAGE },
	{ "cacho", ROBOT_AI_AVERAGE },

	{ "president", ROBOT_AI_AVERAGE },
	{ "chuchi", ROBOT_AI_AVERAGE },
	{ "sandro", ROBOT_AI_AVERAGE },
	{ "beto", ROBOT_AI_AVERAGE },

	{ "raul", ROBOT_AI_AVERAGE },
	{ "medusa", ROBOT_AI_AVERAGE },
	{ "juan", ROBOT_AI_AVERAGE },
	{ "pipito", ROBOT_AI_AVERAGE },
	
	{ "chichi", ROBOT_AI_AVERAGE },
	{ "pichu", ROBOT_AI_AVERAGE },
	{ "cachi", ROBOT_AI_AVERAGE },
	{ "roque", ROBOT_AI_AVERAGE },

	{ "charly", ROBOT_AI_AVERAGE },
	{ "chupete", ROBOT_AI_AVERAGE },
	{ "tula", ROBOT_AI_AVERAGE },
	{ "loko", ROBOT_AI_AVERAGE },

	{ "cachito", ROBOT_AI_AVERAGE },
	{ "sandro", ROBOT_AI_AVERAGE },
	{ "vince", ROBOT_AI_AVERAGE },
	{ "corcho", ROBOT_AI_AVERAGE },

	{ "bombi", ROBOT_AI_AVERAGE },
	{ "edna", ROBOT_AI_AVERAGE },
	{ "viole", ROBOT_AI_AVERAGE },
	{ "papi", ROBOT_AI_AVERAGE },

	{ "mami", ROBOT_AI_AVERAGE },
	{ "arjona", ROBOT_AI_AVERAGE },
	{ "piojo", ROBOT_AI_AVERAGE },
	{ "valerio", ROBOT_AI_AVERAGE },

	{ "sofi", ROBOT_AI_AVERAGE },
	{ "miko", ROBOT_AI_AVERAGE },
	{ "onda", ROBOT_AI_AVERAGE },
	{ "llave", ROBOT_AI_AVERAGE }
};
static int rnombres = sizeof(robot_nombres) / sizeof(robot_nombres[0] );

/*****************************************************************************
			funiones
			auxiliares
******************************************************************************/

/* convierte un char[10][10] a un char[100] */
static void iwtable( char *dest)
{
	int i,x,y;
	x=0;
	y=0;

	for(i=0;i<100;i++) {
		dest[i]=cliente.mitabla[x][y];
		x++;
		if(x>=10) { 
			x=0;
			y++;
		}
	}
}
/* convierte un char[100] a un char[10][10] */
static void poner_en_board( int d, char *orig)
{
	int i,x,y;
	x=0;
	y=0;

	for(i=0;i<100;i++) {
		cliente.boards[d][x][y]=orig[i];
		x++;
		if(x>=10) { 
			x=0;
			y++;
		}
	}
}

/* Esta funcion tiene algo que ver con el AI */
static void que_usrfrom()
{ 
	int i;
	
	for(i=cliente.numjug+1;i<MAXPLAYER;i++) {
		if( cliente.play[i]>=BOARD && cliente.play[i]<=TURN) {
			cliente.usrfrom=i;
			return;
		}
	}
	for(i=0;i<=cliente.numjug;i++) {
		if( cliente.play[i]>=BOARD && cliente.play[i]<=TURN ) {
			cliente.usrfrom=i;
			return;
		}
	}
	printf("gbnrobot: robot %d: que_usrfrom: Error, aca no tendria que llegar\n",cliente.numjug);
}

static void dime_quien_soy( void )
{
	int tmp;

	tmp = ((cliente.random<<1 ) ^ (cliente.random>>1)) % rnombres;
	if(tmp < 0)
		tmp=-tmp;

	strncpy( cliente.mi_nombre,  robot_nombres[tmp].nombre, sizeof(cliente.mi_nombre) );
	cliente.ai = robot_nombres[tmp].ai;
	
}

static BATNAV_STATUS send_new_board()
{
	char buf[101];

	generar();

	iwtable(buf);
	buf[10*10]=0;
	if( bnwrite(cliente.sock,BN_SEND"=%s",buf) < 0)
		return BATNAV_STATUS_ERROR;

	if(!cliente.with_nogui)
		gtk_progress_set_value (GTK_PROGRESS (pbar), 20);

	return BATNAV_STATUS_SUCCESS;
}

/*****************************************************************************
			funciones
			robot_*
******************************************************************************/
static void robot_fire(char *str)
{
	gfloat new_val;
	GtkAdjustment *adj;
	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)		/* X */
			x=atoi(p.token);

		else if(p.status && j==1)	/* Y */
			y=atoi(p.token);	
	
		else if(p.status && j==2)	/* Tocado, hundio, etc */
			z=atoi(p.token);	
		j++;
	} while(i);
	if(j!=3) {
		printf("gbnrobot: Error parsing in '"BN_FIRE"'");
		return;
	}
	if(z==HUNDIDO) {
		if(!cliente.with_nogui) {
			adj = GTK_PROGRESS (pbar)->adjustment;

			new_val = adj->value - 1;
			if (new_val > adj->upper)
				new_val = adj->lower;

			gtk_progress_set_value (GTK_PROGRESS (pbar), new_val);
		}
	}
}

static void robot_numjug( char *str)
{
	char buf[101];
	int i;
	
	i=atoi(str);
	cliente.numjug=i;

	generar();

	dime_quien_soy();

	iwtable(buf);
	buf[10*10]=0;
	bnwrite(cliente.sock,BN_NAME"=%s;"BN_CLI_VER"="ROBOTVER";"BN_SER_VER";"BN_PROTOCOL";"BN_SEND"=%s",cliente.mi_nombre,buf);

	if(!cliente.with_nogui) {
		sprintf(buf,"-> %s[%i] <-",cliente.mi_nombre,cliente.numjug);
		gtk_label_set_text(GTK_LABEL(label), buf);
	}
}
static void robot_ignore()
{
}
static void robot_killmyself()
{
	bnwrite(cliente.sock,BN_EXIT);

	close( cliente.sock );

	if( !cliente.with_nogui )
		gtk_main_quit();

	exit(1);
}
static void robot_board_not_ok()
{
	printf("gbnrobot: Robot: You sent an invalid board. Report this bug!\n");
	robot_killmyself();
}
static void robot_board_ok( void )
{
	if(!cliente.with_nogui)
		gtk_label_set_text(GTK_LABEL(status_label), _("Ready"));
}
static void robot_start( char *str)
{
	int i,k,l,m;
	PARSER p;
	DELIM igualador={ '=','=','=' };
	DELIM separador={ '/',',','/' };

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

	strncpy(p.sig,str,sizeof(p.sig));
	
	m=0;k=0;l=0;
	do{
		i=parser_init( &p );
		
		if(p.status && m==0)		/* number of player */
			l=atoi(p.token);

		else if(p.status && m==1) {	/* estado */
			k=atoi(p.token);
			cliente.play[l]=k;
		}
		m++;
		m=m%3;
	} while(i);
	que_usrfrom();
/*	printf("Robot %d: Starting...usrfrom=%i\n",cliente.numjug,cliente.usrfrom); */
}
static void robot_discon( char *str )
{
	int x;
	
	x=atoi(str);
	if( cliente.numjug==x ) {
/*		printf("Robot %i: Me estan desconectando\n",x); */
		robot_killmyself();
	}

	if(x>=0 || x < MAXPLAYER)
		cliente.play[x]=DISCON;

	que_usrfrom();
	return;
}
static void robot_turn( char *str )
{
	if( cliente.play[cliente.numjug]==PERDIO)
		return;

	if(atoi(str)==cliente.numjug) {
		bnwrite(cliente.sock,BN_READ"=%i",cliente.usrfrom);
		cliente.play[cliente.numjug]=TURN;
		if(!cliente.with_nogui)
			gtk_label_set_text(GTK_LABEL(status_label), _("My turn"));
	} else {
		cliente.play[cliente.numjug]=PLAY;
		if(!cliente.with_nogui)
			gtk_label_set_text(GTK_LABEL(status_label), _("Waiting turn..."));
	}
}

static void robot_read( char *str )
{
	int i,m,l,x,y;
	char buf[10*10];
	PARSER p;
	DELIM igualador={ '=','=','=' };
	DELIM separador={ '/',',','/' };

	if(cliente.play[cliente.numjug]!=TURN) {
/*		printf("Robot %d: No es mi turno \n",cliente.numjug); */
		return;
	}

	p.igualador = &igualador;
	p.separador = &separador;
	strncpy(p.sig,str,sizeof(p.sig));
	m=0;
	do{
		i=parser_init( &p );
		if(p.status && m==0)		/* numero de jugador */
			l=atoi(p.token);

		else if(p.status && m==1)	/* board del jugador */
			strncpy(buf,p.token,10*10);
		m++;
	} while(i);
	
	if(l==cliente.usrfrom ) {
		if(cliente.usrfrom == cliente.numjug ) {
/*			printf("Robot %i : usrfrom = numjug",cliente.numjug); */
		}
		poner_en_board(l,buf);
		robot_ai(&x,&y);	
		usleep(75000);
		bnwrite(cliente.sock,BN_FIRE"=%i,%i",x,y);
	} else
		bnwrite(cliente.sock,BN_READ"=%i",cliente.usrfrom);
}
static void robot_lost( char *str)
{
	int x;
	x=atoi(str);

	if(x>=0 || x<MAXPLAYER)
		cliente.play[x]=PERDIO;

	if(x==cliente.numjug && !cliente.with_nogui)
		gtk_label_set_text(GTK_LABEL(status_label), _("Game Over"));
	else
		que_usrfrom();
	return;
}
static void robot_ggz()
{
	bnwrite(cliente.sock,BN_NUMJUG);
}

static void robot_win( int j, char *str)
{
	int a;

	/* alguien gano, asi que todo bien */
	send_new_board();
	return;
}

static void robot_game_over( void )
{
	if(!cliente.with_nogui)
		gtk_label_set_text(GTK_LABEL(status_label), _("Game Over"));
	send_new_board();
}

/**********************************************************************
		lookup token, etc
**********************************************************************/
static int robot_lookup_funcion( PARSER *p )
{
	int i;

	struct {
		char *label;
		void (*func) ();
	} tokens[] = {
		{ BN_BOARD_NOT_OK,	robot_board_not_ok },
		{ BN_BOARD_OK,		robot_board_ok },
		{ BN_DISCON,		robot_discon },
		{ BN_GAME_OVER,		robot_game_over },
		{ BN_LOST,		robot_lost },
		{ BN_MESSAGE,		NULL },
		{ BN_NAME,		NULL },
		{ BN_NUMJUG,		robot_numjug },
		{ BN_PROTOCOL,		NULL },
		{ BN_QUMM,		NULL },
		{ BN_READ,		robot_read },
		{ BN_READY_TO_PLAY,	NULL },
		{ BN_REM,		robot_ignore },
		{ BN_SCORES,		NULL },
		{ BN_SER_FULL,		robot_killmyself },
		{ BN_SER_VER,		NULL },
		{ BN_SOL,		robot_killmyself },
		{ BN_START,		robot_start },
		{ BN_STATUS,		NULL },
		{ BN_TEST,		NULL },
		{ BN_TURN,		robot_turn },
		{ BN_WAIT,		robot_killmyself },
		{ BN_WIN,		robot_win },
		{ BN_FIRE,		robot_fire },
		{ BN_GGZ,		robot_ggz },
	};
	int ntokens = sizeof (tokens) / sizeof (tokens[0]);

	for (i = 0; i < ntokens; i++) {
		if (strcmp( p->token, tokens[i].label )==0 ){
			if (tokens[i].func)
				(tokens[i].func)( p->value );
			else {
/*				printf("Robot %d: Function '%s' is not implemented yet!\n",cliente.numjug,tokens[i].label); */
			}
			return TRUE;
		}
	}
	return FALSE;
}

static int robot_proceso( gpointer data, int sock, GdkInputCondition GDK_INPUT_READ )
{
	int i,j;
	PARSER p;
	DELIM igualador={ '=', ':', '=' };
	DELIM separador={ ';', ';', ';' };

	char str[PROT_MAX_LEN];

	p.igualador = &igualador;
	p.separador = &separador;
	str[0]=0;

/*	printf("ROBOT: Entrando a robot_proceso %i\n",sock); */
	j=net_readline( sock, str,PROT_MAX_LEN );
	if( j<1 ) {
		robot_killmyself();
		return -1;
	}
	
	strncpy(p.sig,str,sizeof(p.sig));
	
	do{
		i=parser_init( &p );
		if(p.status) {
			if( !robot_lookup_funcion( &p ) ) {
//				printf("gbnrobot: Token '%s' no encontrado\n",p.token);
			}
		}
	} while(i);
/*	printf("Saliendo de robot_proceso %i\n",sock); */
	return 0;
}

static int robot_init( void )
{
#ifdef WITH_GGZ
	if( cliente.with_ggz ) {
		cliente.with_nogui = 1;
		cliente.sock = 3;
	} else
#endif /* WITH_GGZ */
		cliente.sock = net_connect_tcp( cliente.server, cliente.port);

	if(cliente.sock<0) {
		printf(_("gbnrobot Error: Is the server running?\n"));
		return -1;
	}

	if(!cliente.with_nogui)
		cliente.tag = gdk_input_add( cliente.sock, GDK_INPUT_READ, (GdkInputFunction) robot_proceso, (gpointer) NULL );
    
	return 0;
}

/**
 * @fn char * load_pixmap_file( char *name )
 * Carga un pixmap. Busca en $prefix, localdir & $datadir
 * @param name Pixmap a buscar
 * @return NULL si no encontro o path del pixmap. Hay que g_free ese string
 */
char * load_pixmap_file( char *name )
{
	char *filename = NULL;

	filename = gnome_pixmap_file (name);
	if( filename == NULL ) {
		filename = g_strconcat( PIXMAPDIR,name,NULL );
	}
	return filename;
}

static void on_exit_activate(GtkWidget *widget, gpointer data)
{
	robot_killmyself();
}


static void init_X( )
{
	window = gnome_app_new ("gbnrobot", _("Gnome Batalla Naval robot") );
	gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, TRUE);

	gtk_widget_realize (window);
	gtk_signal_connect ( GTK_OBJECT( window), "destroy",
		GTK_SIGNAL_FUNC( on_exit_activate ),
		GTK_OBJECT(window) );
	gtk_signal_connect ( GTK_OBJECT( window), "delete_event",
		GTK_SIGNAL_FUNC( on_exit_activate ),
		GTK_OBJECT(window) );

	box = gtk_vbox_new( FALSE, 0);
	gnome_app_set_contents(GNOME_APP(window), box );
   
	gtk_container_border_width ( GTK_CONTAINER(box), 0);
  
  	pix = load_pixmap_file("gbnrobot.png");
	if( pix ) {
		imagen = gnome_pixmap_new_from_file( pix );
		gtk_widget_show( imagen );
		g_free( pix );
		gtk_box_pack_start( GTK_BOX(box),imagen,TRUE,TRUE,0);
	}

	adj = (GtkAdjustment *) gtk_adjustment_new (20, 1, 20, 0, 0, 0);

	pbar = gtk_progress_bar_new_with_adjustment (adj);
	
	gtk_progress_set_format_string (GTK_PROGRESS (pbar), "[%v/%u] %p%%");
	gtk_progress_set_show_text (GTK_PROGRESS (pbar), TRUE );

	gtk_box_pack_start (GTK_BOX (box), pbar, FALSE, FALSE, 5);
	
	label = gtk_label_new (_(".")); 
	gtk_box_pack_start (GTK_BOX (box), label, FALSE, TRUE, 5);

	status_label = gtk_label_new (_("Connecting...")); 
	gtk_box_pack_start (GTK_BOX (box), status_label, FALSE, TRUE, 5);

	/* Principal */   
	gtk_widget_show_all( window);
}

void nogui_main()
{
	int fd_max;
	fd_set active_fd,read_fd;
	int r;

	FD_ZERO(&active_fd);
	FD_SET(cliente.sock,&active_fd);

	fd_max = cliente.sock;

	while(1) {
		read_fd = active_fd;
		r = select(fd_max+1,&read_fd,NULL,NULL,NULL);
		if(r < 0 && errno == EINTR )
			continue;
		if(r < 0) {
			fprintf(stderr,"abnormal error\n");
			return;
		}
		if( FD_ISSET(cliente.sock,&read_fd))
			robot_proceso(NULL,cliente.sock,0);
	}
}

/****************************************************************************
 *                           MAIN * MAIN * MAIN
 ****************************************************************************/
int main (int argc, char *argv[])
{
	int i;
	poptContext ctx=NULL;

	dont_run_as_root();

	bindtextdomain( PACKAGE, GNOMELOCALEDIR );
	textdomain( PACKAGE );
 
	cliente.port = 1995;
	strncpy(cliente.server,"127.0.0.1",sizeof(cliente.server)-1);
	cliente.with_nogui=0;

	ctx = poptGetContext(NULL,argc,argv,options,0);
	if(ctx) {
		while( poptGetNextOpt(ctx) != -1);
		poptFreeContext(ctx);
	}

	if( !cliente.with_nogui ) {
		gnome_init_with_popt_table("gbnrobot", ROBOTVER, argc, argv, options, 0, NULL);
		init_X();
	}

	if( server_name ) {
		strncpy(cliente.server,server_name,sizeof(cliente.server)-1);
		cliente.server[sizeof(cliente.server)-1]=0;
	}

	if( robot_init() < 0 ) exit(-1);

	bnwrite( cliente.sock,BN_NUMJUG);

	if(cliente.with_nogui)
		nogui_main();
	else
		gtk_main();
	return 0;
}
