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

using namespace std;

#ifdef WIN32 // remove a warning and use a safe function
#define sscanf sscanf_s
#endif

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], "%u", &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;
        }
