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

extern BotCommand g_LastCommand = BOT_CMD_NONE;
extern BotExpenseEntry *g_LastExpenseEntry = NULL;
extern BotReminder *g_LastReminder = NULL;
extern GString *g_filename = NULL;

BotCommand bot_get_last_command()
{
    return g_LastCommand;
}
void bot_set_last_command(BotCommand cmd)
{
    g_LastCommand = cmd;
}

BotExpenseEntry *bot_get_last_expense()
{
    return g_LastExpenseEntry;
}
void bot_set_last_expense(BotExpenseEntry *entry)
{
    if(g_LastExpenseEntry) {
        free_bot_expense_entry(g_LastExpenseEntry);
    }
    g_LastExpenseEntry = entry;
}

BotReminder *bot_get_last_reminder()
{
    return g_LastReminder;
}
void bot_set_last_reminder(BotReminder *reminder)
{
    if(g_LastReminder) {
        free_bot_reminder(g_LastReminder);
    }
    g_LastReminder = reminder;
}

GString *bot_get_filename()
{
    return g_filename;
}

void bot_set_filename(GString *fn)
{
    if(g_filename) {
        g_string_free(g_filename, true);
    }
    g_filename = fn;
}

int bot_get_current_entry_id
(
 const GString* filename
)
{
    /* TODO this counts by char, probably more efficient ways exist */
    char c;
    FILE *fp;
    unsigned int count = 0;

    if((fp = fopen(filename->str, "r"))) {
        for (c = getc(fp); c != EOF; c = getc(fp)) {
            if (c == '\n') {
                count++;
            }
        }
        fclose(fp);
    }

    return count;
}

/* Create a BotExpenseEntry with data fed from the parser.
 * Returns NULL if error getting local time.
 */
BotExpenseEntry *create_bot_expense_entry
(
 const BotExpenseEntry *previous,
 const unsigned int id,
 const BotCommand cmd,
 const double value,
 GString* user,
 GString* notes
)
{
    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;
    }

    entry->id = id;
    entry->time = *lt;
    entry->value = signedValue;
    entry->user = user;
    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,%s,%s\n",
                    entry->id,
                    tstr->str,
                    entry->value,
                    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
(
 const GString *filename,
 BotExpenseEntry *entry,
 bool needsHeaders
)
{
    FILE *fp;
    int result;
    GString *line = serialize_bot_expense_entry_csv(entry);

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

    if((fp = fopen(filename->str, "a"))) {
        if(needsHeaders) {
            fprintf(fp, "ID,Time,Value,User,Notes\n");
        }

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

        return result;
        fprintf(stderr, "Error opening %s\n", filename->str);
    }

    fprintf(stderr, "Error opening %s\n", filename->str);
    return -1;
}

/* whole routine to write an expense to a CSV file */
int write_update_to_csv
(
 const GString *filename,
 const BotCommand cmd,
 const double value,
 GString* user,
 GString* notes
)
{
    int result;
    unsigned int id;
    BotExpenseEntry *entry;
    BotExpenseEntry *previous = bot_get_last_expense();

    id = (previous == NULL) ? bot_get_current_entry_id(filename) : previous->id + 1;

    entry = create_bot_expense_entry(previous, id, cmd, value, user, notes);
    if(entry == NULL) {
        return -1;
    }

    /* if no ID, empty file, initialize with headers before writing entry */
    result = write_bot_expense_entry(filename, entry, (id == 0));

    if(result > 0) {  /* write successful */
        bot_set_last_command(cmd);
        bot_set_last_expense(entry);
    }
    return result;
}

/* start a SIGALARM routine */
int start_alarm_reminder(unsigned int minutes)
{
    /* in seconds */
    alarm(minutes * 60);

    BotReminder *reminder = malloc(sizeof *reminder);
    reminder->minutes = minutes;
    reminder->from = g_string_new(NULL); /* to be set by the message handler of the bot */

    bot_set_last_command(BOT_CMD_REMINDER);
    bot_set_last_reminder(reminder);

    return 0;
}

/* 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);
}

void free_bot_reminder
(
 BotReminder *reminder
)
{
    g_string_free(reminder->from, true);
    free(reminder);
}
