// SPDX-License-Identifier: GPL-2.0-or-later
/**
 * @file
 * A dialog that displays log messages.
 */
/* Authors:
 *   Bob Jamison
 *   Other dudes from The Inkscape Organization
 *
 * Copyright (C) 2004 The Inkscape Organization
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#include <gtkmm/box.h>
#include <gtkmm/dialog.h>
#include <gtkmm/textview.h>
#include <gtkmm/menubar.h>
#include <gtkmm/scrolledwindow.h>
#include <glibmm/i18n.h>

#include "debug.h"

namespace Inkscape {
namespace UI {
namespace Dialog {

/**
 * A very simple dialog for displaying Inkscape messages - implementation.
 */
class DebugDialogImpl : public DebugDialog, public Gtk::Dialog
{
public:
    DebugDialogImpl();
    ~DebugDialogImpl() override;

    void show() override;
    void hide() override;
    void clear() override;
    void message(char const *msg) override;
    void captureLogMessages() override;
    void releaseLogMessages() override;

private:
    Gtk::MenuBar menuBar;
    Gtk::Menu   fileMenu;
    Gtk::ScrolledWindow textScroll;
    Gtk::TextView messageText;

    //Handler ID's
    guint handlerDefault;
    guint handlerGlibmm;
    guint handlerAtkmm;
    guint handlerPangomm;
    guint handlerGdkmm;
    guint handlerGtkmm;
};

void DebugDialogImpl::clear()
{
    Glib::RefPtr<Gtk::TextBuffer> buffer = messageText.get_buffer();
    buffer->erase(buffer->begin(), buffer->end());
}

DebugDialogImpl::DebugDialogImpl()
{
    set_title(_("Messages"));
    set_size_request(300, -1);
    auto mainVBox = get_content_area();

    //## Add a menu for clear()
    Gtk::MenuItem* item = Gtk::manage(new Gtk::MenuItem(_("_File"), true));
    item->set_submenu(fileMenu);
    menuBar.append(*item);

    item = Gtk::manage(new Gtk::MenuItem(_("_Clear"), true));
    item->signal_activate().connect(sigc::mem_fun(*this, &DebugDialogImpl::clear));
    fileMenu.append(*item);

    item = Gtk::manage(new Gtk::MenuItem(_("Capture log messages")));
    item->signal_activate().connect(sigc::mem_fun(*this, &DebugDialogImpl::captureLogMessages));
    fileMenu.append(*item);
    
    item = Gtk::manage(new Gtk::MenuItem(_("Release log messages")));
    item->signal_activate().connect(sigc::mem_fun(*this, &DebugDialogImpl::releaseLogMessages));
    fileMenu.append(*item);

    mainVBox->pack_start(menuBar, Gtk::PACK_SHRINK);
    

    //### Set up the text widget
    messageText.set_editable(false);
    textScroll.add(messageText);
    textScroll.set_policy(Gtk::POLICY_ALWAYS, Gtk::POLICY_ALWAYS);
    mainVBox->pack_start(textScroll);

    show_all_children();

    message("ready.");
    message("enable log display by setting ");
    message("dialogs.debug 'redirect' attribute to 1 in preferences.xml");

    handlerDefault = 0;
    handlerGlibmm  = 0;
    handlerAtkmm   = 0;
    handlerPangomm = 0;
    handlerGdkmm   = 0;
    handlerGtkmm   = 0;
}


DebugDialog *DebugDialog::create()
{
    DebugDialog *dialog = new DebugDialogImpl();
    return dialog;
}

DebugDialogImpl::~DebugDialogImpl()
= default;

void DebugDialogImpl::show()
{
    //call super()
    Gtk::Dialog::show();
    //sp_transientize(GTK_WIDGET(gobj()));  //Make transient
    raise();
    Gtk::Dialog::present();
}

void DebugDialogImpl::hide()
{
    // call super
    Gtk::Dialog::hide();
}

