#ifndef __TEXTREAD_H__
#define __TEXTREAD_H__

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

#include <glib.h>

#include "libcommon/error.h"

#ifdef __cplusplus
extern "C" {
#endif

#define TEXTREAD_MAX_NFIELDS 100

typedef struct _textread textread_t;
struct _textread {
    char *csvlog_prefix;
    char *csvlog_dir;
    char *csvlog_fname;
    FILE *csvlog_fid;

    char *channel;
    GString *textread_string;
    GString *csvheader_string;
    GString *const_string;

    int  nfields;
    char **lvalue;
    char **rvalue;

    bool empty;
};

static inline textread_t *
textread_create (const char *csvlog_dir, const char *csvlog_prefix)
{
    textread_t *tr = calloc (1, sizeof (textread_t));
    *tr = (textread_t) {
        .empty = true,
        .csvlog_prefix = strdup (csvlog_prefix),
        .csvlog_dir  = strdup (csvlog_dir),
        .lvalue = calloc (TEXTREAD_MAX_NFIELDS, sizeof (char *)),
        .rvalue = calloc (TEXTREAD_MAX_NFIELDS, sizeof (char *)),
        .textread_string = g_string_sized_new (4096),
        .csvheader_string = g_string_sized_new (4096),
        .const_string = g_string_sized_new (4096),
    };
    return tr;
}

static inline void
textread_destroy (textread_t *tr)
{
    if (tr) {
        free (tr->csvlog_prefix);
        free (tr->csvlog_dir);
        free (tr->csvlog_fname);

        free (tr->channel);
        g_string_free (tr->textread_string, TRUE);
        g_string_free (tr->csvheader_string, TRUE);
        g_string_free (tr->const_string, TRUE);

        free (tr->lvalue);
        free (tr->rvalue);
    }
    free (tr);
}

static inline char *
textread_format (const char *format)
{
    if (strchr (format, 's'))
        return strdup (format);
    else if (strchr (format, 'c'))
        return strdup (format);
    else // number
        return strdup ("%n");
}

#define textread_add_field(tr, name, format, data)              \
    {                                                           \
        if (tr->empty) {                                        \
            tr->lvalue[tr->nfields] = strdup (name);            \
            tr->rvalue[tr->nfields] = textread_format (format); \
            tr->nfields++;                                      \
        }                                                       \
        else                                                    \
            fprintf (tr->csvlog_fid, format ";", data);         \
    }

static inline void
textread_add_const (textread_t *tr, const char *name, int64_t value)
{
    if (tr->empty)
        g_string_append_printf (tr->const_string, "nav_t.%s.const.%s=%"PRId64";\n", tr->channel, name, value);
}

static inline void
textread_start (textread_t *tr, const char *channel)
{
    if (tr->empty) {
        tr->channel = strdup (channel);

        char csvlog_fname[NAME_MAX];
        if (strlen (tr->csvlog_prefix))
            snprintf (csvlog_fname, sizeof csvlog_fname, "%s-%s.csv", tr->csvlog_prefix, channel);
        else
            snprintf (csvlog_fname, sizeof csvlog_fname, "%s.csv", channel);
        tr->csvlog_fname = strdup (csvlog_fname);

        char fullpath[PATH_MAX];
        sprintf (fullpath, "%s/%s", tr->csvlog_dir, tr->csvlog_fname);
        tr->csvlog_fid = fopen (fullpath, "w");
        if (!tr->csvlog_fid) {
            ERROR ("fopen (%s, \"w\") failed", fullpath);
            exit (EXIT_FAILURE);
        }
    }
}

static inline void
textread_stop (textread_t *tr)
{
    if (tr->empty) {
        tr->empty = false;

        /* generate matlab textread cmd */
        g_string_printf (tr->textread_string, "[");
        for (int i=0; i<tr->nfields; i++)
            g_string_append_printf (tr->textread_string, "nav_t.%s.%s,", tr->channel, tr->lvalue[i]);
        g_string_truncate (tr->textread_string, tr->textread_string->len-1); /* backup over last comma */
        g_string_append_printf (tr->textread_string, "]=textread('%s','", tr->csvlog_fname);
        for (int i=0; i<tr->nfields; i++)
            g_string_append_printf (tr->textread_string, "%s", tr->rvalue[i]);
        g_string_append_printf (tr->textread_string, "','headerlines',2,'delimiter',';');");
        fprintf (tr->csvlog_fid, "%s\n", tr->textread_string->str);

        /* csv header tags */
        for (int i=0; i<tr->nfields; i++)
            g_string_append_printf (tr->csvheader_string, "%s;", tr->lvalue[i]);
        fprintf (tr->csvlog_fid, "%s\n", tr->csvheader_string->str);
    }
    else
        fprintf (tr->csvlog_fid, "\n");
}

static inline void
textread_gen_matlab (textread_t *tra[], size_t tra_len, const char *mfile_dir, const char *mfile_fname)
{
    char mfile_fullname[PATH_MAX];
    sprintf (mfile_fullname, "%s/%s.m", mfile_dir, mfile_fname);

    FILE *fid = fopen (mfile_fullname, "w");
    if (!fid) {
        ERROR ("fopen (%s, \"w\") failed", mfile_fullname);
        exit (EXIT_FAILURE);
    }

    fprintf (fid, "function nav_t = %s()\n", mfile_fname);
    fprintf (fid, "%% lcmlog-export autogenerated loader file %%\n");
    for (int i=0; i<tra_len; i++) {
        if (!tra[i]->empty) {
            fprintf (fid, "\n%%%s\n", tra[i]->channel);
            fprintf (fid, "%s\n", tra[i]->textread_string->str);
            if (tra[i]->const_string->len)
                fprintf (fid, "%s\n", tra[i]->const_string->str);
        }
    }
    fclose (fid);
}


#ifdef __cplusplus
}
#endif

#endif //__TEXTREAD_H__
