#include "message_handler.h"

#include <QDateTime>
#include <cstring>

#define OUTPUT_LEN (20)


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)
void atbDebugOutput(QtMsgType type, const char *msg) {
    switch (type) {
        case QtDebugMsg: {
            if (debugLevel <= QtDebugMsg) {
                fprintf(stderr, "%*.*s Debug: %s\n", OUTPUT_LEN, OUTPUT_LEN,
                    QDateTime::currentDateTime().toString(Qt::ISODate).toStdString().c_str(), msg);
            }
        } break;
        case QtInfoMsg: {
            if (debugLevel <= QtInfoMsg) {
                fprintf(stderr, "%*.*s Info: %s\n", OUTPUT_LEN, OUTPUT_LEN,
                    QDateTime::currentDateTime().toString(Qt::ISODate).toStdString().c_str(), msg);
            }
        } break;
        case QtWarningMsg: {
            if (debugLevel <= QtWarningMsg) {
                fprintf(stderr, "%*.*s Warning: %s\n", OUTPUT_LEN, OUTPUT_LEN,
                    QDateTime::currentDateTime().toString(Qt::ISODate).toStdString().c_str(), msg);
            }
        } break;
        case QtCriticalMsg: {
            if (debugLevel <= QtCriticalMsg) {
                fprintf(stderr, "%*.*s Critical: %s\n", OUTPUT_LEN, OUTPUT_LEN,
                    QDateTime::currentDateTime().toString(Qt::ISODate).toStdString().c_str(), msg);
            }
        } break;
        case QtFatalMsg: {
            if (debugLevel <= QtFatalMsg) {
                fprintf(stderr, "%*.*s Fatal: %s\n", OUTPUT_LEN, OUTPUT_LEN,
                    QDateTime::currentDateTime().toString(Qt::ISODate).toStdString().c_str(), msg);
            }
            // abort();
        } break;
        default: {
            fprintf(stderr, "%*.*s No ErrorLevel defined! %s\n", OUTPUT_LEN, OUTPUT_LEN,
                QDateTime::currentDateTime().toString(Qt::ISODate).toStdString().c_str(), msg);
        }
    }
}
#elif 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();
    const char *file = context.file ? context.file : "";
    const char *function = context.function ? context.function : "";
    const char *p = std::strstr(function, "::");
    if (p) {
        function = p + 2;
    }
    char const* output = std::strrchr(file, '/');
    if (output) {
        file = output + 1;
    }
    qint64 const currentMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch();
    int const fractional_part = currentMSecsSinceEpoch % 1000;
    QDateTime const datetime = QDateTime::fromMSecsSinceEpoch(currentMSecsSinceEpoch);
    switch (type) {
        case QtDebugMsg: {
            if (debugLevel <= QtDebugMsg) {
                // fprintf(stderr, "%*.*s          CTX %s (%s:%u) ->\n", OUTPUT_LEN, OUTPUT_LEN,
                //        "", function, file, context.line);
                //fprintf(stderr, "%*.*s.%03d DEBG %s\n", OUTPUT_LEN, OUTPUT_LEN,
                //        datetime.toString(format).toStdString().c_str(), fractional_part,
                //        localMsg.constData());
                fprintf(stderr, "%*.*s.%03d DEBUG %s (%s:%u)\n", OUTPUT_LEN, OUTPUT_LEN,
                        datetime.toString(format).toStdString().c_str(), fractional_part,
                        localMsg.constData(), file, context.line);
            }
        } break;
        case QtInfoMsg: {
            if (debugLevel <= QtInfoMsg) {
                fprintf(stderr, "%*.*s.%03d INFO %s (%s:%u)\n", OUTPUT_LEN, OUTPUT_LEN,
                        datetime.toString(format).toStdString().c_str(), fractional_part,
                        localMsg.constData(), file, context.line);
            }
        } break;
        case QtWarningMsg: {
            if (debugLevel <= QtWarningMsg) {
                fprintf(stderr, "%*.*s.%03d WARN %s (%s:%u)\n", OUTPUT_LEN, OUTPUT_LEN,
                        datetime.toString(format).toStdString().c_str(), fractional_part,
                        localMsg.constData(), file, context.line);
            }
        } break;
        case QtCriticalMsg: {
            if (debugLevel <= QtCriticalMsg) {
                fprintf(stderr, "%*.*s.%03d CRIT %s (%s:%u)\n", OUTPUT_LEN, OUTPUT_LEN,
                        datetime.toString(format).toStdString().c_str(), fractional_part,
                        localMsg.constData(), file, context.line);
            }
        } break;
        case QtFatalMsg: {
            if (debugLevel <= QtFatalMsg) {
                fprintf(stderr, "%*.*s.%03d FATAL %s (%s:%u)\n", OUTPUT_LEN, OUTPUT_LEN,
                        datetime.toString(format).toStdString().c_str(), fractional_part,
                        localMsg.constData(), file, context.line);
            }
        } break;
        default: {
            fprintf(stderr, "%*.*s.%03d No ErrorLevel defined! %s\n", OUTPUT_LEN, OUTPUT_LEN,
                datetime.toString(format).toStdString().c_str(), fractional_part,
                msg.toStdString().c_str());
        }
    }
}
#endif