/*
** This file is part of the ViTE project.
**
** This software is governed by the CeCILL-A license under French law
** and abiding by the rules of distribution of free software. You can
** use, modify and/or redistribute the software under the terms of the
** CeCILL-A license as circulated by CEA, CNRS and INRIA at the following
** URL: "http://www.cecill.info".
**
** As a counterpart to the access to the source code and rights to copy,
** modify and redistribute granted by the license, users are provided
** only with a limited warranty and the software's author, the holder of
** the economic rights, and the successive licensors have only limited
** liability.
**
** In this respect, the user's attention is drawn to the risks associated
** with loading, using, modifying and/or developing or reproducing the
** software by the user in light of its specific status of free software,
** that may mean that it is complicated to manipulate, and that also
** therefore means that it is reserved for developers and experienced
** professionals having in-depth computer knowledge. Users are therefore
** encouraged to load and test the software's suitability as regards
** their requirements in conditions enabling the security of their
** systems and/or data to be ensured and, more generally, to use and
** operate it in the same conditions as regards security.
**
** The fact that you are presently reading this means that you have had
** knowledge of the CeCILL-A license and that you accept its terms.
**
**
** ViTE developers are (for version 0.* to 1.0):
**
**        - COULOMB Kevin
**        - FAVERGE Mathieu
**        - JAZEIX Johnny
**        - LAGRASSE Olivier
**        - MARCOUEILLE Jule
**        - NOISETTE Pascal
**        - REDONDY Arthur
**        - VUCHENER Clément
**
*/
#include <iostream>
#include <fstream>
#include <string>
#include <set>
#include <map>
#include <queue>
#include <list>
/* -- */
#include "common/Errors.hpp"
/* -- */
#include "trace/values/Values.hpp"
#include "trace/EntityTypes.hpp"
#include "trace/Entitys.hpp"
#include "trace/Trace.hpp"
/* -- */
#include "parser/Parser.hpp"
/* -- */
#include "parser/PajeParser/mt_PajeFileManager.hpp"
#include "parser/PajeParser/PajeDefinition.hpp"
#include "parser/PajeParser/ParserDefinitionPaje.hpp"
#include "parser/PajeParser/mt_ParserEventPaje.hpp"
#include "parser/PajeParser/mt_ParserPaje.hpp"
#include "parser/PajeParser/BuilderThread.hpp"
#include "trace/TraceBuilderThread.hpp"
/* -- */
/*
#ifndef WIN32
#include "sys/time.h"
#else
#include <windows.h>

LARGE_INTEGER GetFrequency()
{
  LARGE_INTEGER freq;
  QueryPerformanceFrequency(&freq);
  return freq;
}

#endif



//function for getting a quite accurate timestamp (used for debug purposes and performance evaluation)
unsigned long long  getCurrentTime (){
#ifndef WIN32
        // Get current time in microseconds
        struct timeval ts;
        gettimeofday(&ts,NULL);
        unsigned long long  timeValue = 0;
        timeValue = (1000000 * (unsigned long long)ts.tv_sec) + (unsigned long long)ts.tv_usec;
        //unsigned long long  timeValue = (100000000000000000);
        //double t2=ts.tv_sec+(ts.tv_usec/1000000.0);
        //printf("Current Time: %llu µs \n", timeValue);
        return timeValue;
#else
        LARGE_INTEGER tick, freq;
    freq = GetFrequency();
    QueryPerformanceCounter(&tick);
    return tick.QuadPart / freq.QuadPart;
#endif

}
*/
using namespace std;

mt_ParserPaje::mt_ParserPaje() : _ParserDefinition(new ParserDefinitionPaje()),
                           _ParserEvent(new mt_ParserEventPaje(_ParserDefinition)), _file(NULL){}

mt_ParserPaje::mt_ParserPaje(const string &filename) : Parser(filename),
                                                 _ParserDefinition(new ParserDefinitionPaje()),
                                                 _ParserEvent(new mt_ParserEventPaje(_ParserDefinition)), _file(NULL) {}

mt_ParserPaje::~mt_ParserPaje() {
    delete _ParserDefinition;
    delete _ParserEvent;
    if (_file != NULL)
        delete _file;
}


