/****************************************************************************/
/* Copyright 2008 MBARI.                                                    */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
#include <stdio.h>
#include <string.h>

#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>

#define DEBUG 0
#define dprintf if ( DEBUG ) printf

/* globals */
static char servName[128] = "";
static char servResponse[512] = "";

static int servPort;
static int servSock;
static int servConnected = 0;
static int servTimeout = 500;  /* default timeout in milliseconds */
static struct timeval servTimeVal;


static struct hostent *servHp;

/************************* local function prototypes *************************/
static int openSocket();
static int getHost();
static int cmdResponse(char* cmd, char* resp);
/************************* local function prototypes *************************/


int connectAutoRov(char* name, int port)
{
    strcpy(servName, name);
    servPort = port;

    dprintf("connectAutoRov(%s, %d)\n", servName, servPort);
    
    if ( openSocket() < 0 )
        return -1;
    
    if ( getHost() < 0 )
    {
        close(servSock);
        servConnected = 0;
        return -1;
	}
	
    servConnected = 1;

    return 0;
}

void disconnectAutoRov()
{
    servConnected = 0;
    close(servSock);
}

int setAutoRovTimeOut(int timeout)
{
    dprintf("setAutoRovTimeOut(%d)\n", timeout);
    
    if ( (timeout < 0) || (timeout > 5000) )
    {
        dprintf("setAutoRovTimeOut(%d) range error (0 - 5000 ms)\n", timeout);
        return -1;
    }

    servTimeout = timeout;

    return 0;
}

void getAutoRovSrv(char* name)
{
    strcpy(name, servName);
}

int getAutoRovPort()
{
    return servPort;
}

int getAutoRovConnect()
{
    return servConnected; 
}

int sendAutoRovCmd(char* cmd, char* resp)
{
    int ret_val = cmdResponse(cmd, resp); 
    
    dprintf("sendAutoRovCmd(cmd = '%s', resp = '%s')\n", cmd, resp);

    return ret_val;
}

/*********************** local function implementation ***********************/

static int openSocket()
{
    struct timeval tv;

    /* if you're connected bail out */
    if ( servConnected )
    {
        dprintf("openSocket(): Already connected\n");
        return -1;
    }
    
    servSock = socket(AF_INET, SOCK_DGRAM, 0);
    
    if ( servSock < 0 )
    {
        perror("socket");
        return -1;
    }

    fcntl(servSock, F_SETFL, O_NONBLOCK);

    /* set the server timeout */
    tv.tv_sec = 0;
    tv.tv_usec = (servTimeout * 1000);

    /* inc the seconds if necessary */
    while ( tv.tv_usec >= 1000000L ) 
    {
		tv.tv_usec -= 1000000L;
		tv.tv_sec++;
        dprintf("openSocket() tv.tv_sec++\n");
	}

    /* set global timeval struct */
    servTimeVal.tv_sec = tv.tv_sec;
    servTimeVal.tv_usec = tv.tv_usec;
    
    dprintf("openSocket() servTimeVal.tv_sec = %ld, servTimeVal.tv_usec = %ld\n", 
            servTimeVal.tv_sec, servTimeVal.tv_usec);
    
#if 0
    dprintf("openSocket() tv.tv_sec = %ld, tv.tv_usec = %ld\n", 
            tv.tv_sec, tv.tv_usec);
    /* if the servTimeout is zero then the socket blocks forever */
    if ( servTimeout != 0 )
    {
        if ( setsockopt(servSock, SOL_SOCKET, SO_RCVTIMEO, 
                        (char *)&tv, sizeof(tv)) < 0 )
        {
            perror("setsockopt");
            close(servSock);
            return -1;
        }
    }
#endif

    return 0;
}

static int getHost()
{
    servHp = gethostbyname(servName);
    
    if ( servHp == 0 )
    {
        perror("gethostbyname");
        return -1;
    }

    return 0;
}

static int cmdResponse(char* cmd, char* resp)
{
    int ret;
    socklen_t so_len;
    struct sockaddr_in to, from;

    /* select vars */
    struct timeval tv;
    fd_set readfds;


    /* clear out the response buffer */
    resp[0] = '\0';
    
    /* if you're not connected, reopen the socket */
    if ( !servConnected )
    {
        dprintf("cmdResponse(...): Not connected\n");
        return -1;
    }
    
    /* even though you have a good socket, don't assume the server is up
    until you get a response */
    servConnected = 0;

    bcopy((char *)servHp->h_addr, (char *)&to.sin_addr, servHp->h_length);
    to.sin_family = AF_INET;
    to.sin_port = htons(servPort);
    so_len = sizeof(struct sockaddr_in);
        
    ret = sendto(servSock, cmd, strlen(cmd), 0, (struct sockaddr *)&to, so_len);
    
    if ( ret < 0 ) 
    {
        perror("sendto");
        close(servSock);
        return -1;
    }

    /* use select to see if a datagram is ready to read */
    tv.tv_sec = servTimeVal.tv_sec;
    tv.tv_usec = servTimeVal.tv_usec;

    FD_ZERO(&readfds);
    FD_SET(servSock, &readfds);

    /* don't care about writefds and exceptfds */
    select(servSock + 1, &readfds, NULL, NULL, &tv);

    if ( FD_ISSET(servSock, &readfds) )
    {
        /* we got data, read it */
        ret = recvfrom(servSock, resp, 256, 0, 
                       (struct sockaddr *)&from, &so_len);
    }
    else
    {
        /* we timed out */
        dprintf("cmdResponse(...): select() timed out\n");
        ret = -1;
    }

    /* check the results from the read */
    if ( ret < 0 ) 
    {
        perror("recvfrom");
        close(servSock);
        return -1;
    }
    else
    {
        resp[ret] = '\0';
    }

    /* your got a response, the server is up */
    servConnected = 1;
    return 0;
}

