/// Includes
#include <devices/input.h>
#include <devices/inputevent.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <string.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <clib/socket_protos.h>
#include <pragmas/socket_pragmas.h>
///
/// Prototypes
void HandleConnection( int peer );
BOOL RecvIEvent( int peer, struct InputEvent *event );
int NetRecvT( int sock, char *buffer, int len, int flags, int timeout );
BOOL RecvWord( int sock, void *buf, int timeout );
///
/// Some macros
#define bzero( buf, size )              memset( buf, 0, size )
#define RecvByte( sock, buf, timeout )  ( NetRecvT( sock, buf, 1, 0, timeout ) > 0 )
#define NET_TIMEOUT                     (-2)
///
/// Data
static char Version[] = "$VER: Twiny 1.0 (21.4.00) by Simone Tellini";

struct Library *SocketBase;
///

/// main
int main( int argc, char *argv[] )
{
    if( SocketBase = OpenLibrary( "bsdsocket.library", 0 )) {
        int sock;

        sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );

        if( sock >= 0 ) {
            struct sockaddr_in  addr;

            memset( &addr, 0, sizeof( addr ));

            addr.sin_family = AF_INET;
            addr.sin_port   = htons( 1414 );

            if( bind( sock, (struct sockaddr *)&addr, sizeof( addr )) >= 0 ) {
                ULONG   sigs = SIGBREAKF_CTRL_C;

                listen( sock, 1 );

                for(;;) {
                    fd_set  fds;

                    FD_ZERO( &fds );
                    FD_SET( sock, &fds );

                    Printf( "waiting for a connection...\n" );

                    if( WaitSelect( sock + 1, &fds, NULL, NULL, NULL, &sigs ) <= 0 )
                        break;

                    if( FD_ISSET( sock, &fds )) {
                        int     peer;
                        LONG    len = sizeof( &addr );

                        if(( peer = accept( sock, (struct sockaddr *)&addr, &len )) >= 0 ) {

                            HandleConnection( peer );

                            CloseSocket( peer );

                        } else
                            Printf( "Incoming connection couldn't be accepted!\n" );
                    }
                }

            } else
                Printf( "Cannot bind()!\n" );

            CloseSocket( sock );

        } else
            Printf( "Can't create a socket!\n" );

        CloseLibrary( SocketBase );

    } else
        Printf( "Can't open socket.library!\n" );

    return( 0 );
}
///
/// HandleConnection
void HandleConnection( int peer )
{
    struct MsgPort *Port;

    if( Port = CreateMsgPort() ) {
        struct IOStdReq *req;

        if( req = ( struct IOStdReq * )CreateIORequest( Port, sizeof( struct IOStdReq ))) {

            if( !OpenDevice( "input.device", 0, ( struct IORequest * )req, 0L )) {

                for(;;) {
                    fd_set              fds;
                    struct InputEvent   event;
                    ULONG               sigs = SIGBREAKF_CTRL_C;

                    memset( &event, 0, sizeof( event ));

                    FD_ZERO( &fds );
                    FD_SET( peer, &fds );

                    if( WaitSelect( peer + 1, &fds, NULL, NULL, NULL, &sigs ) <= 0 )
                        break;

                    if( FD_ISSET( peer, &fds ) && ( recv( peer, (char *)&sigs, 1, MSG_PEEK ) <= 0 )) {
                        Printf( "remote connection closed\n" );
                        break;
                    }

                    if( FD_ISSET( peer, &fds ) && RecvIEvent( peer, &event )) {

                        req->io_Command = IND_WRITEEVENT;
                        req->io_Length  = sizeof( struct InputEvent );
                        req->io_Data    = &event;

                        DoIO(( struct IORequest * )req );
                    }
                }               

                CloseDevice(( struct IORequest * )req );
            }

            DeleteIORequest( req );
        }

        DeleteMsgPort( Port );
    }
}
///
/// RecvIEvent
BOOL RecvIEvent( int peer, struct InputEvent *event )
{
    BOOL    ok;

    ok = RecvByte( peer, &event->ie_Class,     5 ) &&
         RecvByte( peer, &event->ie_SubClass,  5 ) &&
         RecvWord( peer, &event->ie_Code,      5 ) &&
         RecvWord( peer, &event->ie_Qualifier, 5 ) &&
         RecvWord( peer, &event->ie_X,         5 ) &&
         RecvWord( peer, &event->ie_Y,         5 );

    return( ok );
}
///
/// NetRecvT
int NetRecvT( int sock, char *buffer, int len, int flags, int timeout )
{
    struct timeval  to;
    fd_set          fds;
    int             ret;
    ULONG           sigs = SIGBREAKF_CTRL_C;

    memset( &to, 0, sizeof( to ));

    to.tv_sec = timeout;

    FD_ZERO( &fds );
    FD_SET( sock, &fds );

    ret = WaitSelect( sock + 1, &fds, NULL, NULL, &to, &sigs );

    if(( ret > 0 ) && FD_ISSET( sock, &fds ))
        ret = recv( sock, buffer, len, flags );
    else if( ret == 0 )
        ret = NET_TIMEOUT;

    return( ret );
}
///
/// RecvWord
BOOL RecvWord( int sock, void *buf, int timeout )
{
    BOOL    ok = FALSE;

    if( NetRecvT( sock, buf, 2, 0, timeout ) >= 2 ) {
        *((short *)buf ) = ntohs( *((short *)buf ));
        ok   = TRUE;
    }

    return( ok );
}
///