void mt_ParserPaje::parse(Trace &trace,
                       bool   finish_trace_after_parse) {
    static const string PERCENT = "%";

#ifdef DBG_PARSER_PAJE
    int lineid = 0;
#endif

    // Open the trace
    try {
        _file = new mt_PajeFileManager(_file_to_parse.c_str());
    } catch (const char *) {
        delete _file;
        _file = NULL;
        _is_canceled = true;
        finish();
        trace.finish();
            std::cerr <<  "Cannot open file " <<  _file_to_parse.c_str() << std::endl;
        Error::set(Error::VITE_ERR_OPEN, 0, Error::VITE_ERRCODE_WARNING);
        return;
    }

    QWaitCondition ended;

        QWaitCondition trace_ended;
    QMutex mutex;
    QMutex mutex2;
    QSemaphore freeSlots(5);
    QSemaphore linesProduced(5);
        _ParserEvent->setTrace(&trace);

        //this object will handle syntaxic verification of the events
    BuilderThread bt(_ParserEvent, &trace, &ended, &trace_ended, &freeSlots, &linesProduced, &mutex, &mutex2);
    bt.connect((const QObject*)this,
                     SIGNAL(produced(int, PajeLine*)),
                     SLOT(run(int, PajeLine*)));
        bt.connect((const QObject*)this,
                     SIGNAL(build_finish()),
                     SLOT(finish_build()));


        QThread consumerThread;
    bt.moveToThread(&consumerThread);

        //this object will handle all trace operations
    TraceBuilderThread T_bt(&trace_ended, &freeSlots, &mutex2);
    T_bt.connect((const QObject*)&bt,
                SIGNAL(build_trace(int, Trace_builder_struct*)),
                SLOT(build_trace(int, Trace_builder_struct*)));
    T_bt.connect((const QObject*)&bt,
                     SIGNAL(build_finished()),
                     SLOT(build_finished()));

    QThread traceBuilderThread;
    T_bt.moveToThread(&traceBuilderThread);

        // they both get their own thread

    consumerThread.start();
    traceBuilderThread.start();

        //we use blocks of 10000 lines
    PajeLine*     line=(PajeLine*)calloc(10000,sizeof(PajeLine));
        int i=0;
    while( (!(_file->eof())) && !(_is_canceled) ) {


        try {

#ifdef DBG_PARSER_PAJE
            if ( (lineid+1) ==  _file->get_line(&line[i]) )
            {
                _file->print_line();
                lineid++;
            }
#else
            _file->get_line(&line[i]);
#endif
        }
        catch(char const *){
            Error::set(Error::VITE_ERR_EXPECT_ID_DEF, 0, Error::VITE_ERRCODE_ERROR);
            continue;
        }

        // If it's an empty line
        if (line[i]._nbtks == 0) {
            continue;
        }
        // The line starts by a '%' : it's a definition
        else if(line[i]._tokens[0][0] == '%') {
            _ParserDefinition->store_definition(&line[i]);
        }
        // It's an event
        else {
        i++;
            if(i==10000){//we finished to build a block, send it to the BuilderThread

                    linesProduced.acquire();
                    emit produced(i, line);
                    i=0;
                    line=(PajeLine*)calloc(10000,sizeof(PajeLine));

                    //if a chunk of file is finished, wait for its complete treatment before releasing the chunk
                        // avoids reading too quickly in the file
                    if(_file->eoc()){
                        linesProduced.acquire(5);
                        _file->close_old_chunk();
                        linesProduced.release(5);
                    }

                   }

        }
    }

    //send the last batch
    emit produced(i, line);
    line=NULL;
    //send the finish signal to the BuilderThread, which will do the same to the TraceBuilderThread
        //both threads will then be finished and ready to destroy
        //locks the mutex and automatically unlocks it when going out of scope
        QMutexLocker locker(&mutex);
        emit build_finish();
        ended.wait(&mutex);
        locker.unlock();
    traceBuilderThread.quit();
    traceBuilderThread.wait();
        consumerThread.quit();
        consumerThread.wait();

    if(finish_trace_after_parse) {

        trace.finish();
         finish();
    }

}


float mt_ParserPaje::get_percent_loaded() const {
    if (_file != NULL)
        return _file->get_percent_loaded();
    else
        return 0.;
}
