
/*
	highscore-client.c -- The hiscore client using TCP/IP stream.
	~yas/dsys/highscore/tcp/highscore-client.c
*/

#include <stdio.h>	/* stderr, fprintf() */
#include <stdlib.h>	/* strtol() */
#include <unistd.h>	/* close() */
#include <string.h>	/* memcpy() */
#include <sys/types.h>	/* socket() */
#include <sys/socket.h>	/* socket() */
#include <netinet/in.h>	/* struct sockaddr_in */
#include <netdb.h>	/* getaddrinfo(), freeaddrinfo(), struct addrinfo */
#include "highscore-tcp.h"
#include "marshaling-burffer.h"

/* From Coins System Program */
extern	int tcp_connect( char *server, int portno );

int get_highscore_client( char *server, int portno,
			      score_record_t records[], int len ) {
    int sock, cmd, n, i;
    marbuf_t request, reply;
	marbuf_init( &request,HISCORE_PROTO_MAX_MESSAGE_SIZE );
	marbuf_init( &reply,HISCORE_PROTO_MAX_MESSAGE_SIZE );
	if( !marbuf_marshal_int( &request, HISCORE_PROTO_GET_HISCORE ) )
	    goto error0;
	marbuf_marshal_int( &request, len );
	if( (sock = tcp_connect( server, portno )) < 0 ) {
	    perror("tcp_sonnect");
	    goto error0;
	}
	if( marbuf_send_message( &request, sock ) < 0 ) {
	    perror("send");
	    goto error1;
	}
	if( marbuf_receive_message( &reply, sock ) < 0 ) {
	    perror("recieve");
	    goto error1;
	}
	if( !marbuf_unmarshal_int( &reply, &n ) ) {
	    fprintf(stderr,"unmarshal n\n");
	    goto error1;
	}
	if( n > len ) {
	    fprintf(stderr,"received message too large: %d > %d\n", n, len );
	    goto error1;
	}
	for( i=0 ; i<n; i++ ) {
	    if( !marbuf_unmarshal_int(&reply,&records[i].score ) ) {
		fprintf(stderr,"unmarshal name\n");
		goto error1;
	    }
	    if( !marbuf_unmarshal_byte_array(&reply,records[i].name,
					     HIGHSCORE_NAME_LEN) ) {
		fprintf(stderr,"unmarshal name\n");
		goto error1;
	    }
	}
	close( sock );
	marbuf_final( &request );
	marbuf_final( &reply );
	return( n );

error1:	close( sock );
error0:	marbuf_final( &request );
	marbuf_final( &reply );
	return( -1 );
}

int put_score_client(char *server, int portno, int score, char *name) {
    int sock, cmd, ok;
    marbuf_t request, reply;
    char name_buf[HIGHSCORE_NAME_LEN];
	marbuf_init( &request,HISCORE_PROTO_MAX_MESSAGE_SIZE );
	marbuf_init( &reply,HISCORE_PROTO_MAX_MESSAGE_SIZE );
	if( !marbuf_marshal_int( &request, HISCORE_PROTO_PUT_SCORE) )
	    goto error0;
	if( !marbuf_marshal_int( &request, score ) )
	    goto error0;
	memset( name_buf, 0, HIGHSCORE_NAME_LEN );
	snprintf( name_buf, HIGHSCORE_NAME_LEN, "%s", name );
	if( !marbuf_marshal_byte_array( &request, name_buf, HIGHSCORE_NAME_LEN ) )
	    goto error0;
	if( (sock = tcp_connect( server, portno )) < 0 ) {
	    perror("tcp_sonnect");
	    goto error0;
	}
	if( marbuf_send_message( &request, sock ) < 0 ) {
	    perror("send");
	    goto error1;
	}
	if( marbuf_receive_message( &reply, sock ) < 0 ) {
	    perror("recieve");
	    goto error1;
	}
	if( !marbuf_unmarshal_int( &reply, &ok ) ) {
	    fprintf(stderr,"unmarshal n\n");
	    goto error1;
	}
	close( sock );
	marbuf_final( &request );
	marbuf_final( &reply );
	return( ok );

error1:	close( sock );
error0:	marbuf_final( &request );
	marbuf_final( &reply );
	return( -1 );
}

/* From Coins System Program */
#define PORTNO_BUFSIZE 30

int
tcp_connect( char *server, int portno )
{
	struct addrinfo hints, *ai;
	char portno_str[PORTNO_BUFSIZE];
	int s, err;
	snprintf( portno_str,sizeof(portno_str),"%d",portno );
	memset( &hints, 0, sizeof(hints) );
	hints.ai_socktype = SOCK_STREAM;
	if( (err = getaddrinfo( server, portno_str, &hints, &ai )) )
	{
		fprintf(stderr,"unknown server %s (%s)\n",server,
			gai_strerror(err) );
		goto error0;
	}
	if( (s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0 )
	{
		perror("socket");
		goto error1;
	}
	if( connect(s, ai->ai_addr, ai->ai_addrlen) < 0 )
	{
		perror( server );
		goto error2;
	}
	freeaddrinfo( ai );
	return( s );
error2:
	close( s );
error1:
	freeaddrinfo( ai );
error0:
	return( -1 );
}
