/*	$Id: batnavggz.c,v 1.13 2002/04/20 05:07:50 riq Exp $	*/
/* Tenes Empanadas Graciela
 *
 * Copyright (C) 2000 Ricardo Quesada
 *
 * Author: Ricardo Calixto Quesada <rquesada@core-sdi.com>
 *
 * 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; only version 2 of the License
 *
 * 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.
 */
/**
 * @file batnavggz.c
 * Funcion que maneja un poco el soporte para GGZ
 */


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

#include <config.h>

#ifdef WITH_GGZ

#include "server.h"
#include "common.h"

#include <ggzdmod.h>

static GGZdMod *g_ggzdmod = NULL;

static void _handle_ggz_state(GGZdMod *ggz, GGZdModEvent event, void *data);
static void _handle_ggz_log(GGZdMod *ggz, GGZdModEvent event, void *data);
static void _handle_ggz_error(GGZdMod *ggz, GGZdModEvent event, void *data);
static void _handle_ggz_join(GGZdMod *ggz, GGZdModEvent event, void *data);
static void _handle_ggz_leave(GGZdMod *ggz, GGZdModEvent event, void *data);
static void _handle_ggz_player(GGZdMod *ggz, GGZdModEvent event, void *data);

/* argh... GGZ leave event sucks... fix for that */
#define SEATFD_MAX	(32)
static int _seatfd_map[ SEATFD_MAX ];
static int seatfd_init()
{
	int i;
	for(i=0;i<SEATFD_MAX;i++)
		_seatfd_map[i] = -1;

	return 0;
}

static int seatfd_add_map( int seatnum, int fd )
{
	if( seatnum < 0 || seatnum >= SEATFD_MAX )
		return -1;

	_seatfd_map[ seatnum ] = fd;
	return 0;
}

static int seatfd_del_map( int seatnum )
{
	if( seatnum < 0 || seatnum >= SEATFD_MAX )
		return -1;

	_seatfd_map[ seatnum ] = -1;
	return 0;
}

static int seatfd_get_map( int seatnum )
{
	if( seatnum < 0 || seatnum >= SEATFD_MAX )
		return -1;

	return _seatfd_map[ seatnum ];
}

static BATNAV_STATUS launch_bot( int *robot_socket  )
{
	pid_t pid;
	char *args[5];

	int sockets[2];

	// sockets[0] is the client side of the robot
	// sockets[1] is the server side
	int r = socketpair(AF_LOCAL, SOCK_STREAM, 0, sockets );
	if( r != 0 )
		return BATNAV_STATUS_ERROR;

	// launch the server connected
	if ( (pid = fork()) < 0) {
		perror("batnavserver: launch_bot() ");
		return BATNAV_STATUS_ERROR;
	} else if (pid == 0) {

		close( sockets[1] );
		args[0] = BINDIR"/gbnrobot";
		args[1] = "--ggz";
		args[2] = NULL;

		if ( dup2(  sockets[0], 3 ) == -1 )
			return BATNAV_STATUS_ERROR;
	
		ggzdmod_log(g_ggzdmod, "Launching robot with options: %s %s",args[0],args[1]);
		if( execv(args[0], args) < 0) {
			ggzdmod_log(g_ggzdmod,"Launching robot failed. Does the file `%s' exists ?",args[0]);
			perror("exe:");
		}
		exit(1);
	} else {
		close( sockets[0] );
	}

	// return the robot fd
	*robot_socket = sockets[1];

	return BATNAV_STATUS_SUCCESS;
}

/* Setup game state and board */
static int batnav_ggz_init()
{
	g_ggzdmod = ggzdmod_new(GGZDMOD_GAME);
	if( ! g_ggzdmod )
		return -1;

	/* Setup GGZ game module */
	ggzdmod_set_handler(g_ggzdmod, GGZDMOD_EVENT_STATE, &_handle_ggz_state);
	ggzdmod_set_handler(g_ggzdmod, GGZDMOD_EVENT_JOIN, &_handle_ggz_join);
	ggzdmod_set_handler(g_ggzdmod, GGZDMOD_EVENT_LEAVE, &_handle_ggz_leave);
	ggzdmod_set_handler(g_ggzdmod, GGZDMOD_EVENT_PLAYER_DATA, &_handle_ggz_player);
        ggzdmod_set_handler(g_ggzdmod, GGZDMOD_EVENT_LOG, &_handle_ggz_log);
	ggzdmod_set_handler(g_ggzdmod, GGZDMOD_EVENT_ERROR, &_handle_ggz_error);

	return 0;
}
/* Callback for GGZDMOD_EVENT_STATE */
static void _handle_ggz_state(GGZdMod *ggz, GGZdModEvent event, void *data)
{
	switch(ggzdmod_get_state(ggz)) {
	case GGZDMOD_STATE_PLAYING:
		break;
	case GGZDMOD_STATE_WAITING:
	{
		GGZSeat seat;
		int i;
		int robot_socket;
		int max = ggzdmod_get_num_seats( g_ggzdmod );

		// launch robots if necesary
		for (i = 0; i < max; i++) {
			seat = ggzdmod_get_seat( g_ggzdmod, i );
			if( seat.type == GGZ_SEAT_BOT ) {
				if (launch_bot( &robot_socket ) == BATNAV_STATUS_SUCCESS )
				{
					seat.fd = robot_socket;

					ggzdmod_set_seat( g_ggzdmod, &seat );
					seatfd_add_map( seat.num, seat.fd );

					if( accept_new_player( seat.fd ) < 0) {
						ggzdmod_log(g_ggzdmod, "\batnavserver: Failed launching bot :-(!");
						return;
					}
					bnwrite(seat.fd,BN_GGZ);
				} else
					ggzdmod_log(g_ggzdmod, "\batnavserver: Failed launching bot :-(!");
			}
		}
		break;
	}
	case GGZDMOD_STATE_DONE:
		break;
	default:
		break;
	}
}

