///////////////////////////////////////////////////////////////////////////////////////
//  file   : tcp_server.c
//  author : Alain Greiner
//  date   : march 2020
///////////////////////////////////////////////////////////////////////////////////////
//  This file describes a single thread TCP server chat application.
//  The client send the first message.
///////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <almosmkh.h>
#include <unistd.h>
#include <sys/socket.h>

#define   BUF_SIZE             256

#define   MAX_PENDING_CONNECT  16

#define   LOCAL_PORT           13
#define   LOCAL_ADDR           0xBBBBBBBB

////////////////////////////
void server_chat( int fdid )
{
    char   buffer[BUF_SIZE];   // string buffer (for send and receive)
    int    size;               // string length (including NUL)
    int    nbytes;             // number of characters actually sent or received

    // chat loop
    while( 1 )
    {
        // display client prompt
        printf("\n[from client] ");

        // get client message
        nbytes = recv( fdid, buffer, BUF_SIZE, 0 );

        if( nbytes < 0 )
        {
            printf("\n\n[server] receive error => return to main\n" );
            return;
        }
        else if( nbytes == 0 )
        {
            printf("\n\n[server] receive EOF => return to main\n" );
            return;
        }

        // display client message
        printf("%s\n", buffer );

        // display server prompt
        printf("\n[from server] ");

        // build server message
        size = get_string( buffer , BUF_SIZE );

        // exit chat function when local message is the "exit" string
        if( strncmp( "exit" , buffer , 4 ) == 0 )
        {
            return;
        }

        // send server message
        nbytes = send( fdid , buffer , size , 0 ) ;

        if( nbytes != size )
        {
            printf("\n[server] send error => chat return to main\n" );
            return;
        }
    }  // end chat loop

}  // end server_chat()


////////////////
int main( void )
{
    int                  pid;          // process pid
    int                  listen_fdid;  // listen socket identifier
    int                  chat_fdid;    // chat socket identifier
    int                  error;

    struct sockaddr_in   server_sin;   // server socket IP address
    struct sockaddr_in   client_sin;   // client socket IP address

    unsigned long long   start_cycle;

    int                  addr_length = sizeof(sockaddr_t);

    // get  start cycle
    get_cycle( &start_cycle );

    pid = getpid();

    printf("\n[server] starts at cycle %d\n",
    (unsigned int)start_cycle );

    // 1. create TCP socket
    listen_fdid = socket( AF_INET,
                        SOCK_STREAM,
                        0 );

    if( listen_fdid < 0 )
    {
        printf("\n[server error] cannot create socket\n");
        exit( 0 );
    }
    else
    {
        printf("\n[server] created socket[%x,%d]\n", pid, listen_fdid );
    }

    // 2. bind local socket
    server_sin.sin_domain = HTONS( AF_INET );
    server_sin.sin_addr   = HTONL( LOCAL_ADDR );
    server_sin.sin_port   = HTONS( LOCAL_PORT );

    error = bind( listen_fdid,
                  (sockaddr_t *)(&server_sin),
                  sizeof(sockaddr_t) );
    if( error )
    {
        printf("\n[server error] bind failure for socket[%x,%d]\n",
        pid, listen_fdid );
        exit( 0 );
    }

    printf("\n[server] listening socket[%x,%d] bound : [%x,%x]\n",
    pid, listen_fdid, server_sin.sin_addr, server_sin.sin_port );

    // 3. Listen clients connection requests
    error = listen( listen_fdid , MAX_PENDING_CONNECT );
                     
    if( error )
    {
        printf("\n[server error] listen failure for socket[%x,%d]\n",
        pid, listen_fdid );
        exit( 0 );
    }

    printf("\n[server] listening socket[%x,%d] waiting connection request\n",
    pid, listen_fdid );
    
    // 4. accept connection(s)
    chat_fdid = accept( listen_fdid,
                        (sockaddr_t *)&client_sin,
                        &addr_length );

    if( chat_fdid < 0 )
    {
        printf("\n[server error] accept failure on socket[%x,%d]\n",
        pid, listen_fdid );
        exit( 0 );
    }

    printf("\n[server] chat socket[%x,%d] accepted client [%x,%x]\n",
    pid, chat_fdid , client_sin.sin_addr , client_sin.sin_port );

    // 5. call chat function
    server_chat( chat_fdid ); 

    // 6. close listen and chat sockets
    close( chat_fdid );
    close( listen_fdid );

    printf("\n[server] closed both <listen> & <chat> sockets\n" );

    exit(0);

    return 0;
}
