/**
 *
 * @file src/parser/PajeParser/mt_ParserEventPaje.cpp
 *
 * @copyright 2008-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
 *                      Univ. Bordeaux. All rights reserved.
 *
 * @author Camille Ordronneau
 * @author Johnny Jazeix
 * @author Augustin Degomme
 * @author Mathieu Faverge
 *
 * @date 2024-07-17
 */
#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;
}

#include "moc_mt_ParserEventPaje.cpp"
