%{
#include <stdio.h>
#include <glib.h>  /* For GString */
#include <math.h>

/* suppress warning about missing declarations */
int yylex();
int yyparse();

void yyerror(const char *str)
{
    fprintf(stderr,"error: %s\n",str);
}

int yywrap()
{
    return 1;
}

%}

%code requires {
#include "../../src/expenses/types.h"
#include "../../src/expenses/task.h"
#include "../../src/expenses/csv.h"

/* Parse a string with Flex+Bison instead of a file / stdout */
int grammar_parse_string(const GString *buf);
}

%define parse.lac full
%define parse.error verbose

%union {
    int res;                     /* return value of C procedure */
    double val;                  /* For returning tnbers.                   */
    BotCommand cmd;
    BotReportTimespan tsp;
    BotReport rp;
    GString *str;
}

%token  <res>  TOK_INT
%token  <val>  TOK_DOUBLE 
%token  <cmd>  TOK_CMD_IN TOK_CMD_OUT TOK_CMD_DEL TOK_CMD_REPORT TOK_CMD_REMINDER
%token  <tsp>  TOK_TS_DAY TOK_TS_WEEK TOK_TS_MONTH TOK_TS_YEAR
%token  <str>  TOK_USER      /* string for user */
%token  <str>  TOK_STR       /* string for notes */
%type   <cmd>  update;
%type   <val>  value;
%type   <res>  msg;
%type   <rp>  days;
%type   <res>  tn;

%%
input:    /* empty */
        | input line
;

line:     '\n'
        | msg
        | error '\n' { yyerrok; }
;

msg:      update value TOK_USER TOK_STR
                {
                    bot_set_last_command($1);
                    int res = write_update_to_csv(bot_get_filename(), $1, $2, $3, $4);
                }
        | TOK_CMD_REMINDER TOK_INT
                {
                    bot_set_last_command($1);
                    bot_set_last_reminder(abs($2));
                }
        | TOK_CMD_DEL TOK_INT                   { printf("delete: %d\n", $2); $$ = 0; } // TODO
        | TOK_CMD_REPORT days
                {
                    bot_set_last_command($1);
                    bot_set_report($2);
                }

value:    TOK_INT                             { $$ = (double)$1; } 
        | TOK_DOUBLE
        ;

update:   TOK_CMD_IN                          
        | TOK_CMD_OUT
;

/* convert a timestamp into num. of days. No calendar taken into account */
days:   /* empty */                           { $$ = (BotReport) { .days = 1 }; }
        | TOK_TS_DAY tn                       { $$ = (BotReport) { .days = $2 }; }
        | TOK_TS_WEEK tn                      { $$ = (BotReport) { .days = 7*$2 }; }
        | TOK_TS_MONTH tn                     { $$ = (BotReport) { .days = 31*$2 }; }
        | TOK_TS_YEAR tn                      { $$ = (BotReport) { .days = 365*$2 }; }
;

tn:     /* empty */                           { $$ = 1; }
        | TOK_INT                             { $$ = $1; }
        ;
%%

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