
/*
************
* Includes *
************
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

/*
***********
* Library *
***********
*/
// Prorotypes
int Init(char *destIP, int destPort);
int SendHB(void);
int RecvHB(void);
int Close();

// Global Variable
static int                 sock = 0;
static struct sockaddr_in  address;

// Functions
int Init(char *destIP, int destPort)
{
    // If there is a socket, close it
    if (sock)
    {
        int result = Close();
        if (result != 0)
            return result;
    }

    // Build a socket
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock == -1)
        return 1; // (errno)
    
    // Configure a name to be open on the destPort
	address.sin_family = AF_INET;
	address.sin_port = htons(destPort);
	address.sin_addr.s_addr = 0;
	memset(&(address.sin_zero), '\0', 8);
    
    // Set the name to the socket
    if (bind(sock, (struct sockaddr *)(&address), sizeof (struct  sockaddr_in)) >= 0)
    {
        // Configure the name to use the IP destIP
        address.sin_addr.s_addr = inet_addr(destIP);
        return 0;
    }
    else
        return 1; // (errno)

    return 0;
}

int SendHB(void)
{
    static const char *trame = "iBootHB";

    // Send a trame from the socket to the address name
    if (sendto(sock, trame, strlen(trame), 0, (struct sockaddr *)(&address), sizeof(struct  sockaddr_in)) >= 0)
        return 0;
    else
        return 1; // (errno)
}


int RecvHB(void) 
{
    static const char   *trame = "iBootACK";
    int                 reads;
    unsigned            size;
    char                buf[132];
    struct sockaddr     from;
    fd_set              readSet;
    struct timeval      timeVal;
    
    // Configure the fd set
    FD_ZERO(&readSet);
    FD_SET(sock, &readSet);
    
    // Configure the time-out (5 seconds)
    timeVal.tv_sec = 5;
    timeVal.tv_usec = 0;
    
    // Wait for data with select
    if (select(sock + 1, &readSet, NULL, NULL, &timeVal) > 0)
    {
        // We have data, get the trame
        size = sizeof(struct sockaddr);
        reads = recvfrom(sock, buf, 128, 0, &from, &size);    
    
        if (reads == -1)
            return 1; // (errno)
        else
        {
            buf[reads] = '\0';
        
            // Check the trame
            if (strcmp(buf, trame) == 0)
                return 0;
            else
                return 1; // (errno)
        }
    }
    else
    {
        // Here is a time Out (=0) or an error (=-1)
        return 1; // (errno)
    }

  return 0;
}

int Close()
{
    if (close(sock) >= 0)
        return 0;
    else
        return 1; // (errno)
}


/*
****************************
* Main for the application *
****************************
*/
int main(int argc, char **argv)
{
    int     err_cnt = 0;
    char    *ip = NULL;
    int     port = -1;
    int     frequence = 0;
        
    // Check arguments
    if (argc != 4)
    {
        fprintf(stderr, "Usage : %s ip port freq\n", argv[0]);
        return 1;
    }

    // Get the parameters
    ip = argv[1];
    port = atoi(argv[2]);
    frequence = atoi(argv[3]);

    // Try to init a socket
    err_cnt = 0;
    while (err_cnt < 20)
    {
        if (Init(ip, port) == 0)
        {
            err_cnt = 0;
            break;
        }
        
        fprintf(stderr, "Warning: Can't build socket, retry...\n");
        err_cnt++;
        sleep(1);
    }
    
    // If too much try, exit
    if (err_cnt != 0)
    {
        fprintf(stderr, "Error: After 20 try, socket can't be build\n");
        return 1;
    }
    

    // Send HB to iBoot and receive ACK from iBoot
    while (err_cnt < 20)
    {
        if (SendHB() != 0)
        {
            fprintf(stderr, "Warning: Can't send the HB, retry...\n");
            err_cnt++;
            sleep(1);
            continue;
        }
    
        if (RecvHB() != 0)
        {
            fprintf(stderr, "Warning: Can't receive the ACK, retry...\n");
            err_cnt++;
            sleep(1);
            continue;
        }
        
        sleep(frequence);
        err_cnt = 0;
    }
    
    fprintf(stderr, "Error: After 20 try, HB/ACK can't be well send or received\n"); 
    return 1;
}
