/*
** 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 <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <queue>
/* -- */
#include "common.hpp"
/* -- */
#include "interface/Interface.hpp"
/* -- */
#include "common/Message.hpp"
#include "common/Errors.hpp"
/* -- */
#ifdef WIN32
#define snprintf sprintf_s
#endif

using namespace std;

queue<string> Error::_errors;
queue<string> Error::_warnings;

const int Error::VITE_ERRCODE_EVERYTHING = 0;
const int Error::VITE_ERRCODE_WARNING    = 1;
const int Error::VITE_ERRCODE_ERROR      = 2;

string Error::_content = "";

const string Error::VITE_ERR_PARSE                       = "expected \" before end of file";
const string Error::VITE_ERR_MMAP                        = "mmap error";
const string Error::VITE_ERR_EMPTY_FILE                  = "empty file";
const string Error::VITE_ERR_FSTAT                       = "status file error";
const string Error::VITE_ERR_OPEN                        = "open file error";
const string Error::VITE_ERR_MUNMAP                      = "munmap error";
const string Error::VITE_ERR_EXPECT_END_DEF              = "expected %EndEventDef";
const string Error::VITE_ERR_EXPECT_EVENT_DEF            = "expected %EventDef";
const string Error::VITE_ERR_EXPECT_NAME_DEF             = "the definition is not named";
const string Error::VITE_ERR_EXPECT_ID_DEF               = "the definition is not identified";
const string Error::VITE_ERR_UNKNOWN_ID_DEF              = "there is no definition with the identity: ";
const string Error::VITE_ERR_EXTRA_TOKEN                 = "extra token(s) ignored";
const string Error::VITE_ERR_UNKNOWN_EVENT_DEF           = "the following event doesn't match with any event known: ";
const string Error::VITE_ERR_FIELD_TYPE_MISSING          = "a field type is missing ";
const string Error::VITE_ERR_FIELD_TYPE_UNKNOWN          = "the following field type is unknown: ";
const string Error::VITE_ERR_EMPTY_DEF                   = "a definition line is empty";
const string Error::VITE_ERR_INCOMPATIBLE_VALUE_IN_EVENT = "incompatible value: ";
const string Error::VITE_ERR_BAD_FILE_EXTENSION          = "the extension of the file is not .trace";
const string Error::VITE_ERR_LINE_TOO_SHORT_EVENT        = "missing field value(s) in an event";

const string Error::VITE_ERR_UNKNOWN_CONTAINER_TYPE      = "Unknown container type: ";
const string Error::VITE_ERR_UNKNOWN_CONTAINER           = "Unknown container: ";
const string Error::VITE_ERR_UNKNOWN_EVENT_TYPE          = "Unknown event type: ";
const string Error::VITE_ERR_UNKNOWN_STATE_TYPE          = "Unknown state type: ";
const string Error::VITE_ERR_UNKNOWN_VARIABLE_TYPE       = "Unknown variable type: ";
const string Error::VITE_ERR_UNKNOWN_LINK_TYPE           = "Unknown link type: ";
const string Error::VITE_ERR_UNKNOWN_ENTITY_TYPE         = "Unknown entity type: ";
const string Error::VITE_ERR_UNKNOWN_ENTITY_VALUE        = "Unknown entity value: ";

const string Error::VITE_ERR_EVENT_ALREADY_DEF           = "Event trid is already defined";
const string Error::VITE_ERR_FIELD_NAME_MISSING          = "Field's name is missing";
const string Error::VITE_ERR_FIELD_NOT_ALLOWED           = "This type is not allowed for this field";
const string Error::VITE_ERR_EVENT_NOT_CORRECT           = "The definition of this event is not correct : ";


void Error::set(const string kind_of_error, const int priority){
    Error::_content = kind_of_error;

    switch(priority){
    case VITE_ERRCODE_WARNING:
        Error::_warnings.push(Error::_content);
        break;
    default: // Include the _ERROR
        Error::_errors.push(Error::_content);
        break;
    }
}

void Error::set(const string kind_of_error, const unsigned int line_number, const int priority){
    char line[sizeof(line_number)*3+3+1];
    snprintf(line, sizeof(line), "%d : ", line_number);
    set(line + kind_of_error, priority);
}

void Error::set_and_print(const string kind_of_error, const int priority){
    set(kind_of_error, priority);
    print(priority);
}

void Error::set_and_print(const string kind_of_error, const unsigned int line_number, const int priority){
    char line[sizeof(line_number)*3+1];
    snprintf(line, sizeof(line), "%d", line_number);
    set(kind_of_error + " on line " + line, priority);
    print(priority);
}


bool Error::set_if(bool condition, const string kind_of_error, const unsigned int line_number, const int priority){
    if(condition){
        char line[sizeof(line_number)*3+1];
        snprintf(line, sizeof(line), "%d", line_number);
        set(kind_of_error + " on line " + line, priority);
        return true;
    }
    return false;
}


void Error::print(const int priority) {
    *Message::get_instance() << _content;
    switch(priority){
    case VITE_ERRCODE_WARNING:
        *Message::get_instance() << Message::endw;
        break;
    default: // Include the _ERROR
        *Message::get_instance() << Message::ende;
        break;
    }
}

void Error::print(const string content, const int priority) {
    *Message::get_instance() << content;
    switch(priority){
    case VITE_ERRCODE_WARNING:
        *Message::get_instance() << Message::endw;
        break;
    default: // Include the _ERROR
        *Message::get_instance() << Message::ende;
        break;
    }
}

void Error::print_numbers(){
    *Message::get_instance() << Error::_errors.size() << " errors and " << Error::_warnings.size() << " warnings were found during parsing.";
    if(Error::_warnings.size() == 0 && Error::_errors.size() == 0){
        *Message::get_instance() << Message::endi;
    }
    else if(Error::_errors.size() == 0){
        *Message::get_instance() << Message::endw;
    }
    else{
        *Message::get_instance() << Message::ende;
    }
}

void Error::flush(const string &filename){

    if(_errors.empty() && _warnings.empty()) {
        return;
    }
    else {

        print("Errors and warnings can be found in "+filename, VITE_ERRCODE_ERROR);

        ofstream outfile(filename.c_str(), ios::out | ios::trunc);
        
        const int number_of_errors = Error::_errors.size();
        const int number_of_warnings = Error::_warnings.size();

        if(!outfile.is_open()){
            cerr << "unable to open " << filename << " to print the errors encountered in the file opening" << endl;
            return;
        }
        else {
            outfile << "File " << Message::get_interface()->get_filename() << endl << endl;

            if(!_errors.empty()) {
                outfile << "Errors :" << endl;
            }

            // Print the errors
            while(!_errors.empty()) {
                outfile << _errors.front() << endl;
                //print(_errors.front(), _ERROR);
                _errors.pop();
            }

            // Print the warnings
            if(!_warnings.empty()) {
                outfile << endl << "Warnings :" << endl;
            }
            while(!_warnings.empty()) {
                outfile << _warnings.front() << endl;
                //print(_warnings.front(), _WARNING);
                _warnings.pop();
            }

            outfile << endl << "Your trace has " << number_of_errors << " errors and " << number_of_warnings << " warnings." << endl;

            outfile.close();
        }
    }
}