static int seats_full(void)
{
	return ggzdmod_count_seats(g_ggzdmod, GGZ_SEAT_OPEN)
		+ ggzdmod_count_seats(g_ggzdmod, GGZ_SEAT_RESERVED) == 0;
}

/* Callback for GGZDMOD_EVENT_LOG */
static void _handle_ggz_log(GGZdMod *ggz, GGZdModEvent event, void *data)
{
}

/* Callback for GGZDMOD_EVENT_ERROR */
static void _handle_ggz_error(GGZdMod *ggz, GGZdModEvent event, void *data)
{
}

/* Callback for GGZDMOD_EVENT_JOIN */
static void _handle_ggz_join(GGZdMod *ggz, GGZdModEvent event, void *data)
{
	GGZSeat seat;
	int numseat = *(int*)data;

	seat = ggzdmod_get_seat(ggz, numseat);

	if( accept_new_player( seat.fd ) < 0) {
		ggzdmod_log( g_ggzdmod, "player is not accepted" );
	}

	seatfd_add_map( numseat, seat.fd );

	bnwrite(seat.fd,BN_GGZ);
	
	/* XXX: We start playing only when there are no open seats. */
	if (seats_full())
		ggzdmod_set_state( g_ggzdmod, GGZDMOD_STATE_PLAYING);
}

/* Callback for GGZDMOD_EVENT_LEAVE */
static void _handle_ggz_leave(GGZdMod *ggz, GGZdModEvent event, void *data)
{
	int fd;
	int numseat = *(int*)data;

	fd = seatfd_get_map( numseat );
	if( fd == -1 ) {
		fprintf(stderr,"Cant find fd from GGZSeat number\n");
		return;
	}

	seatfd_del_map( numseat );

	token_exit(fd);
}

/* Handle message from player */
static void _handle_ggz_player(GGZdMod *ggz, GGZdModEvent event, void *data)
{
	int num = *(int*)data;
	int fd;
	GGZSeat seat;

	seat = ggzdmod_get_seat(ggz, num);
	fd = seat.fd;
	play_batnav( NULL, fd, NULL );
}

BATNAV_STATUS batnavggz_find_ggzname( int fd, char *n, int len )
{
	int i;
	int max;
	GGZSeat seat;
	if(!n)
		return BATNAV_STATUS_ERROR;

	max = ggzdmod_get_num_seats( g_ggzdmod );


	for (i = 0; i < max; i++) {
		seat = ggzdmod_get_seat( g_ggzdmod, i );
		if( seat.fd == fd ) {

			/* dont update robot's names */
			if( seat.type == GGZ_SEAT_BOT )
				return BATNAV_STATUS_ERROR;

			if( seat.name ) {
				strncpy(n,seat.name,len);
				n[len]=0;
				return BATNAV_STATUS_SUCCESS;
			} else
				return BATNAV_STATUS_ERROR;
		}
	}
	return BATNAV_STATUS_ERROR;
}

int batnavggz_main_loop()
{
	seatfd_init();

	if( batnav_ggz_init() == -1 )
		return -1;

	/* Connect to GGZ server; main loop */
	if (ggzdmod_connect( g_ggzdmod) < 0)
		return -1;

	(void) ggzdmod_loop( g_ggzdmod );
	(void) ggzdmod_disconnect( g_ggzdmod );

	ggzdmod_free( g_ggzdmod );
	
	return 0;
}

int batnavggz_del_fd( int fd )
{
	/* should I remove the player */
	return 0;
}

#endif /* WITH_GGZ */
