#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <assert.h>
#include <limits.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>

#include <apci1710.h>

#include "sample_apci1710_common.h"

//------------------------------------------------------------------------------
/**
 * @brief Global data
 */
static int stop = 1;
static int fd = 0;
static unsigned char module = 0;

//------------------------------------------------------------------------------
/**
 * @brief Read an integer value from stdin within a given range
 * safely read an integer value from standard input - loop until input is correct
 * @param min Minimum accepted value
 * @param max Maximum accepted value
 * @return The read integer value
 */
int get_integer(long min, long max)
{
    long val;
    while(stop)
    {
        char buff[10];
        char * endptr = NULL;

        fgets(buff,sizeof(buff)-1,stdin);
        errno=0;
        val = strtol(buff, &endptr, 10);
        if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || (errno != 0 && val == 0)) { perror("strtol") ; continue; }
        if (endptr == buff) { fprintf(stderr, "input is not a digit\n"); continue; }
        if ( (val >= min) && (val <= max) ) return val;
        fprintf(stderr,"input is out of range\n");
    }

    return 0;
}

//----------------------------------------------------------------------------------

/**
 * @brief Signal handler for Ctrl+C
 * @param notused Unused parameter
 */
static void exit_event (int notused)
{
      stop = 0;
      printf("Sample aborted.\n");
}

//----------------------------------------------------------------------------------
/**
 * @brief apci1710 user interrupt routine
 * @param notused Unused parameter
 */
void apci1710_event(int notused)
{
    int ret = 0;
    uint32_t ulParam[3] = {0};

    for ( ; ; )
    {
        ret = ioctl(fd, CMD_APCI1710_TestInterrupt, ulParam);
        if (ret < 0)
        {
            perror("CMD_APCI1710_TestInterrupt");
            break;
        }

        /* Read while pending interrupt data (1: No interrupt, otherwise the interrupt number) */
        if (ret == 1)
             break;

        printf ("Interrupt on module %u mask %u value %u\n", ulParam[0], ulParam[1], ulParam[2]);
    }

    return;
}

//----------------------------------------------------------------------------------
/**
 * @brief Check and print error codes
 * @param function Function name
 * @param error Error code
 */
int checkForErrors(char * function, int error)
{
    /* It's an ioctl error */
    if (error < 0)
        perror(function);

    /* APCIx-171X error */
    if (error > 0)
        printf("%s error: %i", function, error);

    return error;
}

//----------------------------------------------------------------------------------

int main(int argc, char** argv)
{
    int ret = 0;
    unsigned char ucParam[5] = {0};
    uint32_t ulParam[7] = {0};
    int oflags = 0;
    int *fds = NULL;

    ret = apci1710_find_cards(&fds);

    if (ret < 1)
    {
        printf("No board found, sample will quit.\n");
        return 0;
    }

    printf ("Select the board index to use (0 to %i): ", (ret-1));
    {
        int index = get_integer(0, (ret-1));
        fd = fds[index];
    }

    printf ("Select the module index to use (0 to 3): ");
    module = get_integer(0, 3);

    ret = ioctl(fd, CMD_APCI1710_SetBoardIntRoutine);
    if (checkForErrors("CMD_APCI1710_SetBoardIntRoutine", ret) != 0)
        goto end;

    stop = 1;

    if (signal(SIGINT, exit_event) == SIG_ERR)
    {
        perror("exit_event registration");
        goto end;
    }

    if (signal(SIGIO, &apci1710_event) == SIG_ERR)
    {
        perror("apci1710_event registration");
        goto end;
    }

    if (fcntl(fd, F_SETOWN, getpid()) == -1)
    {
        perror("apci1710_event registration");
        goto end;
    }

    oflags = fcntl(fd, F_GETFL);
    if (fcntl(fd, F_SETFL, oflags | FASYNC) == -1)
    {
        perror("apci1710_event registration");
        goto end;
    }

    ucParam[0] = module;
    ucParam[1] = APCI1710_32BIT_COUNTER;
    ucParam[2] = APCI1710_SIMPLE_MODE;
    ucParam[3] = APCI1710_HYSTERESIS_ON;
    ucParam[4] = APCI1710_SIMPLE_MODE;
    ucParam[5] = APCI1710_HYSTERESIS_ON;

    ret = ioctl(fd, CMD_APCI1710_InitCounter, ucParam);
    if (checkForErrors("CMD_APCI1710_InitCounter", ret) != 0)
        goto end;

    ret = ioctl(fd, CMD_APCI1710_ClearCounterValue, module);
    if (checkForErrors("CMD_APCI1710_ClearCounterValue", ret) != 0)
        goto end;

    printf ("Enable the reference 1, disable the reference 0: ");
    ulParam[1] = get_integer(0, 1);

    printf ("Select the action to do when index, A and B event occurs\n");
    printf ("0: APCI1710_HIGH_EDGE_CLEAR_COUNTER\n");
    printf ("1: APCI1710_HIGH_EDGE_LATCH_COUNTER\n");
    printf ("2: APCI1710_LOW_EDGE_CLEAR_COUNTER\n");
    printf ("3: APCI1710_LOW_EDGE_LATCH_COUNTER\n");
    printf ("4: APCI1710_HIGH_EDGE_LATCH_AND_CLEAR_COUNTER\n");
    printf ("5: APCI1710_LOW_EDGE_LATCH_AND_CLEAR_COUNTER\n");
    ulParam[2] = get_integer(0, 5);

    ulParam[3] = 0;
    if ((ulParam[2] == 0) || (ulParam[2] == 2) || (ulParam[2] >= 4))
    {
        printf ("Select the counter value to set when a CLEAR action is set: ");
        ulParam[3] = get_integer(0, 4294967295);
    }

    printf ("Select A and B state at which the event should occurs\n");
    printf ("0: Only the index is used\n");
    printf ("1: A is high and B is low\n");
    printf ("2: A is low and B is high\n");
    printf ("3: A is high and B is high\n");
    printf ("4: A is low and B is low\n");
    ulParam[4] = get_integer(0, 4);

    ulParam[0] = module;
    ulParam[5] = 1;                                                /* Repeat the action for each encoder revolution */
    ulParam[6] = 1;                                                /* Enable the interrupt */

    ret = ioctl(fd, CMD_APCI1710_InitIndexEx, ulParam);
    if (checkForErrors("CMD_APCI1710_InitIndexEx", ret) != 0)
        goto end;

    ret = ioctl(fd, CMD_APCI1710_EnableIndex, module);
    if (checkForErrors("CMD_APCI1710_EnableIndex", ret) != 0)
        goto end;

    printf ("To quit, use the Ctrl+c key combination.\n");
    while (stop) pause();

end:
    ret = ioctl(fd, CMD_APCI1710_DisableIndex, module);
    ret = ioctl(fd, CMD_APCI1710_ResetBoardIntRoutine);

    return 0;
}
