#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cerrno>

#include <fcntl.h>
#include <unistd.h>

static constexpr uint16_t kMarkerDi8High = 0x00A8U;
static constexpr uint16_t kMarkerStandard = 0x000AU;
static constexpr uint16_t kMarkerAmplitude = 0x001AU;
static constexpr uint16_t kMarkerDo1Low = 0x00A3U;
static constexpr uint16_t kMarkerDo1High = 0x00A4U;

static const char* marker_name(uint16_t m) {
    switch (m) {
    case kMarkerDi8High:   return "DI8_REF";
    case kMarkerStandard:  return "STD";
    case kMarkerAmplitude: return "AMP";
    case kMarkerDo1Low:    return "DO1_LO";
    case kMarkerDo1High:   return "DO1_HI";
    default:               return "???";
    }
}

int main(int argc, char** argv) {
    bool show_all = false;
    const char* path = "/tmp/ttyADC_data";

    for (int i = 1; i < argc; ++i) {
        if (std::strcmp(argv[i], "--all") == 0 || std::strcmp(argv[i], "-a") == 0) {
            show_all = true;
        } else if (std::strcmp(argv[i], "-h") == 0 || std::strcmp(argv[i], "--help") == 0) {
            std::fprintf(stderr,
                "Usage: %s [OPTIONS] [tty_path]\n"
                "  tty_path          path to TTY device (default: /tmp/ttyADC_data)\n"
                "  -a, --all         show all frames, not just 0x00A8\n"
                "  -h, --help        this message\n", argv[0]);
            return 0;
        } else {
            path = argv[i];
        }
    }

    std::fprintf(stderr, "Opening %s ...\n", path);
    const int fd = open(path, O_RDONLY);
    if (fd < 0) {
        std::fprintf(stderr, "open(%s): %s\n", path, std::strerror(errno));
        return 1;
    }
    std::fprintf(stderr, "Listening (filter: %s). Ctrl+C to stop.\n",
                 show_all ? "all frames" : "0x00A8 only");

    uint8_t buf[8];
    std::size_t buf_fill = 0;
    uint64_t frame_seq = 0;
    uint64_t di8_count = 0;

    for (;;) {
        const ssize_t n = read(fd, buf + buf_fill, sizeof(buf) - buf_fill);
        if (n <= 0) {
            if (n == 0) {
                break;
            }
            if (errno == EINTR) {
                continue;
            }
            std::fprintf(stderr, "read: %s\n", std::strerror(errno));
            break;
        }
        buf_fill += static_cast<std::size_t>(n);
        if (buf_fill < 8) {
            continue;
        }

        uint16_t words[4];
        std::memcpy(words, buf, 8);
        buf_fill = 0;

        const uint16_t marker = words[0];
        const uint16_t step   = words[1];
        const int16_t  ch1    = static_cast<int16_t>(words[2]);
        const int16_t  ch2    = static_cast<int16_t>(words[3]);

        ++frame_seq;

        if (marker == kMarkerDi8High) {
            ++di8_count;
            std::printf("#%-8lu [%s] step=%5u  ch1=%6d  ch2=%6d  (di8_total=%lu)\n",
                        frame_seq, marker_name(marker), step, ch1, ch2, di8_count);
            std::fflush(stdout);
        } else if (show_all) {
            std::printf("#%-8lu [%s] step=%5u  ch1=%6d  ch2=%6d\n",
                        frame_seq, marker_name(marker), step, ch1, ch2);
        }
    }

    close(fd);
    std::fprintf(stderr, "Done. Total frames: %lu, DI8 ref frames: %lu\n", frame_seq, di8_count);
    return 0;
}
