#define NX_SERVICE_ASSUME_NON_DOMAIN
#include "types.h"
#include "result.h"
#include "kernel/detect.h"
#include "kernel/svc.h"
#include "sf/service.h"
#include "services/fatal.h"
#include "services/sm.h"
#include "runtime/hosversion.h"

static void _fatalCmd(Result err, FatalPolicy type, FatalCpuContext *ctx, u32 cmd_id) {
    Result rc = 0;

    //Only [3.0.0+] supports FatalPolicy_ErrorScreen, when specified on pre-3.0.0 use FatalPolicy_ErrorReportAndErrorScreen instead.
    if (type == FatalPolicy_ErrorScreen && hosversionBefore(3,0,0)) type = FatalPolicy_ErrorReportAndErrorScreen;

    if (detectDebugger()) {
        svcBreak(BreakReason_Panic | BreakReason_NotificationOnlyFlag, (uintptr_t)&err, sizeof(err));
    }

    Handle session;
    rc = smInitialize();
    if (R_SUCCEEDED(rc)) {
        rc = smGetServiceOriginal(&session, smEncodeName("fatal:u"));
        smExit();
    }

    if (R_SUCCEEDED(rc)) {
        const struct {
            u32 result;
            u32 type;
            u64 pid_placeholder;
        } in = { err, type };

        Service s;
        serviceCreate(&s, session);
        serviceDispatchIn(&s, cmd_id, in,
            .buffer_attrs = { ctx ? (SfBufferAttr_In | SfBufferAttr_HipcMapAlias) : 0U },
            .buffers      = { { ctx, sizeof(*ctx) } },
            .in_send_pid  = true,
        );
        serviceClose(&s);
    }

    switch (type) {
        case FatalPolicy_ErrorReport:
            break;
        case FatalPolicy_ErrorReportAndErrorScreen:
        case FatalPolicy_ErrorScreen:
        default:
            svcExitProcess();
            __builtin_unreachable();
    }
}

void NX_NORETURN fatalThrow(Result err) {
    // By default, do not generate an error report.
    fatalThrowWithPolicy(err, FatalPolicy_ErrorScreen);
    svcExitProcess();
    __builtin_unreachable();
}

void fatalThrowWithPolicy(Result err, FatalPolicy type) {
    _fatalCmd(err, type, NULL, 1);
}

void fatalThrowWithContext(Result err, FatalPolicy type, FatalCpuContext *ctx) {
    _fatalCmd(err, type, ctx, 2);
}