/******************************************************************************!
 * \file wiring_digital-gpiod.c
 * \author Sebastien Beaugrand
 * \sa http://beaugrand.chez.com/
 * \copyright CeCILL 2.1 Free Software license
 ******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <gpiod.h>
#include "wiring.h"
#include "debug.h"

#define GPIOD_MAX_PIN_COUNT 24
struct gpiod_chip* chips[GPIOD_MAX_PIN_COUNT] = { NULL };
struct gpiod_line* lines[GPIOD_MAX_PIN_COUNT] = { NULL };

#ifndef NDEBUG
/******************************************************************************!
 * \fn debugValues
 ******************************************************************************/
void debugValues()
{
    int n;
    int i;

    n = 0;
    for (i = 0; i < GPIOD_MAX_PIN_COUNT; ++i) {
        if (lines[i] != NULL) {
            fprintf(stderr, "%d ", gpiod_line_get_value(lines[i]));
            ++n;
        }
    }
    if (n > 0) {
        fprintf(stderr, "\n");
    }
}
#endif

/******************************************************************************!
 * \fn digitalInit
 ******************************************************************************/
int digitalInit(uint8_t pin, uint8_t mode)
{
    struct gpiod_chip* chip;
    struct gpiod_line* line;
    char buf[6];

    line = lines[pin - 1];
    if (line == NULL) {
        if (snprintf(buf, 6, "pin%u", pin) >= 6) {
            ERROR("snprintf");
            return 1;
        }
        line = gpiod_line_find(buf);
        if (line == NULL) {
            ERROR("gpiod_line_find");
            return 2;
        }
        lines[pin - 1] = line;
        chip = gpiod_line_get_chip(line);
        chips[pin - 1] = chip;
    }

    if (gpiod_line_is_requested(line)) {
        gpiod_line_release(line);
    }
    if (mode == INPUT) {
        if (gpiod_line_request_input(line, "wiring") < 0) {
            ERRNO("gpiod_line_request_input");
            return 3;
        }
    } else {
        if (gpiod_line_request_output(line, "wiring", 0) < 0) {
            ERRNO("gpiod_line_request_output");
            return 3;
        }
    }

    return 0;
}

/******************************************************************************!
 * \fn digitalRead
 ******************************************************************************/
int digitalRead(uint8_t pin)
{
    struct gpiod_line* line;
    int val;

#   ifndef NDEBUG
    debugValues();
#   endif
    line = lines[pin - 1];
    if (line == NULL) {
        ERROR("line == NULL");
        return -1;
    }

    val = gpiod_line_get_value(line);
    if (val < 0) {
        ERROR("gpiod_line_get_value");
        return GPIO_ERROR_READ;
    }
    return val;
}

/******************************************************************************!
 * \fn digitalWrite
 ******************************************************************************/
void digitalWrite(uint8_t pin, uint8_t val)
{
    struct gpiod_line* line;

#   ifndef NDEBUG
    debugValues();
#   endif
    line = lines[pin - 1];
    if (line == NULL) {
        ERROR("line == NULL");
        return;
    }

    gpiod_line_set_value(line, val);
}

/******************************************************************************!
 * \fn digitalQuit
 ******************************************************************************/
int digitalQuit(uint8_t pin)
{
    struct gpiod_chip* chip;
    struct gpiod_line* line;
    int i;

#   ifndef NDEBUG
    debugValues();
#   endif
    line = lines[pin - 1];
    if (line == NULL) {
        ERROR("line == NULL");
        return 1;
    }
    gpiod_line_release(line);
    lines[pin - 1] = NULL;

    chip = chips[pin - 1];
    if (chip == NULL) {
        ERROR("chip == NULL");
        return 2;
    }
    chips[pin - 1] = NULL;
    for (i = 0; i < GPIOD_MAX_PIN_COUNT; ++i) {
        if (chips[i] == chip) {
            return 0;
        }
    }
    gpiod_chip_close(chip);

    return 0;
}