void DebugDialogImpl::message(char const *msg)
{
    Glib::RefPtr<Gtk::TextBuffer> buffer = messageText.get_buffer();
    Glib::ustring uMsg = msg;
    if (uMsg[uMsg.length()-1] != '\n')
        uMsg += '\n';
    buffer->insert (buffer->end(), uMsg);
}

/* static instance, to reduce dependencies */
static DebugDialog *debugDialogInstance = nullptr;

DebugDialog *DebugDialog::getInstance()
{
    if (!debugDialogInstance) {
        debugDialogInstance = new DebugDialogImpl();
    }
    return debugDialogInstance;
}



void DebugDialog::showInstance()
{
    DebugDialog *debugDialog = getInstance();
    debugDialog->show();
    // this is not a real memleak because getInstance() only creates a debug dialog once, and returns that instance for all subsequent calls
    // cppcheck-suppress memleak
}




/*##### THIS IS THE IMPORTANT PART ##### */
static void dialogLoggingFunction(const gchar */*log_domain*/,
                                  GLogLevelFlags /*log_level*/,
                                  const gchar *messageText,
                                  gpointer user_data)
{
    DebugDialogImpl *dlg = static_cast<DebugDialogImpl *>(user_data);
    dlg->message(messageText);
}


void DebugDialogImpl::captureLogMessages()
{
    /*
    This might likely need more code, to capture Gtkmm
    and Glibmm warnings, or maybe just simply grab stdout/stderr
    */
    GLogLevelFlags flags = (GLogLevelFlags) (G_LOG_LEVEL_ERROR   | G_LOG_LEVEL_CRITICAL |
                             G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE  |
                             G_LOG_LEVEL_INFO    | G_LOG_LEVEL_DEBUG);
    if ( !handlerDefault ) {
        handlerDefault = g_log_set_handler(nullptr, flags,
              dialogLoggingFunction, (gpointer)this);
    }
    if ( !handlerGlibmm ) {
        handlerGlibmm = g_log_set_handler("glibmm", flags,
              dialogLoggingFunction, (gpointer)this);
    }
    if ( !handlerAtkmm ) {
        handlerAtkmm = g_log_set_handler("atkmm", flags,
              dialogLoggingFunction, (gpointer)this);
    }
    if ( !handlerPangomm ) {
        handlerPangomm = g_log_set_handler("pangomm", flags,
              dialogLoggingFunction, (gpointer)this);
    }
    if ( !handlerGdkmm ) {
        handlerGdkmm = g_log_set_handler("gdkmm", flags,
              dialogLoggingFunction, (gpointer)this);
    }
    if ( !handlerGtkmm ) {
        handlerGtkmm = g_log_set_handler("gtkmm", flags,
              dialogLoggingFunction, (gpointer)this);
    }
    message("log capture started");
}

void DebugDialogImpl::releaseLogMessages()
{
    if ( handlerDefault ) {
        g_log_remove_handler(nullptr, handlerDefault);
        handlerDefault = 0;
    }
    if ( handlerGlibmm ) {
        g_log_remove_handler("glibmm", handlerGlibmm);
        handlerGlibmm = 0;
    }
    if ( handlerAtkmm ) {
        g_log_remove_handler("atkmm", handlerAtkmm);
        handlerAtkmm = 0;
    }
    if ( handlerPangomm ) {
        g_log_remove_handler("pangomm", handlerPangomm);
        handlerPangomm = 0;
    }
    if ( handlerGdkmm ) {
        g_log_remove_handler("gdkmm", handlerGdkmm);
        handlerGdkmm = 0;
    }
    if ( handlerGtkmm ) {
        g_log_remove_handler("gtkmm", handlerGtkmm);
        handlerGtkmm = 0;
    }
    message("log capture discontinued");
}



} //namespace Dialogs
} //namespace UI
} //namespace Inkscape

/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
