#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <inttypes.h>
#include <sys/time.h>

#include <ncurses.h>

#include <hexagram/can.h>

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

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

    fprintf(stderr, "usage: %s canif\n", argv[0]);

    return 1;
}

int main(int argc, char **argv) {
    WINDOW *win;
    hexagram_can_if *can_if;
    struct can_frame frame;

    struct timeval now, last;

    if (argc != 2) {
        return usage(argc, argv, NULL);
    }

    if ((can_if = hexagram_can_if_open(argv[1])) == NULL) {
        perror("hexagram_can_if_open()");

        goto error_can_if_open;
    }

    gettimeofday(&now, NULL);
    memcpy(&last, &now, sizeof(now));

    initscr();

    win = newwin(LINES, COLS, 0, 0);

    while (hexagram_can_if_read(can_if, &frame) >= 0) {
        gettimeofday(&now, NULL);

        if (frame.can_id == 0x280) {
            double rpm = 0.25 * (double)(frame.data[2] | (frame.data[3] << 8));

            wmove(win, 1, 0);

            wprintw(win, "%6.1lf RPM", rpm);
        } else if (frame.can_id == 0x540) {
            wmove(win, 7, 0);

            if ((frame.data[7] & 0xc) == 0xc) {
                uint8_t gear = ((frame.data[7] & 0xf0) >> 4) - 2;

                wprintw(win, "1 2 3 4 5 6");

                wmove(win, 8, 0);
                wprintw(win, "                   ");

                wmove(win, 8, gear * 2);

                wprintw(win, "_");
            } else {
                uint8_t pos = ((frame.data[7] & 0xf0) >> 4) - 2;

                wprintw(win, "P R N D S  ");

                wmove(win, 8, 0);
                wprintw(win, "                   ");

                wmove(win, 8, pos * 2);

                wprintw(win, "_");
            }
        } else if (frame.can_id == 0x5a0) {
            /*
             * Speed is expressed in units of 0.001 wheel cycles per second
             */
            double rps = 0.001 * (double)((frame.data[1] >> 1)
                                        | (frame.data[2] << 8));

            double kph = (2.032 * rps * 3600) / 1000.0;

            double g = 0.01 * (((double)frame.data[0]) - 127.0);

            wmove(win, 2, 0);
            wprintw(win, "%6.1lf kph", kph);

            wmove(win, 4, 0);
            wprintw(win, "%6.1lf  G lateral", g);
        } else if (frame.can_id == 0x420) {
            double temp = ((double)frame.data[1] - 100) / 2.0;

            wmove(win, 3, 0);

            wprintw(win, "%6.1lf °C ambient", temp);
        } else if (frame.can_id == 0x320) {
            double fuel = (double)(frame.data[2] & 0xf) / 16.0;

            wmove(win, 5, 0);
            wprintw(win, "%5.0lf%% Fuel", fuel * 100.0);
        }

        if (((now.tv_sec * 1000000) + now.tv_usec) - ((last.tv_sec * 1000000) + last.tv_usec) > 100000) {
            memcpy(&last, &now, sizeof(now));

            wrefresh(win);
        }
    }

    hexagram_can_if_close(can_if);

    return 0;

error_can_if_open:
    return 1;
}