/*
 *  sample/sample.c
 *
 *  A sample program
 *
 *  Copyright (C) 2004  Phoenix Grid Computing Project
 *                      ( http://www.logos.ic.i.u-tokyo.ac.jp/phoenix/ )
 *
 *  2004-11-26  Implemented basic functions 
 *              by Kaneda Kenji
 */


#include "grid.h"

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <error.h>
#include <unistd.h>
#include <string.h>
#include <openssl/ssl.h>


enum {
	COMMON_PORT = 10000
};

const struct opt_t client_opt =
{
	"keys/client/priv_key.pem",	/* Private Key */
	"kaneda",			/* Password */
	"keys/client/cert.pem",		/* Certification */
	"keys/cacert1.pem" 		/* CA Certification */
};


const struct opt_t server_opt =
{
	"keys/server/priv_key.pem", 	/* Private Key */
	"alice",			/* Password */
	"keys/server/cert.pem",		/* Certification */
	"keys/cacert1.pem"		/* CA Certification */
};

#if 0
const struct opt_t server_opt =
{
	"keys/dummy/priv_key.pem", 	/* Private Key */
	"bobbb",			/* Password */
	"keys/dummy/cert.pem",		/* Certification */
	"keys/cacert2.pem"		/* CA Certification */
};
#endif

/************************************************/

pid_t
Fork(void)
{
     pid_t ret;

     ret = fork();

     if ( ret == -1 ) {
	     perror ( "fork" );
	     exit ( 1 );
     }

     return ret;
}

/************************************************/

void 
client_init ( void )
{
	grid_initialize ( &client_opt );
}

void 
wait_for_server_to_start ( void )
{
	enum { SLEEP_TIME = 1 };
	sleep ( SLEEP_TIME );
}

int
client_connect( const char *name, int port )
{
	int sockfd;
	in_addr_t in_addr;
	struct sockaddr_in sin;
	
	assert ( name != NULL );
	
	sockfd = Socket ( AF_INET, SOCK_STREAM, 0 );
	
	in_addr = in_addr_of_hostname ( name );
	sin = create_inet_sockaddr ( in_addr, port );
	
	Grid_connect ( sockfd, (struct sockaddr *)&sin, sizeof(sin) );
	
	return sockfd;
}

void
client_run ( void )
{
	int sockfd;
	int n;

	client_init ( );

	wait_for_server_to_start ( );
	
	sockfd = client_connect ( "localhost", COMMON_PORT );

	n = Grid_send ( sockfd, "HELLO", 6, 0 );
	printf ( " send : n = %d\n", n );

	grid_finalize ( );
}

/************************************************/

void 
server_init ( void )
{
	grid_initialize ( &server_opt );
}

int
server_listen ( int port )
{
	enum { LISTENQ = 1024 };
	int sockfd;
	in_addr_t in_addr;
	struct sockaddr_in sin;
	
	sockfd = Socket ( AF_INET, SOCK_STREAM, 0 );
	Setsockopt_reuseaddr ( sockfd );
	
	in_addr = htonl ( INADDR_ANY );
	sin = create_inet_sockaddr ( in_addr, port );
	
	Grid_bind ( sockfd, (struct sockaddr *)&sin, sizeof(sin) );
	
	Grid_listen ( sockfd, LISTENQ );
	
	return sockfd;
}

void
server_run ( void )
{
	int sockfds[2];
	int n;
	enum { BUF_SIZE = 16 };
	char buf[BUF_SIZE];

	server_init ( );

	sockfds[0] = server_listen ( COMMON_PORT );
	sockfds[1] = Grid_accept ( sockfds[0], NULL, NULL );

	for ( ; ; ) {
		fd_set fds;

		FD_ZERO ( &fds );
		FD_SET ( sockfds[1], &fds );
		printf ( " select begin\n" );
		n = Grid_select ( sockfds[1] + 1,  &fds, NULL, NULL, NULL );
		printf ( " select end: n = %d\n", n );

		n = Grid_recv ( sockfds[1], buf, 1, 0 );
		printf ( " recv : n = %d, buf = '%c'\n", n, buf[0] );

	}

	grid_finalize ( );
}

int
main ( void ) 
{
	pid_t pid;

	pid = Fork ( );

	if ( pid == 0 ) {
		client_run ( );
	} else {
		server_run ( );
	}

	sleep ( 1024 );

	return 0;
}
