/*
 *	$Source: /mit/jik/sipbsrc/src/gameserver/RCS/user.c,v $
 *	$Header: /mit/jik/sipbsrc/src/gameserver/RCS/user.c,v 1.10 1992/03/03 02:18:25 jik Exp $
 */

#ifndef lint
static char *rcsid_user_c = "$Header: /mit/jik/sipbsrc/src/gameserver/RCS/user.c,v 1.10 1992/03/03 02:18:25 jik Exp $";
#endif lint


#include <stdio.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <assert.h>
#include "messagecodes.h"
#include "user.h"
#ifdef HESIOD
#include <hesiod.h>
#endif

#define PIT_LENGTH 1024
char pit[PIT_LENGTH];

static u_short nslen;			/* for network byte order */
static u_long nlen;

int get_socket_to_gameserver_for_game(game)
char *game;
{
#ifdef HESIOD
     char **ret;

     ret = hes_resolve(game, "sloc");
     if ((! ret) || (! *ret)) {
	  return(-2);
     }
     else {
	  return(get_socket_to_gameserver_host(ret[0]));
     }
#else
     return(get_socket_to_gameserver());
#endif
}

int get_socket_to_gameserver()
{
     return(get_socket_to_gameserver_host(GAMESERVER_HOST_NAME));
}

int get_socket_to_gameserver_host(hostname)
char *hostname;
{
    struct hostent *host;
    struct sockaddr_in sin;
    int server;
#ifndef GAMESERVER_PORT
    struct servent *service = 0;

#ifdef HESIOD
    service = hes_getservbyname("gameserver", "tcp");
#endif
    if (! service) service = getservbyname("gameserver","tcp");
    if (service == NULL) return(-2);
#endif

    host = gethostbyname(hostname);
    if (host == NULL) return(-1);

    bzero((char *)&sin,sizeof(sin));
    bcopy(host->h_addr, &sin.sin_addr, host->h_length);
    sin.sin_family = host->h_addrtype;

#ifdef GAMESERVER_PORT
    sin.sin_port = htons(GAMESERVER_PORT);
#else
    sin.sin_port = service->s_port;
#endif

    server = socket(host->h_addrtype, SOCK_STREAM, 0);
    if (server < 0) return(server);
    if (connect(server, (char *)&sin, sizeof(sin)) < 0) {
	    return(-3);
    }
    return(server);
}


int get_list_of_games(server, list_of_games, list_length)
int server, list_length;
struct game_info list_of_games[];
{
    char message_code;
    short message_length;
    int list_size;
    extern char pit[PIT_LENGTH];

    message_code = MC_LIST_GAMES;
    write(server, &message_code, sizeof(message_code));

    if (safe_read(server, &message_code, sizeof(message_code)) < 0)
	return(-1);
    if (message_code != MC_GAME_LIST)
	return(-1);

    if (safe_read(server, &nslen, sizeof(nslen)) < 0)
	return(-1);
    message_length = ntohs(nslen);
    assert(message_length >= 0);
    list_size = list_length * sizeof(struct game_info);
    if (message_length <= list_size) {
	if (safe_read(server, list_of_games, (int) message_length) < 0)
	    return(-1);
	else {
		fix_gamelist(list_of_games,
			     message_length / sizeof (struct game_info));
		return(message_length / sizeof(struct game_info));
	}
    }
    else
	if (safe_read(server, list_of_games, list_size) < 0)
	    return(-1);
	else
	    while(1) {
		if (message_length <= PIT_LENGTH)
		    if (safe_read(server, pit, (int) message_length) < 0)
			return(-1);
		    else
			return(list_length);
		else
		    if (safe_read(server, pit, PIT_LENGTH) < 0)
			return(-1);
		    else
			message_length -= PIT_LENGTH;
	    }
}


fix_gamelist(glist, num)
struct game_info glist[];
int num;
{
	register int i;

	for (i = 0; i < num; i++) {
		glist[i].type = ntohl(glist[i].type);
		glist[i].id = ntohl(glist[i].id);
	}
}

