///////////////////////////////////////////////////////////////////////////////
// Name:        wx/gtk/private/log.h
// Purpose:     Support for filtering GTK log messages.
// Author:      Vadim Zeitlin
// Created:     2022-05-11
// Copyright:   (c) 2022 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence:     wxWindows licence
///////////////////////////////////////////////////////////////////////////////

#ifndef _WX_GTK_PRIVATE_LOG_H_
#define _WX_GTK_PRIVATE_LOG_H_

#include <glib.h>

// Support for custom log writers is only available in glib 2.50 or later.
#if GLIB_CHECK_VERSION(2, 50, 0)
    #define wxHAS_GLIB_LOG_WRITER
#endif

namespace wxGTKImpl
{

#ifdef wxHAS_GLIB_LOG_WRITER

// LogFilter is the base class for filtering GTK log messages
//
// Note that all members of this class are defined in src/gtk/app.cpp.
class LogFilter
{
public:
    LogFilter()
    {
        m_next = nullptr;
    }

    // Allow installing our own log writer function, we don't do it by default
    // because this results in a fatal error if the application had already
    // called g_log_set_writer_func() on its own.
    static void Allow() { ms_allowed = true; }

    // Function to call to install this filter as the active one if we're
    // allowed to do this, i.e. if Allow() had been called before.
    //
    // Does nothing and just returns false if run-time glib version is too old.
    bool Install();

protected:
    // Function to override in the derived class to actually filter: return
    // true if the message should be suppressed or false if it should be passed
    // through to the default writer (which may, or not, show it).
    virtual bool Filter(GLogLevelFlags log_level,
                        const GLogField* fields,
                        gsize n_fields) const = 0;

    // Typically called from the derived class dtor to stop using this filter.
    void Uninstall();

private:
    // The function used as glib log writer.
    static GLogWriterOutput
    wx_log_writer(GLogLevelFlags   log_level,
                  const GLogField *fields,
                  gsize            n_fields,
                  gpointer         user_data);

    // False initially, indicating that we're not allowed to install our own
    // logging function.
    static bool ms_allowed;

    // False initially, set to true when we install wx_log_writer() as the log
    // writer. Once we do it, we never change it any more.
    static bool ms_installed;

    // We maintain a simple linked list of log filters and this is the head of
    // this list.
    static LogFilter* ms_first;

    // Next entry in the linked list, may be null.
    LogFilter* m_next;

    wxDECLARE_NO_COPY_CLASS(LogFilter);
};

// LogFilterByLevel filters out all the messages at the specified level.
class LogFilterByLevel : public LogFilter
{
public:
    LogFilterByLevel() = default;

    void SetLevelToIgnore(int flags)
    {
        m_logLevelToIgnore = flags;
    }

protected:
    bool Filter(GLogLevelFlags log_level,
                const GLogField* WXUNUSED(fields),
                gsize WXUNUSED(n_fields)) const override
    {
        return log_level & m_logLevelToIgnore;
    }

private:
    int m_logLevelToIgnore;

    wxDECLARE_NO_COPY_CLASS(LogFilterByLevel);
};

// LogFilterByMessage filters out all the messages with the specified content.
class LogFilterByMessage : public LogFilter
{
public:
    // Objects of this class are supposed to be created with literal strings as
    // argument, so don't bother copying the string but just use the pointer.
    explicit LogFilterByMessage(const char* message)
        : m_message(message)
    {
        // We shouldn't warn about anything if Install() failed.
        m_warnNotFiltered = Install();
    }

    // Remove this filter when the object goes out of scope.
    //
    // The dtor also checks if we actually filtered the message and logs a
    // trace message with the "gtklog" mask if we didn't: this allows checking
    // if the filter is actually being used.
    ~LogFilterByMessage();

protected:
    bool Filter(GLogLevelFlags WXUNUSED(log_level),
                const GLogField* fields,
                gsize n_fields) const override;

private:
    const char* const m_message;

    mutable bool m_warnNotFiltered;

    wxDECLARE_NO_COPY_CLASS(LogFilterByMessage);
};

#else // !wxHAS_GLIB_LOG_WRITER

// Provide stubs to avoid having to use preprocessor checks in the code using
// these classes.
class LogFilterByMessage
{
public:
    explicit LogFilterByMessage(const char* WXUNUSED(message)) { }
};

#endif // wxHAS_GLIB_LOG_WRITER/!wxHAS_GLIB_LOG_WRITER

} // namespace wxGTKImpl

#endif // _WX_GTK_PRIVATE_LOG_H_
