/*---------------------------------------------------------------------------------------------------------------------
- File      : messages_manager.h                                                          Project ELSE, EPFL - DI/LIA -
-                                                                       Evaluation in Language and Speech Engineering -
- Author    : Seydoux Florian   Creation date : 16 Aug 1999                                                           -
- Eulogist  : -                 Approval date : -                  Version: 0.1                                       -
-                                                                                                                     -
- Descript. : Manage all user's messages. Define some type of messages (Standard, Warning, Error, Unrecoverable) and  -
-             verbosity level (0: no message -> 3: Debug phase, all dump).                                            -
-             Define also where the message must be output. By default, all Warning and Error are put to stdout,      -
-             standard message and unrecoverable error to stderr. Serious errors are always displayed on the both.    -                                                     -
-                                                                                                                     -
-             Format of the output (Error/Warning examples):                                                          -
-             <ERROR   A.1.6. - Name of the error> ('filename' 000)  Description    // single file, single position   -
-             <WARNING a.1.9. - Name of the warning> ('filename' [32:44]) Descr.    // Single file, gap position      -
-             <ERROR   C.0.0. - Name of the error> ('file1' 00, 'file2' 00) Descr.  // Double file, single positions  -
-             <ERROR   D.1.4. - Name...> ('file1' [00:00], 'file2' [00:00]) Descr.  // Double file, gap positions     -
-                                                                                                                     -
-             Exit code:                                                                                              -
-             1 -> serious_error                                                                                      -
-             2 -> single error                                                                                       -
-                                                                                                                     -
- Requested : -                                                                                                       -
-                                                                                                                     -
- Gaps      : o) Modif. the format to make it most lighter: <ERROR/WARNING (event. Line source code) Name> descr.     -                                                                                                     -
-                                                                                                                     -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Rev. date | Reviser               | Revise's description                                                            -
- - - - - - + - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ../../....| ........              | ...                                                                             -
---------------------------------------------------------------------------------------------------------------------*/

#ifndef MESSAGES_MANAGER_H
#define MESSAGES_MANAGER_H

#include <iomanip>
#include <iostream>

#include "globaldef.h"