int query_game(server, game_id, buf, buflen)
int server, game_id, buflen;
char *buf;
{
    extern char pit[PIT_LENGTH];
    unsigned char message_code;
    short message_length;

#ifdef DEBUG
    fprintf(stderr, "user: calling query_game\n");
#endif
    message_code = MC_QUERY_GAME;
    write(server, &message_code, sizeof(message_code));
    nlen = htonl(game_id);
    write(server, &nlen, sizeof(nlen));
    if (safe_read(server, &message_code, sizeof(message_code)) < 0) {
#ifdef DEBUG
	fprintf(stderr, "user: no response to query request\n");
#endif
	return(-1);
    }
    if (message_code != MC_QUERY_RESPONSE) {
#ifdef DEBUG
	fprintf(stderr, "user: bad response to query request %d\n", message_code);
#endif
	return(-1);
    }
    if (safe_read(server, &nslen, sizeof(nslen)) < 0) {
#ifdef DEBUG
	fprintf(stderr, "user: cannot get response length\n");
#endif
	return(-1);
    }
    message_length = ntohs(nslen);
    assert(message_length >= 0);
    if (message_length <= buflen)
	if (safe_read(server, buf, (int) message_length) < 0) {
#ifdef DEBUG
            fprintf(stderr, "user: response shorter than expected\n");
#endif
	    return(-1);
	}
	else
	    return(message_length);
    else {
#ifdef DEBUG
	fprintf(stderr, "user: buffer not large enough\n");
#endif
	if (safe_read(server, buf, buflen) < 0)
	    return(-1);
	else
	    while (1) {
		if (message_length <= PIT_LENGTH)
		    if (safe_read(server, pit, (int) message_length) < 0)
			return(-1);
		    else
			return(buflen);
		else
		    if (safe_read(server, pit, PIT_LENGTH) < 0)
			return(-1);
		    else
			message_length -= PIT_LENGTH;
	    }
    }
}


int join_game(server, game_id)
int server, game_id;
{
    unsigned char message_code;

    message_code = MC_JOIN_GAME;
    write(server, &message_code, sizeof(message_code));
    nlen = htonl(game_id);
    write(server, &nlen, sizeof(nlen));
    if (safe_read(server, &message_code, sizeof(message_code)) < 0)
	return(-1);
    if (message_code == MC_OK)
	return(connect_to_game(server));
    else
	return(-1);
}


int create_game(server, game_type)
int server, game_type;
{
    unsigned char message_code;

    message_code = MC_CREATE_GAME;
    write(server, &message_code, sizeof(message_code));
    nlen = htonl(game_type);
    write(server, &nlen, sizeof(nlen));
    if (safe_read(server, &message_code, sizeof(message_code)) < 0)
	return(-1);
    if (message_code == MC_OK)
	return(connect_to_game(server));
    else
	return(-1);
}


int find_game(server, game_type)
int server, game_type;
{
    char message_code;

    message_code = MC_FIND_GAME;
    write(server, &message_code, sizeof(message_code));
    nlen = htonl(game_type);
    write(server, &nlen, sizeof(nlen));
    if (safe_read(server, &message_code, sizeof(message_code)) < 0)
	return(-1);
    if (message_code == MC_OK)
	return(connect_to_game(server));
    else
	return(-1);
}


int connect_to_game(server)
int server;
{
    int new_s, new_new_s, len;
    struct sockaddr_in nsin;
    struct sockaddr_in nsinnbo;		/* net byte order */

    new_s = socket(AF_INET, SOCK_STREAM, 0);
    if (new_s < 0) return(new_s);

    len = sizeof(nsin);
    getsockname(server, &nsin, &len);
    nsin.sin_port = 0;
    if (bind(new_s, &nsin, sizeof(nsin)) < 0) return(-1);

    if (listen(new_s, 1) < 0) return(-1);

    len = sizeof(nsin);
    getsockname(new_s, &nsin, &len);

    bcopy(&nsin, &nsinnbo, sizeof(nsin));
    nsinnbo.sin_family = htons(nsin.sin_family);

    write(server, &nsinnbo, sizeof(nsinnbo));

    new_new_s = accept(new_s, &nsin, &len);
    if (new_new_s < 0) return(new_new_s);

    close(server);
    close(new_s);
    return(new_new_s);
}

