/*
 ** 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 <string>
#include <map>
#include <set>
#include <queue>
#include <list>
/* -- */
#include "common/Errors.hpp"
/* -- */
#include "trace/values/Values.hpp"
#include "trace/EntityValue.hpp"
#include "trace/EntityTypes.hpp"
#include "trace/Entitys.hpp"
#include "trace/Trace.hpp"
/* -- */
#include <fstream>
#include <sstream>
#include "parser/PajeParser/mt_PajeFileManager.hpp" // temporary
#include "parser/PajeParser/PajeDefinition.hpp"
#include "parser/PajeParser/ParserDefinitionPaje.hpp"
#include "parser/PajeParser/mt_ParserEventPaje.hpp"
#include "trace/TraceBuilderThread.hpp"
/* -- */
#include <QMetaType>

Q_DECLARE_METATYPE(Name);
Q_DECLARE_METATYPE(Date);
Q_DECLARE_METATYPE(String);

#if defined WIN32 && !defined (__MINGW32__)
#define sscanf sscanf_s
#endif

using namespace std;

mt_ParserEventPaje::mt_ParserEventPaje(ParserDefinitionPaje *defs) {
    _Definitions = defs;
}

mt_ParserEventPaje::~mt_ParserEventPaje(){
    _containers.clear();
}

void mt_ParserEventPaje::setTrace(Trace* trace){_trace=trace;}

