#include "message_handler.h"

#include <QDateTime>
#include <cstring>
#include <QString>
#include <QFileInfo>
#include <QMessageLogContext>

#define OUTPUT_LEN (1024)

static bool installedMsgHandler = false;
static QtMsgType debugLevel = QtInfoMsg;

QtMsgType getDebugLevel() { return debugLevel; }
void setDebugLevel(QtMsgType newDebugLevel) {
    debugLevel = newDebugLevel;
}

bool messageHandlerInstalled() {
    return installedMsgHandler;
}

QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler) {
    installedMsgHandler = (handler != 0);
    static QtMessageHandler prevHandler = nullptr;
    if (handler) {
        prevHandler = qInstallMessageHandler(handler);
        return prevHandler;
    } else {
        return qInstallMessageHandler(prevHandler);
    }
}

///
/// \brief Print message according to given debug level.
///
/// \note Install this function using qInstallMsgHandler().
///
///  int main(int argc, char **argv) {
///     installMsgHandler(atbDebugOutput);
///     QApplication app(argc, argv);
///     ...
///     return app.exec();
///  }
///
#if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
    // static constexpr const char *format = "dd.MM.yyyy hh:mm:ss";
    QByteArray localMsg = msg.toLocal8Bit();
    QString fileName(context.file ? context.file : "N/A");
    QString function(context.function ? context.function : "N/A");
    char buf[OUTPUT_LEN]{};
    memset(buf, 0x00, sizeof(buf));
    QString const datetime = QDateTime::currentDateTime().toString(Qt::ISODateWithMs);
    switch (type) {
        case QtDebugMsg: {
            if (debugLevel == QtDebugMsg) {
                snprintf(buf, sizeof(buf)-1, "%s DEBG [%s:%s:%04u] %s",
                         datetime.toStdString().c_str(),
                         function.toStdString().c_str(),
                         fileName.toStdString().c_str(),
                         context.line,
                         localMsg.constData());
                fprintf(stderr, "%s\n", buf);
            }
        } break;
        case QtInfoMsg: {
            if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg) {
                snprintf(buf, sizeof(buf)-1, "%s DEBG [%s:%s:%04u] %s",
                         datetime.toStdString().c_str(),
                         function.toStdString().c_str(),
                         fileName.toStdString().c_str(),
                         context.line,
                         localMsg.constData());
                fprintf(stderr, "%s\n", buf);
            }
        } break;
        case QtWarningMsg: {
            if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg || debugLevel == QtWarningMsg) {
                snprintf(buf, sizeof(buf)-1, "%s DEBG [%s:%s:%04u] %s",
                         datetime.toStdString().c_str(),
                         function.toStdString().c_str(),
                         fileName.toStdString().c_str(),
                         context.line,
                         localMsg.constData());
                fprintf(stderr, "%s\n", buf);
            }
        } break;
        case QtCriticalMsg: {
            if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg
             || debugLevel == QtWarningMsg || debugLevel == QtCriticalMsg) {
                snprintf(buf, sizeof(buf)-1, "%s DEBG [%s:%s:%04u] %s",
                         datetime.toStdString().c_str(),
                         function.toStdString().c_str(),
                         fileName.toStdString().c_str(),
                         context.line,
                         localMsg.constData());
                fprintf(stderr, "%s\n", buf);
            }
        } break;
        case QtFatalMsg: {
            if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg
             || debugLevel == QtWarningMsg || debugLevel == QtCriticalMsg
             || debugLevel == QtFatalMsg) {
                snprintf(buf, sizeof(buf)-1, "%s DEBG [%s:%s:%04u] %s",
                         datetime.toStdString().c_str(),
                         function.toStdString().c_str(),
                         fileName.toStdString().c_str(),
                         context.line,
                         localMsg.constData());
                fprintf(stderr, "%s\n", buf);
            }
        } break;
        default: {
            fprintf(stderr, "%s No ErrorLevel defined! %s\n",
                datetime.toStdString().c_str(), msg.toStdString().c_str());
        }
    }
}
#endif