#include <fcntl.h>
#include <linux/gpio.h>
#include <sys/ioctl.h>

#include <cerrno>
#include <chrono>
#include <cstring>
#include <iostream>
#include <thread>

static constexpr auto s_which_gpio_chip = "/dev/gpiochip2";
static constexpr auto s_which_gpio_line = 10;

int main()
{
    int chip_fd = open(s_which_gpio_chip, O_RDWR);
    if (chip_fd < 0)
    {
        std::cout << "failed to open gpio chip: " << strerror(errno) << std::endl;
        return -1;
    }

    gpio_v2_line_request request{};
    request.num_lines = 1;
    request.offsets[0] = s_which_gpio_line;
    request.event_buffer_size = 16;
    request.config.num_attrs = 0;
    request.config.flags = GPIO_V2_LINE_FLAG_OUTPUT;

    if (ioctl(chip_fd, GPIO_V2_GET_LINE_IOCTL, &request) != 0)
    {
        std::cout << "GPIO_V2_GET_LINE_IOCTL failed: " << strerror(errno) << std::endl;
        return -2;
    }

    int line_fd = request.fd;
    if (line_fd < 0)
    {
        std::cout << "GPIO_V2_GET_LINE_IOCTL returned unexpected fd: " << line_fd << std::endl;
        return -3;
    }

    for (;;)
    {
        // set pin active
        std::cout << "setting pin to ACTIVE!" << std::endl;
        gpio_v2_line_values values_1{.bits = 1u, .mask = 1u};
        if (ioctl(line_fd, GPIO_V2_LINE_SET_VALUES_IOCTL, &values_1) != 0)
        {
            std::cout << "GPIO_V2_LINE_SET_VALUES_IOCTL failed: " << strerror(errno) << std::endl;
            return -4;
        }

        // read pin state
        gpio_v2_line_values confirm_values_1{.bits = 0u, .mask = 1u};
        if (ioctl(line_fd, GPIO_V2_LINE_GET_VALUES_IOCTL, &confirm_values_1) != 0)
        {
            std::cout << "GPIO_V2_LINE_GET_VALUES_IOCTL failed: " << strerror(errno) << std::endl;
            return -5;
        }
        std::cout << "confirming pin state as " << ((confirm_values_1.bits & 1u) ? "ACTIVE" : "INACTIVE") << "!"
                  << std::endl;

        std::this_thread::sleep_for(std::chrono::seconds(5));

        // set pin inactive
        std::cout << "setting pin to INACTIVE!" << std::endl;
        gpio_v2_line_values values_2{.bits = 0u, .mask = 1u};
        if (ioctl(line_fd, GPIO_V2_LINE_SET_VALUES_IOCTL, &values_2) != 0)
        {
            std::cout << "GPIO_V2_LINE_SET_VALUES_IOCTL failed: " << strerror(errno) << std::endl;
            return -4;
        }

        // read pin state
        gpio_v2_line_values confirm_values_2{.bits = 0u, .mask = 1u};
        if (ioctl(line_fd, GPIO_V2_LINE_GET_VALUES_IOCTL, &confirm_values_2) != 0)
        {
            std::cout << "GPIO_V2_LINE_GET_VALUES_IOCTL failed: " << strerror(errno) << std::endl;
            return -5;
        }
        std::cout << "confirming pin state as " << ((confirm_values_2.bits & 1u) ? "ACTIVE" : "INACTIVE") << "!"
                  << std::endl;

        std::this_thread::sleep_for(std::chrono::seconds(5));
    }
}
 