#ifdef _USE_NAMESPACES
    namespace Else { 
#endif // _USE_NAMESPACES

class Location {
    friend ostream& operator<< (ostream&, const Location&);
public:

    Location()
    : valid(false)
    { }

    Location(const StringCPtr fileNameAccess, const VeryLongNatural& position = 0)
    : valid(true),
      gap(false),
      link(fileNameAccess),
      start(position)
    { }

    Location(const StringCPtr fileNameAccess, const VeryLongNatural& start, const VeryLongNatural& stop)
    : valid(true),
      gap(true),
      link(fileNameAccess),
      start(start),
      stop(stop)
    { }

    void display(ostream& os) const // display: _('filename' [000:000]) or nothing.
    {
        if (valid) {
            os << " ('" << *link << "' ";
            if (gap)
                os << '[' 
//                 << setfill('0') << setw(15) 
                   << start 
                   << ':' 
//                 << setfill('0') << setw(15) 
                   << stop 
                   << ']';
            else
//              os << setfill('0') << setw(15) << start;
                os << start;              
            os << ')';
        }
    }
    

private:
    const bool            valid; // Tell if the instance was explicitely initialized
    const bool            gap;   // Tell if the instance show a single point, or a space between two points
    const StringCPtr      link;  // Point the name of the file where the location is.
    const VeryLongNatural start; // Location of the (start) point.
    const VeryLongNatural stop;  // Gap's ending point
};

extern const Location nullLocation;

/*
ostream& operator<< (ostream& os, const Location& loc)
{
    loc.display(os);
    return os;
}
*/

const ShortNatural DEBUG_MSG = 2;
const ShortNatural DETAIL_MSG = 1;
const ShortNatural STANDARD_MSG = 0;

class MessagesManager {
public:
    enum VerbosityLevel { NoMessage = 0, ModerateDisplay = 1, ExplicitDisplay = 2, OverloadedDisplay = 3};

public:

    MessagesManager()
    : displayWarning(true),
      stopOnFirstError(true),
      verbosityLevel(ModerateDisplay),
      messageStreamPtr(&cerr),
      errorStreamPtr(&cout)
    { }

    void
    message(const string&       from,
            const string&       msg,
            const ShortNatural  level = STANDARD_MSG) const
    {
        if (verbosityLevel > level)
            (*messageStreamPtr) << '[' << from << " (" << level << ")] " << msg << '\n';
    }

    void
    message(const string&       from,
            const char*const    msg,
            const ShortNatural  level = STANDARD_MSG) const
    {
        if (verbosityLevel > level)
            (*messageStreamPtr) << '[' << from << " (" << level << ")] " << msg << '\n';
    }

    void
    message(const string&       msg,
            const ShortNatural  level = STANDARD_MSG) const
    {
        if (verbosityLevel > level)
            (*messageStreamPtr) << "[(" << level << ")] " << msg << '\n';
    }

    void
    message(const char*const    msg,
            const ShortNatural  level = STANDARD_MSG) const
    {
        if (verbosityLevel > level)
            (*messageStreamPtr) << "[(" << level << ")] " << msg << '\n';
    }
            
    void
    warning(const unsigned char appId,
            const ShortNatural  majorId,
            const ShortNatural  minorId,
            const string&       abstract,
            const string&       description,
            const Location&     mistake = nullLocation,
            const Location&     reference = nullLocation) const
    {
        if (displayWarning)
            (*errorStreamPtr) << "<WARNING " << appId << '.' << majorId << '.' << minorId << ". - "
                              << abstract << "> " << mistake << reference << ' ' << description << '\n';
    }

    void
    warning(const unsigned char appId,
            const ShortNatural  majorId,
            const ShortNatural  minorId,
            const char*const    abstract,
            const char*const    description,
            const Location&     mistake = nullLocation,
            const Location&     reference = nullLocation) const
    {
        if (displayWarning)
            (*errorStreamPtr) << "<WARNING " << appId << '.' << majorId << '.' << minorId << ". - "
                              << abstract << "> " << mistake << reference << ' ' << description << '\n';
    }
            
    void
    error(const unsigned char appId,
          const ShortNatural  majorId,
          const ShortNatural  minorId,
          const string&       abstract,
          const string&       description,
          const Location&     mistake = nullLocation,
          const Location&     reference = nullLocation) const
    {
        (*errorStreamPtr) << "<ERROR " << appId << '.' << majorId << '.' << minorId << ". - "
                          << abstract << "> " << mistake << reference << ' ' << description << '\n';
        if (stopOnFirstError) exit(2);
    }

    void
    error(const unsigned char appId,
          const ShortNatural  majorId,
          const ShortNatural  minorId,
          const char*const    abstract,
          const char*const    description,
          const Location&     mistake = nullLocation,
          const Location&     reference = nullLocation) const
    {
        (*errorStreamPtr) << "<ERROR " << appId << '.' << majorId << '.' << minorId << ". - "
                          << abstract << "> " << mistake << reference << ' ' << description << '\n';
        if (stopOnFirstError) exit(2);
    }


    void
    seriousError(const string& from, const string& msg, const bool both = false) const
    {
        if (!from.empty()) cerr << '[' << from << "] ";
        cerr << "UNRECOVERABLE ERROR: " << msg << '\n';
        if (both)
        {
            if (!from.empty()) cout << '[' << from << "] ";
            cout << "UNRECOVERABLE ERROR: " << msg << '\n';
        }
        exit(1);
    }
    void
    seriousError(const string& from, const char*const msg, const bool both = false) const
    {
        if (!from.empty()) cerr << '[' << from << "] ";
        cerr << "UNRECOVERABLE ERROR: " << msg << '\n';
        if (both)
        {
            if (!from.empty()) cout << '[' << from << "] ";
            cout << "UNRECOVERABLE ERROR: " << msg << '\n';
        }
        exit(1);
    }

    void
    seriousError(const char*const from, const char*const msg, const bool both = false) const
    {
        if (from) cerr << '[' << from << "] ";
        cerr << "UNRECOVERABLE ERROR: " << msg << '\n';
        if (both)
        {
            if (from) cout << '[' << from << "] ";
            cout << "UNRECOVERABLE ERROR: " << msg << '\n';
        }
        exit(1);
    }

    void
    seriousError(const char*const from, const string& msg, const bool both = false) const
    {
        if (from) cerr << '[' << from << "] ";
        cerr << "UNRECOVERABLE ERROR: " << msg << '\n';
        if (both)
        {
            if (from) cout << '[' << from << "] ";
            cout << "UNRECOVERABLE ERROR: " << msg << '\n';
        }
        exit(1);
    }
    
    void
    checkParams(Parameters& params)   // remove all correct parameters.
    {
        // Set the default value.
        displayWarning = true;
        stopOnFirstError = true;
        verbosityLevel = ModerateDisplay;
        messageStreamPtr = &cerr;
        errorStreamPtr = &cout;

        // Check all parameters.
        Parameters::iterator currentArg(params.begin());
        while (currentArg != params.end())
        {
            if (!strcmp(*currentArg, "-w"))
                { displayWarning = false; currentArg = params.erase(currentArg); continue; }
            if (!strcmp(*currentArg, "-all"))
                { stopOnFirstError = false; currentArg = params.erase(currentArg); continue; }
            if (!strcmp(*currentArg, "-v0"))
                { verbosityLevel = NoMessage; currentArg = params.erase(currentArg); continue; }
            if (!strcmp(*currentArg, "-v1"))
                { verbosityLevel = ModerateDisplay; currentArg = params.erase(currentArg); continue; }
            if (!strcmp(*currentArg, "-v2"))
                { verbosityLevel = ExplicitDisplay; currentArg = params.erase(currentArg); continue; }
            if (!strcmp(*currentArg, "-v3"))
                { verbosityLevel = OverloadedDisplay; currentArg = params.erase(currentArg); continue; }
            if (!strcmp(*currentArg, "-m"))
                { Parameters::iterator next(currentArg+1);
                  if (next != params.end()) {
                      if (!strcmp(*next, "cout")) 
                          { messageStreamPtr = &cout; currentArg = params.erase(currentArg, ++next); continue; }
                      if (!strcmp(*next, "cerr"))
                          { messageStreamPtr = &cerr; currentArg = params.erase(currentArg, ++next); continue; }
                  } 
                }
            if (!strcmp(*currentArg, "-e"))
                { Parameters::iterator next(currentArg+1);
                  if (next != params.end()) {
                      if (!strcmp(*next, "cout")) 
                          { errorStreamPtr = &cout; currentArg = params.erase(currentArg, ++next); continue; }
                      if (!strcmp(*next, "cerr"))
                          { errorStreamPtr = &cerr; currentArg = params.erase(currentArg, ++next); continue; }
                  } 
                }
            ++currentArg;
        }
    }
    
    void
    switchesFormat(ostream& os) const  // dump help about parameters.
    {
        os << "  -w             : disable output of warning messages (default: warning displaying = yes).\n"
           << "  -all           : continue even if errors are encounter (default: stop at first error).\n"
           << "  -v<0..3>       : set the verbosity level:\n"
           << "                   0 = Quiet,    1 = Moderate display (default),\n"
           << "                   2 = Heavy,    3 = Overloaded (debugging).\n"
           << "  -m <cout|cerr> : set the messages's output stream (default: cerr).\n"
           << "  -e <cout|cerr> : set the errors's output stream (default: cout).\n";
    }
           
public:
    bool            displayWarning;         // Param: -w   -> disable warning displaying
    bool            stopOnFirstError;       // Param: -all -> do not stop at first error
    VerbosityLevel  verbosityLevel;         // Param: -v0, -v1, -v2 -v3 -> define the verbosity level

    ostream*        messageStreamPtr;       // Param: -m [cout/cerr=default]
    ostream*        errorStreamPtr;         // Param: -e [cerr/cout=default]

};

extern MessagesManager msg;

#ifdef _USE_NAMESPACES
    }
#endif // _USE_NAMESPACES

#endif // MESSAGES_MANAGER_H

