#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <errno.h>

#include <patty/ax25.h>
#include <patty/print.h>

static void usage(int argc, char **argv, const char *message, ...) {
    if (message != NULL) {
        va_list args;

        va_start(args, message);
        vfprintf(stderr, message, args);
         fprintf(stderr, "\n");
        va_end(args);
    }

    fprintf(stderr, "usage: %s /var/run/patty/patty.sock\n", argv[0]);

    exit(1);
}

int main(int argc, char **argv) {
    patty_client *client;
    patty_client_setsockopt_if ifreq;

    uint8_t buf[4096];
    ssize_t readlen;

    patty_kiss_tnc *raw;

    int fd;

    if (argc < 2) {
        usage(argc, argv, "No patty socket provided");
    } else if (argc > 2) {
        usage(argc, argv, "Too many arguments provided");
    }

    if ((client = patty_client_new(argv[1])) == NULL) {
        fprintf(stderr, "%s: %s: %s: %s\n",
            argv[0], "patty_client_new()", argv[1], strerror(errno));

        goto error_client_new;
    }

    if ((fd = patty_client_socket(client, PATTY_AX25_PROTO_NONE, PATTY_AX25_SOCK_RAW)) < 0) {
        fprintf(stderr, "%s: %s: %s\n",
            argv[0], "patty_client_socket()", strerror(errno));

        goto error_client_socket;
    }

    strncpy(ifreq.name, "kiss0", sizeof(ifreq.name));

    ifreq.state = PATTY_AX25_SOCK_PROMISC;

    if (patty_client_setsockopt(client, fd, PATTY_AX25_SOCK_IF, &ifreq, sizeof(ifreq)) < 0) {
        fprintf(stderr, "%s: %s: %s\n",
            argv[0], "patty_client_setsockopt()", strerror(errno));

        goto error_client_setsockopt;
    }

    if ((raw = patty_kiss_tnc_new_fd(fd)) == NULL) {
        fprintf(stderr, "%s: fd %d: %s: %s\n",
            argv[0], fd, "patty_kiss_tnc_new_fd()", strerror(errno));

        goto error_kiss_tnc_new_fd;
    }

    while ((readlen = patty_kiss_tnc_recv(raw, buf, sizeof(buf), NULL)) > 0) {
        ssize_t decoded,
                offset = 0;

        patty_ax25_frame frame;

        if ((decoded = patty_ax25_frame_decode_address(&frame, buf, readlen)) < 0) {
            printf("Invalid frame address\n");

            goto error_ax25_frame_decode_address;
        } else {
            offset += decoded;
        }

        if ((decoded = patty_ax25_frame_decode_control(&frame, PATTY_AX25_FRAME_NORMAL, buf, decoded, readlen)) < 0) {
            printf("Invalid frame control\n");

            goto error_ax25_frame_decode_control;
        } else {
            offset += decoded;
        }

        if (patty_print_frame_header(stdout, &frame) < 0) {
            fprintf(stderr, "%s: %s: %s\n",
                argv[0], "patty_print_frame_header()", strerror(errno));

            goto error_io;
        }

        if (frame.type == PATTY_AX25_FRAME_XID) {
            patty_ax25_params params;

            if (patty_ax25_frame_decode_xid(&params,
                                            buf,
                                            offset,
                                            readlen) < 0) {
                printf("Invalid XID parameters\n");

                goto error_ax25_frame_decode_xid;
            } else {
                if (patty_print_params(stdout, &params) < 0) {
                    goto error_io;
                }
            }
        }

error_ax25_frame_decode_xid:
error_ax25_frame_decode_control:
error_ax25_frame_decode_address:
        if (patty_print_hexdump(stdout, buf, readlen) < 0) {
            goto error_io;
        }

        if (fflush(stdout) < 0) {
            goto error_io;
        }
    }

    patty_kiss_tnc_destroy(raw);

    patty_client_close(client, fd);

    patty_client_destroy(client);

    return 0;

error_io:
    patty_kiss_tnc_destroy(raw);

error_kiss_tnc_new_fd:
error_client_setsockopt:
error_client_socket:
    (void)patty_client_close(client, fd);

error_client_new:
    return 1;
}