////////////////////////////////////////////////////////////
//
// BreakToDebugger
//
// When an error message gets written to UE_LOG, we can
// optionally trigger the blueprint debugger.
//
// This only affects UE_LOG messages that are generated by
// the blueprint thread, during blueprint execution. Log
// messages from other threads do not trigger the debugger.
//
// The following explains how we trigger the blueprint
// debugger on UE_LOG messages.  Log messages are sent to a
// long list of output devices, including: the visual studio
// output window, the unreal editor output window, the log
// file, and so forth.  We add another pseudo output device.
// This output device doesn't actually send the log message
// anywhere, instead, it just activates the blueprint
// debugger.
//
// UE_LOG messages can be generated from any thread. The
// pseudo output device checks what thread it is running in,
// and if it's not the blueprint thread, it does nothing at
// all. If it is the blueprint thread, it's safe to trigger
// the blueprint debugger.
//
// One annoying limitation of this design is that our output
// device ends up early in the list, so the debugger runs
// *before* the message shows up in visual studio or the
// unreal editor.  As a result, when you are in the
// debugger, the message won't be in your output window.
// Pressing 'single step' always reveals the message.
//
// The blueprint node "Format Log Message" uses UE_LOG
// internally, so therefore, that too can trigger the
// blueprint debugger.
//
////////////////////////////////////////////////////////////

#pragma once

#include "Containers/Array.h"
#include "CoreMinimal.h"
#include "HAL/Platform.h"
#include "Misc/OutputDeviceError.h"
#include "UObject/NameTypes.h"
#include "UObject/ObjectMacros.h"
#include "UObject/UObjectGlobals.h"
#include "BreakToDebugger.generated.h"

// ElxBreakToDebuggerThreshold:
//
// Controls the sensitivity level at which UE_LOG messages
// trigger the blueprint debugger.
//
UENUM(BlueprintType)
enum class ElxBreakToDebuggerThreshold : uint8 {

	/** Break on errors. */
	Error,

	/** Break on warnings and above. */
	Warning,

	/** Break on display messages and above. */
	Display,

	/** Break on log messages and above. */
	Log,

	/** Break on verbose messages and above. */
	Verbose,

	/** Break on all messages. */
	VeryVerbose,

	/** Break on fatal errors only (ie, never break -- the process crashes instead). */
	Fatal,
};


class UlxEditorSettings;

struct FlxBreakToDebuggerOutputDevice : public FOutputDevice
{
public:
    // The constructor and destructor automatically register
    // this output device with GLog.
    //
    FlxBreakToDebuggerOutputDevice();
    ~FlxBreakToDebuggerOutputDevice();

    // Inspect a log message.
    //
    INTEGRATION_API virtual void Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category) override;

    // If the device is marked 'CanBeUsedOnMultipleThreads,'
    // then UE_LOG will call Serialize from the current
    // thread, otherwise, it will call Serialize from the
    // logging thread.  Using the logging thread would
    // defeat the purpose of this device, so it's imperative
    // that we set this flag.
    //
    INTEGRATION_API virtual bool CanBeUsedOnMultipleThreads() const override { return true; }

private:
    static ELogVerbosity::Type ConvertThreshold(ElxBreakToDebuggerThreshold Verbosity);
    const UlxEditorSettings *Settings = nullptr;
};
