/*
 * AsciiParser.ypp, part of LatAnalyze 3
 *
 * Copyright (C) 2013 - 2020 Antonin Portelli
 *
 * LatAnalyze 3 is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * LatAnalyze 3 is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LatAnalyze 3.  If not, see <http://www.gnu.org/licenses/>.
 */

%{
    #include <LatAnalyze/Global.hpp>
	#include <LatAnalyze/Io/AsciiFile.hpp>
    #include <LatAnalyze/Core/Mat.hpp>
    #include <LatAnalyze/Statistics/MatSample.hpp>

    using namespace std;
    using namespace Latan;
    
    #define TEST_FIRST(name) \
    if (state->isFirst)\
    {\
        state->first   = (name);\
        state->isFirst = false;\
    }
%}

%pure-parser
%name-prefix "_Ascii_"
%locations
%defines
%error-verbose
%parse-param { Latan::AsciiFile::AsciiParserState* state }
%initial-action {yylloc.last_column = 0;}
%lex-param { void* scanner }

%union
{
	long int val_int;
	double   val_double;
    char     val_char;
    char     val_str[256];
}

%token <val_char>   ERR
%token <val_double> FLOAT
%token <val_int>    INT
%token <val_str>    ID
%token OPEN CLOSE MAT SAMPLE RG_STATE

%type <val_str> mat matsample dsample

%{
	int _Ascii_lex(YYSTYPE* lvalp, YYLTYPE* llocp, void* scanner);

	void _Ascii_error(YYLTYPE* locp, AsciiFile::AsciiParserState* state,
                      const char* err)
	{
        stringstream buf;
        
        buf << *state->streamName << ":" << locp->first_line << ":"\
            << locp->first_column << ": " << err;
        LATAN_ERROR(Parsing, buf.str());
	}

	#define scanner state->scanner
%}

%%

datas:
      /* empty string */
    | datas data
	;

data:
      mat
    {
        TEST_FIRST($1);
        (*state->data)[$1].reset(new DMat(state->dMatQueue.front()));
        state->dMatQueue.pop();
    }
    | dsample
    {
        TEST_FIRST($1);
        (*state->data)[$1].reset(new DSample(state->dSampleBuf));
    }
    | matsample
    {
        TEST_FIRST($1);
        (*state->data)[$1].reset(new DMatSample(state->dMatSampleBuf));
    }
    ;

mat:
      OPEN MAT ID INT floats CLOSE MAT
    {
        const unsigned int nRow = state->doubleQueue.size()/$4, nCol = $4;
        Index i, j, r = 0;
        
        if (state->doubleQueue.size() != nRow*nCol)
        {
            LATAN_ERROR(Size, "matrix '" + *state->streamName + ":" + $3 +
                        "' has a wrong size");
        }
        
        state->dMatQueue.push(DMat(nRow, nCol));
        while (!state->doubleQueue.empty())
        {
            j = r % nCol;
            i = (r - j)/nCol;
            state->dMatQueue.back()(i, j) = state->doubleQueue.front();
            state->doubleQueue.pop();
            r++;
        }
        strcpy($$, $3);
    }
    ;

dsample:
      OPEN SAMPLE ID INT mat CLOSE SAMPLE
    {
        const unsigned int nSample = $4, os = DMatSample::offset;
        
        DMat &m = state->dMatQueue.front();
        if (m.rows() != nSample + os)
        {
            LATAN_ERROR(Size, "double sample '" + *state->streamName + ":"
                        + $3 + "' has a wrong size");
        }
        if (m.cols() != 1)
        {
            LATAN_ERROR(Size, "double sample '" + *state->streamName + ":"
                        + $3 + "' is not stored as a column vector");
        }
        state->dSampleBuf = m.array();
        state->dMatQueue.pop();
        strcpy($$, $3);
    }
    ;

matsample:
      OPEN SAMPLE ID INT mat mats CLOSE SAMPLE
    {
        const unsigned int nSample = $4, os = DMatSample::offset;
        
        if (state->dMatQueue.size() != nSample + os)
        {
            LATAN_ERROR(Size, "matrix sample '" + *state->streamName + ":"
                        + $3 + "' has a wrong size");
        }
        state->dMatSampleBuf.resize(nSample);
        state->dMatSampleBuf[central] = state->dMatQueue.front();
        state->dMatQueue.pop();
        for (unsigned int i = 0; i < nSample; ++i)
        {
            state->dMatSampleBuf[i] = state->dMatQueue.front();
            state->dMatQueue.pop();
        }
        strcpy($$, $3);
    }
    ;

mats:
      mats mat
    | mat
    ;

floats:
      floats FLOAT {state->doubleQueue.push($2);}
    | floats INT   {state->doubleQueue.push(static_cast<double>($2));}
    | FLOAT        {state->doubleQueue.push($1);}
    | INT          {state->doubleQueue.push(static_cast<double>($1));}
    ;