int mt_ParserEventPaje::store_event(const PajeLine       *line,
                                    Trace                &trace,
                                    Trace_builder_struct* tb_struct) {
    string      fvalue;
    string      alias;
    string      name;

    const vector<PajeFieldName> *FNames = _Definitions->get_FieldNames();
    vector< Field >       *fields;
    PajeDefinition        *def;
    int                    i, trid;
    int                    defsize;
    int                    idname, idtype;

    // We check if we have an event identifier
    if(sscanf(line->_tokens[0], "%d", &trid) != 1){
        Error::set(Error::VITE_ERR_EXPECT_ID_DEF, line->_id, Error::VITE_ERRCODE_WARNING);
        return -1;
    }

    // We check if the trid is available
    def = _Definitions->getDefFromTrid(trid);
    if ( def == NULL ) {
        std::stringstream s;
        s << Error::VITE_ERR_UNKNOWN_ID_DEF << trid;
        Error::set(s.str(), line->_id, Error::VITE_ERRCODE_ERROR);
        return -1;
    }

    fields  = &(def->_fields);
    defsize = fields->size();

    // We check if we have enough data for this event
    if ( defsize > (line->_nbtks - 1) ) {
        Error::set(Error::VITE_ERR_LINE_TOO_SHORT_EVENT, line->_id,
                   Error::VITE_ERRCODE_WARNING);
        return -1;
    }

    // Warning if we have extra data
    if ( defsize < (line->_nbtks - 1) ) {
        Error::set(Error::VITE_ERR_EXTRA_TOKEN, line->_id, Error::VITE_ERRCODE_WARNING);
    }

    // Dispatch the tokens in the good fields
    for(i=0; i < defsize; i++) {

        fvalue = line->_tokens[i+1];
        idname = (*fields)[i]._idname;
        idtype = (*fields)[i]._idtype;


        // Store the fvalue in the correct field
        switch( idname ) {
        case _PajeFN_Alias :
            alias = fvalue;
            break;

        case _PajeFN_Name :
            name  = fvalue;
            break;

        case _PajeFN_StartContainerType :
            tb_struct->start_container_type = fvalue;
            break;

        case _PajeFN_EndContainerType :
            tb_struct->end_container_type = fvalue;
            break;

        case _PajeFN_Time :
            tb_struct->time = fvalue;
            if(!tb_struct->time.is_correct()) {
                Error::set(Error::VITE_ERR_INCOMPATIBLE_VALUE_IN_EVENT +
                           fvalue + " (expecting a \"date\")",
                           line->_id,
                           Error::VITE_ERRCODE_WARNING);
                return -1;
            }
            break;

        case _PajeFN_Type :
            tb_struct->type = fvalue;
            break;

        case _PajeFN_Container :
            tb_struct->container = fvalue;
            break;

        case _PajeFN_Value :
            if( idtype == _FieldType_Double ) {
                tb_struct->value_double = fvalue;

                if(!tb_struct->value_double.is_correct()) {
                    Error::set(Error::VITE_ERR_INCOMPATIBLE_VALUE_IN_EVENT + fvalue + " (expecting a \"double\")",
                               line->_id, Error::VITE_ERRCODE_WARNING);
                    return -1;
                }
            }
            else {
                tb_struct->value_string = fvalue;
            }
            break;

        case _PajeFN_StartContainer :
            tb_struct->start_container = fvalue;
            break;

        case _PajeFN_EndContainer :
            tb_struct->end_container = fvalue;
            break;

        case _PajeFN_Key :
            tb_struct->key = fvalue;
            break;

        default :
            Value *value = NULL;
            switch( idtype ) {
            case _FieldType_String :
                value = new String(fvalue);
                break;

            case _FieldType_Double :
                value = new Double(fvalue);
                break;

            case _FieldType_Hex :
                value = new Hex(fvalue);
                break;

            case _FieldType_Date :
                value = new Date(fvalue);
                break;

            case _FieldType_Int :
                value = new Integer(fvalue);
                break;

            case _FieldType_Color :
                value = new Color(fvalue);
                break;

            default:
                Error::set(Error::VITE_ERR_FIELD_TYPE_UNKNOWN, line->_id, Error::VITE_ERRCODE_WARNING);
                return -1;
            }

            //          if(!value->is_correct()) { // Check if the value is correct or not
            //              Error::set(Error::VITE_ERR_INCOMPATIBLE_VALUE_IN_EVENT + fvalue + " (expecting a \"" + ftype + "\")",
            //                         line->_id, Error::VITE_ERRCODE_WARNING);
            //              return;
            //          }
            // if(tb_struct->extra_fields==NULL)tb_struct->extra_fields=new map<std::string, Value *>();
            // tb_struct->extra_fields->insert(pair<std::string, Value *>((*FNames)[idname]._name, value));
            tb_struct->extra_fields.insert(pair<std::string, Value *>((*FNames)[idname]._name, value));
        }
    }

    // Name alias_name;

    if ( (alias != "") && (name == "" ) ){
        name = alias;
    }
    if ( (name != "") && (alias == "") ) {
        alias = name;
    }
    tb_struct->alias.set_alias(alias);
    tb_struct->alias.set_name(name);


    tb_struct->_id=line->_id;
    tb_struct->_trace=&trace;
    //tb_struct->extra_fields=extra_fields;
    tb_struct->_containers=&_containers;

    switch( def->_id ) {
    case _PajeDefineContainerType :
    {
        tb_struct->func= TraceBuilderThread::define_container_type;
    }
    break;

    case _PajeCreateContainer :
    {
        tb_struct->func= TraceBuilderThread::create_container;
    }
    break;

    case _PajeDestroyContainer :
    {
        tb_struct->func= TraceBuilderThread::destroy_container;
    }
    break;

    case _PajeDefineEventType :
    {
        tb_struct->func= TraceBuilderThread::define_event_type;
    }
    break;

    case _PajeDefineStateType :
    {
        tb_struct->func= TraceBuilderThread::define_state_type;
    }
    break;

    case _PajeDefineVariableType :
    {
        tb_struct->func= TraceBuilderThread::define_variable_type;
    }
    break;

    case _PajeDefineLinkType :
    {
        tb_struct->func= TraceBuilderThread::define_link_type;
    }
    break;

    case _PajeDefineEntityValue :
    {
        tb_struct->func= TraceBuilderThread::define_entity_value;
    }
    break;

    case _PajeSetState :
    {
        tb_struct->func= TraceBuilderThread::set_state;
    }
    break;

    case _PajePushState :
    {
        tb_struct->func= TraceBuilderThread::push_state;
    }
    break;

    case _PajePopState :
    {
        tb_struct->func= TraceBuilderThread::pop_state;
    }
    break;

    case _PajeNewEvent :
    {
        tb_struct->func= TraceBuilderThread::new_event;
    }
    break;

    case _PajeSetVariable :
    {
        tb_struct->func= TraceBuilderThread::set_variable;
    }
    break;

    case _PajeAddVariable :
    {
        tb_struct->func= TraceBuilderThread::add_variable;
    }
    break;

    case _PajeSubVariable :
    {
        tb_struct->func= TraceBuilderThread::sub_variable;
    }
    break;

    case _PajeStartLink :
    {
        tb_struct->func= TraceBuilderThread::start_link;
    }
    break;

    case _PajeEndLink :
    {
        tb_struct->func= TraceBuilderThread::end_link;
    }
    break;

    default:
        Error::set(Error::VITE_ERR_UNKNOWN_EVENT_DEF + def->_name, line->_id, Error::VITE_ERRCODE_WARNING);
        return  -1;
    }
    return 0;
}
