#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>

#include "libcommon/stdio_util.h"
#include "libcommon/error.h"
#include "libcommon/config.h"
#include "libcommon/getopt.h"
#include "libcommon/timestamp.h"
#include "libcommon/lcm_util.h"

#include "lcmtypes/senlcm_easydaq_t.h"

#include "easydaq.h"

#define EASYDAQ_CHANNEL ("EASYDAQ")

static void
easydaq_callback (const lcm_recv_buf_t *rbuf, const char *channel, 
                  const senlcm_easydaq_t *msg, void *user)
{
    senlcm_easydaq_t *easydaq = (senlcm_easydaq_t *) user;
    easydaq->utime = msg->utime;
    easydaq->self  = msg->self;
    for (size_t i=0; i<8; i++) {
        strcpy (easydaq->relay[i].label, msg->relay[i].label);
        strcpy (easydaq->relay[i].group, msg->relay[i].group);
        easydaq->relay[i].state = msg->relay[i].state;
    }
}

static void
printf_callback (const lcm_recv_buf_t *rbuf, const char *channel, 
                 const senlcm_easydaq_t *msg, void *user)
{
    stdiou_printfb ("%-6s  %-25s %-25s %-s\n", 
                    "Relay#", "Label", "Group", "State");
    for (int i=0; i<8; i++) {
        printf ("%6d  %-25s %-25s %-s\n", i+1,
                msg->relay[i].label, msg->relay[i].group, 
                msg->relay[i].state ? "ON" : "OFF"  );
    }
}


static int
on_off (getopt_t *gopt, const char *lname)
{
    const char *string = getopt_get_string (gopt, lname);
    if (!strcasecmp (string, "on"))
        return 1;
    else if (!strcasecmp (string, "off"))
        return 0;
    else {
        getopt_do_usage (gopt, NULL);
        exit (EXIT_FAILURE);
    }   
}

static senlcm_easydaq_t *
easydaq_create (void)
{
    senlcm_easydaq_t *easydaq = calloc (1, sizeof (senlcm_easydaq_t));
    for (size_t i=0; i<8; i++) {
        easydaq->relay[i].label = (char *) malloc (256 * sizeof (char));
        easydaq->relay[i].group = (char *) malloc (256 * sizeof (char));
    }
    return easydaq;
}

static void
easydaq_destroy (senlcm_easydaq_t *easydaq)
{
    for (size_t i=0; i<8; i++) {
        free (easydaq->relay[i].label);
        free (easydaq->relay[i].group);
    }
    free (easydaq);
}

int main (int argc, char *argv[])
{
    senlcm_easydaq_t *easydaq = easydaq_create ();

    // create an lcm object and subscribe to easydaq channel
    lcm_t *lcm = lcm_create (NULL);
    senlcm_easydaq_t_subscription_t* lcm_sub = senlcm_easydaq_t_subscribe (lcm, EASYDAQ_CHANNEL, &easydaq_callback, easydaq);


    // wait for the current easydaq state
    struct timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 500000;
    int ret = lcmu_handle_timeout (lcm, &timeout);
    if (ret == 0) {
        ERROR ("Timeout: is easydaq daemon running?");
        return -1;
    }
    senlcm_easydaq_t_unsubscribe (lcm, lcm_sub);


    /* command line opts */
    getopt_t *gopt = getopt_create ();
    getopt_add_description (gopt, "Command line tool for setting/querying EasyDAQ relay-card driver.");
    getopt_add_help (gopt, NULL);
    getopt_add_spacer (gopt, "------------------By relay------------------------");
    getopt_add_string (gopt, 'a',  "all",   "", "[\"on\"|\"off\"]");
    for (int i=0; i<8; i++) {
        char relay[8];
        sprintf (relay, "%d", i+1);
        getopt_add_string (gopt, relay[0], relay, "", "[\"on\"|\"off\"]");
    }
    getopt_add_spacer (gopt, "------------------By label-------------------------");
    for (int i=0; i<8; i++)
        getopt_add_string (gopt, '\0', easydaq->relay[i].label, "", "[\"on\"|\"off\"]");
    getopt_add_spacer (gopt, "------------------By group-------------------------");
    for (int i=0; i<8; i++) {
        bool unique = 1;
        for (size_t j=0; j<i; j++)
            unique *= strcmp (easydaq->relay[j].group, easydaq->relay[i].group);
        if (unique)
            getopt_add_string (gopt, '\0', easydaq->relay[i].group, "", "[\"on\"|\"off\"]");
    }

    if (!getopt_parse (gopt, argc, argv, 1) || getopt_get_bool (gopt, "help") || gopt->extraargs->len != 0) {
        getopt_do_usage (gopt, NULL);
        exit (EXIT_SUCCESS);
    }


    /* parse commandline args */
    if (argc == 1) {
        /* do nothing special */
    }
    else if (getopt_has_flag (gopt, "all")) {
        bool state  = on_off (gopt, "all");
        for (size_t i=0; i<8; i++)
            easydaq->relay[i].state = state;
    }
    else {
        for (int i=0; i<8; i++) {
            char relaynum[8];
            sprintf (relaynum, "%d", i+1);
            if (getopt_has_flag (gopt, relaynum))
                easydaq->relay[i].state = on_off (gopt, relaynum);
            else if (getopt_has_flag (gopt, easydaq->relay[i].label))
                easydaq->relay[i].state = on_off (gopt, easydaq->relay[i].label);
            else if (getopt_has_flag (gopt, easydaq->relay[i].group))
                easydaq->relay[i].state = on_off (gopt, easydaq->relay[i].group);
        }
    }
    easydaq->self = 0;
    senlcm_easydaq_t_publish (lcm, EASYDAQ_CHANNEL, easydaq);


    /* send over our relay request */
    senlcm_easydaq_t_subscribe (lcm, EASYDAQ_CHANNEL, printf_callback, NULL);
    timeout.tv_sec = 0;
    timeout.tv_usec = 500000;
    lcmu_handle_timeout (lcm, &timeout);

    /* clean up */
    easydaq_destroy (easydaq);
    lcm_destroy (lcm);
}
