
#include "BreakToDebugger.h"
#include "Blueprint/BlueprintExceptionInfo.h"
#include "Kismet2/KismetDebugUtilities.h"
#include "LuprexEditorSettings.h"

ELogVerbosity::Type FlxBreakToDebuggerOutputDevice::ConvertThreshold(ElxBreakToDebuggerThreshold Verbosity) {
	switch (Verbosity) {
		case ElxBreakToDebuggerThreshold::Error:		return ELogVerbosity::Error;
		case ElxBreakToDebuggerThreshold::Warning:		return ELogVerbosity::Warning;
		case ElxBreakToDebuggerThreshold::Display:		return ELogVerbosity::Display;
		case ElxBreakToDebuggerThreshold::Log:			return ELogVerbosity::Log;
		case ElxBreakToDebuggerThreshold::Verbose:		return ELogVerbosity::Verbose;
		case ElxBreakToDebuggerThreshold::VeryVerbose:	return ELogVerbosity::VeryVerbose;
		case ElxBreakToDebuggerThreshold::Fatal:		return ELogVerbosity::Fatal;
	}
}

FlxBreakToDebuggerOutputDevice::FlxBreakToDebuggerOutputDevice()
    : Settings(GetDefault<UlxEditorSettings>())
{
	GLog->AddOutputDevice(this);
}

FlxBreakToDebuggerOutputDevice::~FlxBreakToDebuggerOutputDevice()
{
	GLog->RemoveOutputDevice(this);
}

namespace UBreakPoint {
    static volatile int V;
    FORCENOINLINE static void OnLogFatal() {
        V = 0;
    }
    FORCENOINLINE static void OnLogError() {
		V = 1;
        OnLogFatal();
	}
    FORCENOINLINE static void OnLogWarning() {
        V = 2;
        OnLogError();
    }
}

static const FName LogBlueprintDebugName(TEXT("LogBlueprintDebug"));


void FlxBreakToDebuggerOutputDevice::Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category)
{
    // If the error isn't serious enough, do nothing.
    //
    if (Verbosity > ConvertThreshold(Settings->BreakToDebuggerLogVerbosity))
    {
        return;
    }

    // If the Category is LogBlueprintDebug, then we're inside the debugger already.
    //
    if (Category == LogBlueprintDebugName)
    {
        return;
    }

    // Find out if we're running in a blueprint thread.  If not, return.
    //
    FFrame* Frame = FFrame::GetThreadLocalTopStackFrame();
    if (Frame == nullptr) return;
    UObject *TopObject = Frame->Object;
    if (TopObject == nullptr) return;

    // If the category is in the exclude list, do nothing.
    //
    if (Settings->BreakToDebuggerExcludeCategories.Contains(Category))
    {
        return;
    }

    // Notify the debugger that there's been an exception.
    //
    FBlueprintExceptionInfo ExceptionInfo(EBlueprintExceptionType::Breakpoint, FText::FromStringView(FStringView(V)));
    FBlueprintCoreDelegates::ThrowScriptException(TopObject, *Frame, ExceptionInfo);
}


