#pragma once

#include <3ds/types.h>
#include <3ds/result.h>
#include <3ds/os.h>

#define REG32(reg)              (*(vu32 *)reg)
#define REG64(reg)              (*(vu64 *)reg)

#define NSTID                   REG64(0x1FF80008)
#define SYSCOREVER              REG32(0x1FF80010)
#define APPMEMTYPE              REG32(0x1FF80030)
#define APPMEMALLOC             REG32(0x1FF80040)
#define SYSMEMALLOC             REG32(0x1FF80044)

#define IS_N3DS                 (*(vu32 *)0x1FF80030 >= 6) // APPMEMTYPE. Hacky but doesn't use APT
#define N3DS_TID_MASK           0xF0000000ULL
#define N3DS_TID_BIT            0x20000000ULL

#define TRY(expr)               if(R_FAILED(res = (expr))) return res;
#define TRYG(expr, label)       if(R_FAILED(res = (expr))) goto label;

#define XDS
#ifdef XDS
static void hexItoa(u64 number, char *out, u32 digits, bool uppercase)
{
    const char hexDigits[] = "0123456789ABCDEF";
    const char hexDigitsLowercase[] = "0123456789abcdef";
    u32 i = 0;

    while(number > 0)
    {
        out[digits - 1 - i++] = uppercase ? hexDigits[number & 0xF] : hexDigitsLowercase[number & 0xF];
        number >>= 4;
    }

    while(i < digits) out[digits - 1 - i++] = '0';
}
#endif

static void __attribute__((noinline)) panic(Result res)
{
#ifndef XDS
    (void)res;
    __builtin_trap();
#else
    char buf[32] = {0};
    hexItoa(res, buf, 8, false);
    svcOutputDebugString(buf, 8);
    svcBreak(USERBREAK_PANIC);
#endif
}

static inline Result assertSuccess(Result res)
{
    if(R_FAILED(res)) {
        panic(res);
    }

    return res;
}

static inline Result notifySubscribers(u32 notificationId)
{
    Result res = srvPublishToSubscriber(notificationId, 0);
    if (res == (Result)0xD8606408) {
        panic(res);
    }

    return res;
}

static inline s64 nsToTicks(s64 ns)
{
    return ns * SYSCLOCK_ARM11 / (1000 * 1000 * 1000LL);
}

static inline s64 ticksToNs(s64 ticks)
{
    return 1000 * 1000 * 1000LL * ticks / SYSCLOCK_ARM11;
}