
/*
	highscore-client.c -- The hiscore client using TCP/IP stream.
	Created on: 2008/12/06 17:03:28
	~yas/dsys/highscore/tcp/highscore-client.c
*/

#include <stdio.h>	/* stderr, fprintf() */
#include <stdlib.h>	/* strtol() */
#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 );
extern	int sockaddr_in_init( struct sockaddr_in *addr, size_t addrlen,
			      char *hostname, 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 */
int tcp_connect( char *server, int portno ) {
    struct addrinfo hints, *ai;
    struct sockaddr_in addr ;	/* IPv4 */
    int s ;
	if( (s = socket(PF_INET, SOCK_STREAM, 0)) < 0 ) {
	    perror("socket");
	    return( -1 );
	}
	if( sockaddr_in_init( &addr, sizeof(addr), server, portno )<0 ) {
	    perror("sockaddr_in_init");
	    return( -1 );
	}
	if( connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0 ) {
	    perror( server );
	    close( s );
	    return( -1 );
	}
	return( s );
}

int sockaddr_in_init( struct sockaddr_in *addr, size_t addrlen,
		  char *hostname, int portno ) {
    struct addrinfo hints, *ai;
    int err ;

	if( addrlen < sizeof(struct sockaddr_in) ) {
	    fprintf(stderr,"sockaddr_in, not enough space (%zu) > (%zu)\n",
		     addrlen, sizeof(struct sockaddr_in) );
	    return( -1 );
	}
	memset( &hints, 0, sizeof(hints) );
	hints.ai_family   = AF_INET ; /* IPv4 */
	if( (err = getaddrinfo( hostname, NULL, &hints, &ai )) ) {
	    fprintf(stderr,"unknown host %s (%s)\n",hostname,
		    gai_strerror(err) );
	    return( -1 );
	}
	if( ai->ai_addrlen > addrlen ) {
	    fprintf(stderr,"sockaddr too large (%u) > (%zu)\n",
		    ai->ai_addrlen,sizeof(addr) );
	    freeaddrinfo( ai );
	    return( -1 );
	}
	memcpy( addr, ai->ai_addr, ai->ai_addrlen );
	addr->sin_port = htons( portno );
	freeaddrinfo( ai );

	return( 0 );
}
