/* function definition to parse a string instead of a file
 */
#include "types.h"

/* Parse a string with Flex+Bison instead of a file / stdout */
int grammar_parse_string(const GString *buf) {
    int res;
    yy_scan_string(buf->str);
    res = yyparse();
    yylex_destroy();
    return res;
}

/* Create a BotExpenseEntry with data fed from the parser. If 'previous' is null, initialize as first expense and 'totalAmount' will be 0
 * Returns NULL if error getting local time.
 */
BotExpenseEntry *create_bot_expense_entry
(
 const BotExpenseEntry *previous,
 const BotCommand cmd,
 const double value,
 GString* user,
 GString* notes
)
{
    unsigned int id;
    double totalAmount;
    double signedValue = value;
    BotExpenseEntry *entry = malloc(sizeof *entry); // TODO this should be freed once it's written to registry
    struct tm *lt = malloc(sizeof *lt);
    time_t t = time(NULL);

    /* get the local time */
    localtime_r(&t, lt);
    if(lt == NULL) {
        fprintf(stderr, "Unable to get localtime.\n");
        return NULL;
    }

    if(cmd == BOT_CMD_OUT) {
        signedValue = -value;
    }

    if(previous == NULL) {
        id = 1;
        totalAmount = signedValue;
    } else {
        id = previous->id + 1;
        totalAmount = previous->totalAmount + signedValue; /* value can be negative */
    }

    entry->id = id;
    entry->time = *lt;
    entry->value = signedValue;
    entry->totalAmount = totalAmount;
    entry->user = user;                             /* we are setting the pointer here, not copying! */
    entry->notes = notes;
    entry->_serialized = false;

    return entry;
}

/* FP should not be null. This assumes that it has been initialized with an header line as well
 * Returns NULL in case 'entry' is uninitialized.
 */
GString *serialize_bot_expense_entry_csv
(
 BotExpenseEntry *entry
)
{
    /* will be automatically expanded by g_string_printf */
    GString *tstr = g_string_new(NULL);
    GString *result = g_string_new(NULL);

    if(entry == NULL) {
        fprintf(stderr, "BotExpenseEntry 'entry' is NULL. Initialize it before calling 'serialize_bot_expense_entry_csv'\n");
        return NULL;
    }

    /* Time in ISO 8061 */
    g_string_printf(tstr,
                    "%d-%02d-%02d %02d:%02d:%02d",
                    entry->time.tm_year + 1900,
                    entry->time.tm_mon + 1,
                    entry->time.tm_mday,
                    entry->time.tm_hour,
                    entry->time.tm_min,
                    entry->time.tm_sec);

    g_string_printf(result,
                    "%u,%s,%.2f,%.2f,%s,%s\n",
                    entry->id,
                    tstr->str,
                    entry->value,
                    entry->totalAmount,
                    entry->user->str,
                    entry->notes->str
                    );

    entry->_serialized = true;

    return result;
}

/* returns the number of bytes written, following the convention of fprintf.
 * Returns negative in case of error
 */
int write_bot_expense_entry
(
 FILE *fp,
 BotExpenseEntry *entry
)
{
    GString *line = serialize_bot_expense_entry_csv(entry);

    if(fp == NULL) {
        fprintf(stderr, "File pointer to CSV is NULL. Initialize it before calling 'serialize_bot_expense_entry_csv'\n");
        return -1;
    }

    if(line == NULL) {
        return -1;
    }

    /* line is terminated with '\n' */
    return fprintf(fp, "%s", line->str);
}

/* To be called after it has been serialized */
void free_bot_expense_entry
(
 BotExpenseEntry *entry
)
{
    if(!entry->_serialized) {
        /* TODO could fail here instead */
        fprintf(stderr, "WARNING: freeing an entry that wasn't serialized!\n");
    }

    /* Also free underlying string, we don't need it anymore */
    g_string_free(entry->user, TRUE);
    g_string_free(entry->notes, TRUE);
    free(entry);
}
