#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/select.h>
#include <time.h>
#include <sys/time.h>

#define GPIO_PATH "/sys/class/gpio"

void export_gpio(int pin) {
    int fd;
    char buf[64];

    fd = open(GPIO_PATH "/export", O_WRONLY);
    if (fd < 0) {
        printf("Failed to open export for writing");
    }

    snprintf(buf, sizeof(buf), "%d", pin);
    if (write(fd, buf, strlen(buf)) < 0) {
        printf("Failed to export gpio");
        close(fd);
    }

    close(fd);
}

void unexport_gpio(int pin) {
    int fd;
    char buf[64];

    fd = open(GPIO_PATH "/unexport", O_WRONLY);
    if (fd < 0) {
        printf("Failed to open unexport for writing");
    }

    snprintf(buf, sizeof(buf), "%d", pin);
    if (write(fd, buf, strlen(buf)) < 0) {
        printf("Failed to unexport gpio");
        close(fd);
    }

    close(fd);
}

void set_gpio_direction(int pin, const char *direction) {
    int fd;
    char buf[64];

    snprintf(buf, sizeof(buf), GPIO_PATH "/gpio%d/direction", pin);
    fd = open(buf, O_WRONLY);
    if (fd < 0) {
        printf("Failed to open gpio direction for writing");
    }

    if (write(fd, direction, strlen(direction)) < 0) {
        printf("Failed to set direction");
        close(fd);
    }

    close(fd);
}

void set_gpio_edge(int pin, const char *edge) {
    int fd;
    char buf[64];

    snprintf(buf, sizeof(buf), GPIO_PATH "/gpio%d/edge", pin);
    fd = open(buf, O_WRONLY);
    if (fd < 0) {
        printf("Failed to open gpio edge for writing");
    }

    if (write(fd, edge, strlen(edge)) < 0) {
        printf("Failed to set edge");
        close(fd);
    }

    close(fd);
}

int main(int argc, char *argv[])
{
    int fd;
    char buf[64];
    fd_set set;
    struct timeval tv;

    // 检查参数数量
    if (argc < 2) {
        printf("Usage: %s <argument1> ...\n", argv[0]);
        return 1;
    }

    printf("Program name: %s\n", argv[0]);
    for (int i = 1; i < argc; i++) {
        printf("Argument %d: %s\n", i, argv[i]);
    }
    

    int gpio_pin = atoi(argv[1]);
    printf("test interrupt gpio pin %d\n", gpio_pin);

    unexport_gpio(gpio_pin);
    // export GPIO
    export_gpio(gpio_pin);

    // Set GPIO as input mode
    set_gpio_direction(gpio_pin, "in");

    // Set GPIO interrupt as falling mode
    set_gpio_edge(gpio_pin, "falling");

    // Open GPIO value file
    snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio_pin);
    fd = open(buf, O_RDONLY);
    if (fd < 0) {
        printf("Failed to open gpio value file");
        return EXIT_FAILURE;
    }

    // First read, clear buf
    read(fd, buf, sizeof(buf));

    printf("GPIO[%d] start monitoring\n", gpio_pin);
    while (1) {
        FD_ZERO(&set);
        FD_SET(fd, &set);
       
        // Wait interrupt
        int ret = select(fd + 1, NULL, NULL, &set, NULL);
        if (ret < 0) {
            printf("select failed");
            close(fd);
            unexport_gpio(gpio_pin);
            return EXIT_FAILURE;
        } else if (ret == 0) {
            printf("Timeout occurred! No GPIO interrupt.\n");
        } else {
            if (FD_ISSET(fd, &set)) {
                // Read GPIO value
                lseek(fd, 0, SEEK_SET);
                read(fd, buf, sizeof(buf));
                // 获取当前时间
                gettimeofday(&tv, NULL);
                // 打印时间戳
                printf("Current time: %ld seconds, %ld microseconds\n", tv.tv_sec, tv.tv_usec);
                // printf("GPIO interrupt occurred, value: %c\n", buf[0]);
            }
        }
    }

    close(fd);
    unexport_gpio(gpio_pin);
    return EXIT_SUCCESS;
}
