#include "wdt.h"
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

#define WDT_DEVICE          "/dev/watchdog"
#define WDT_MAX_TIMEOUT     40

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
int watchdog_announce(char* name, int timeout)
{
    int fd;
    unsigned char* buf;

    if ((timeout < 0) || (timeout > WDT_MAX_TIMEOUT)) return WDT_INVALID_TIMEOUT;

    fd = open(WDT_DEVICE, O_RDWR);
    if (fd < 0) return WDT_ALREADY_REGISTERED;
    buf = malloc(strlen("start") + 2 + strlen(name));
    if (buf == NULL) return WDT_RESOURCE_ERROR;
    strcpy(buf, "start ");
    strcat(buf, name);
    if (write(fd, buf, strlen("start") + 2 + strlen(name)) < 0)
        switch (errno)
        {
            case EBADF:
                free(buf);
                return WDT_UNKNOWN_HANDLE;
            default:
                free(buf);
                return WDT_RESOURCE_ERROR;
        }
    free(buf);

    watchdog_settimeout(fd, timeout);
    close(fd);
    
    return WDT_OK;
}

#if 0
/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
int watchdog_log(const char * __restrict format, ...)
{
    FILE *fd;
	va_list arg;

    fd = fopen("/var/wdtlog", "a+");

	va_start(arg, format);
	vfprintf(fd, format, arg);
	va_end(arg);

    fclose(fd);
}
#endif

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
int watchdog_register(char* name)
{
    int fd;
    unsigned char* buf;

    fd = open(WDT_DEVICE, O_RDWR);
    if (fd < 0) {
        return WDT_ALREADY_REGISTERED;
    }
    buf = malloc(strlen("register") + 2 + strlen(name));
    if (buf == NULL) {
        return WDT_RESOURCE_ERROR;
    }
    strcpy(buf, "register ");
    strcat(buf, name);
    if (write(fd, buf, strlen("register") + 2 + strlen(name)) < 0)
    {
        switch (errno)
        {
            case EBADF:
                free(buf);
                return WDT_UNKNOWN_HANDLE;
            default:
                free(buf);
                return WDT_RESOURCE_ERROR;
        }
    }
    free(buf);
    
    return fd + 1;
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
int watchdog_get_fd(int handle)
{
    return handle - 1;
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
int watchdog_release(int handle)
{
    unsigned char *buf;

    handle--;
    buf = malloc(strlen("release") + 1);
    if (buf == NULL) return WDT_RESOURCE_ERROR;
    strcpy(buf, "release");
    if (write(handle, buf, strlen("release") + 1) < 0) {
        switch (errno)
        {
            case EBADF:
                free(buf);
                return WDT_UNKNOWN_HANDLE;
            default:
                free(buf);
                return WDT_RESOURCE_ERROR;
        }
    }
    free(buf);
    close(handle);
    
    return WDT_OK;
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
int watchdog_settimeout(int handle, int timeout)
{
    unsigned char *buf;

    handle--;
    if ((timeout <= 0) || (timeout > WDT_MAX_TIMEOUT)) return WDT_INVALID_TIMEOUT;
    buf = malloc(strlen("timeout") + 5);
    if (buf == NULL) return WDT_RESOURCE_ERROR;
    sprintf(buf, "timeout %d", timeout);
    if (write(handle, buf, strlen(buf)) < 0)
        switch (errno)
        {
            case EBADF:
                free(buf);
                return WDT_UNKNOWN_HANDLE;
            default:
                free(buf);
                return WDT_RESOURCE_ERROR;
        }
    free(buf);
    
    return WDT_OK;
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
int watchdog_trigger(int handle)
{
    handle--;
    if (write(handle, "trigger", strlen("trigger")) < 0)
        switch (errno)
        {
            case EBADF:
                return WDT_UNKNOWN_HANDLE;
            default:
                return WDT_RESOURCE_ERROR;
        }
    
    return WDT_OK;
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
int watchdog_set_non_blocking(int fd)
{
    int flags;

    flags = fcntl(fd, F_GETFL);
    if (flags == -1)
    {
        switch (errno)
        {
            case EBADF:
                return WDT_UNKNOWN_HANDLE;
            default:
                return WDT_RESOURCE_ERROR;
        }
    }

    if ((flags & O_NONBLOCK)) return WDT_OK;
    flags |= O_NONBLOCK;
    if (fcntl(fd, F_SETFL, flags))
    {
        switch (errno)
        {
            case EBADF:
                return WDT_UNKNOWN_HANDLE;
            default:
                return WDT_RESOURCE_ERROR;
        }
    }

    return WDT_OK;
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
int watchdog_set_blocking(int fd)
{
    int flags;

    flags = fcntl(fd, F_GETFL);
    if (flags == -1)
    {
        switch (errno)
        {
            case EBADF:
                return WDT_UNKNOWN_HANDLE;
            default:
                return WDT_RESOURCE_ERROR;
        }
    }

    if ((flags & O_NONBLOCK) == 0) return WDT_OK;

    flags &= ~O_NONBLOCK;
    if (fcntl(fd, F_SETFL, flags))
    {
        switch (errno)
        {
            case EBADF:
                return WDT_UNKNOWN_HANDLE;
            default:
                return WDT_RESOURCE_ERROR;
        }
    }

    return WDT_OK;
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
int watchdog_wait(int handle)
{
    char c;
    int ret;
    
    handle--;
    watchdog_set_blocking(handle);
    ret = read(handle, (void*)&c, 1);
    if (ret < 0)
        switch (errno)
        {
            case EBADF:
                return WDT_UNKNOWN_HANDLE;
            default:
                return WDT_RESOURCE_ERROR;
        }
    return WDT_REQUEST;
}


/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
int watchdog_poll(int handle)
{
    char c;
    int ret;
    
    handle--;
    watchdog_set_non_blocking(handle);
    ret = read(handle, (void*)&c, 1);
    if (ret < 0)
        switch (errno)
        {
            case EBADF:
                return WDT_UNKNOWN_HANDLE;
            case EAGAIN:
                return WDT_OK;
            default:
                return WDT_RESOURCE_ERROR;
        }
    return WDT_REQUEST;
}


