//
//  tightbeam_base.h
//  Tightbeam

#ifndef __TIGHTBEAM_BASE_H
#define __TIGHTBEAM_BASE_H

#ifndef __RTKIT__
#ifndef KERNEL
#include <TargetConditionals.h>
#define __TB_TARGET_CONDITIONALS_ENABLED__ 1
#endif
#endif // __RTKIT__

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>

#if __has_include(<libkern/Block.h>)
#include <libkern/Block.h>
#elif __has_include(<sys/block.h>)
#include <sys/block.h>
#elif __has_include(<Block.h>)
#include <Block.h>
#endif

#define TB_API_VERSION 20240820
#define TB_MAX_CAPS 4

#if defined(KERNEL)
    #include <kern/debug.h>
    #define TB_ASSERT2(e, fmt, ...) ({ \
        if (!(e)) { \
            panic("TB_ASSERT: " #e ", " fmt " (%s:%d)", ##__VA_ARGS__, __FILE_NAME__, __LINE__); \
            __builtin_trap(); \
        } \
    })
    #define TB_ASSERT(e) TB_ASSERT2(e, "\b\b")
    #define TB_FATAL(fmt, ...) ({ \
        panic("TB_FATAL: " fmt, ##__VA_ARGS__); \
        __builtin_trap(); \
    })
#elif defined(__RTKIT__)
    #include <assert.h>
    #define TB_ASSERT(e) \
        if (!(e)) { \
            RTK_abort("TB_ASSERT: (%s:%d)", __FILE_NAME__, __LINE__); \
            __builtin_trap(); \
        }
    #define TB_ASSERT2(e, fmt, ...) TB_ASSERT(e)
    #define TB_FATAL(fmt, ...) RTK_abort(fmt, ##__VA_ARGS__)
#elif (defined(TARGET_OS_EXCLAVECORE) || defined(TARGET_OS_EXCLAVEKIT)) && (TARGET_OS_EXCLAVECORE || TARGET_OS_EXCLAVEKIT)
    #include <stdio.h>
    #define TB_ASSERT2(e, fmt, ...) \
        if (!(e)) { \
            printf("TB_ASSERT: " #e ", " fmt " (%s:%d)\n", ##__VA_ARGS__, __FILE_NAME__, __LINE__); \
            __builtin_trap(); \
        }
    #define TB_ASSERT(e) TB_ASSERT2(e, "\b\b")
    #define TB_FATAL(fmt, ...) ({ \
        printf("TB_FATAL: " fmt " (%s:%d)\n", \
                ##__VA_ARGS__, __FILE__, __LINE__); \
        __builtin_trap(); \
    })
#else
    #include <os/assumes.h>
    #include <stdio.h>
    #define TB_ASSERT2(e, fmt, ...) ({ \
        if (!(e)) { \
            printf("TB_ASSERT: " #e ", " fmt " (%s:%d)\n", ##__VA_ARGS__, __FILE_NAME__, __LINE__); \
            os_crash("TB_ASSERT: " #e); \
        } \
    })
    #define TB_ASSERT(e) TB_ASSERT2(e, "\b\b")
    #define TB_FATAL(fmt, ...) ({ \
        printf("TB_FATAL: " fmt " (%s:%d)\n", \
            ##__VA_ARGS__, __FILE__, __LINE__); \
        os_crash("TB_FATAL: " fmt); \
    })
#endif // defined(KERNEL)

#if defined(__cplusplus)
#define __TB_BEGIN_DECLS   extern "C" {
#define __TB_END_DECLS     }
#else
#define __TB_BEGIN_DECLS
#define __TB_END_DECLS
#endif

#define TB_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin")
#define TB_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end")
#define TB_EXPORT extern __attribute__((__visibility__("default")))
#define TB_NOEXPORT __attribute__((visibility("hidden")))
#define TB_UNUSED __attribute__((unused))
#define TB_NOESCAPE __attribute__((noescape))
#define TB_NOINLINE __attribute__((noinline))
#define TB_PACKED __attribute__((packed))

#ifndef __cplusplus
#define TB_TRANSPARENT_UNION __attribute__((__transparent_union__))
#else // __cplusplus
#define TB_TRANSPARENT_UNION
#endif // __cplusplus

#define __TB_ENUM_ATTR __attribute__((enum_extensibility(open)))
#define __TB_ENUM_ATTR_CLOSED __attribute__((enum_extensibility(closed)))
#define __TB_OPTIONS_ATTR __attribute__((flag_enum))
#define __TB_WEAK_IMPORT __attribute__((weak_import))
#define __TB_ALLOC_SIZE(...) __attribute__((alloc_size(__VA_ARGS__)))
#define __TB_WARN_UNUSED_RESULT __attribute__((warn_unused_result))

#define TB_OS_MUL_OVERFLOW(...) \
    if (__builtin_mul_overflow(__VA_ARGS__)) { \
        TB_FATAL("overflow detected when multiplying"); \
    }

#define TB_OS_ADD_OVERFLOW(...) \
    if (__builtin_add_overflow(__VA_ARGS__)) { \
        TB_FATAL("overflow detected when adding"); \
    }

#define TB_OS_SUB_OVERFLOW(...) \
    if (__builtin_sub_overflow(__VA_ARGS__)) { \
        TB_FATAL("overflow detected when subtracting"); \
    }

#if defined(TARGET_OS_EXCLAVECORE) && TARGET_OS_EXCLAVECORE
// For ExclaveCore, we put all of the vtables together in their own
// section, which reduces the overhead of fixing up tables we may not use.
// This is not necessary on other platforms, where fixup costs are not as
// high.
#define __TB_COMPONENT_VTABLE_ATTRIBUTE \
    __attribute ((section("__DATA,__TIGHTBEAM_VT")))
#else
#define __TB_COMPONENT_VTABLE_ATTRIBUTE
#endif

#define __TB_COMPONENT_DECLARATION(_sym) \
    TB_EXPORT __attribute__((no_sanitize("address"))) \
    void const* __set_tightbeam_sym_##_sym

#define __TB_COMPONENT_DEFINITION(_sym, _vis) \
    void const* __set_tightbeam_sym_##_sym \
    __attribute__ ((section("__DATA,__TIGHTBEAM"))) \
    __attribute__ ((visibility(_vis))) \
    __attribute__((no_sanitize("address"))) \
    __attribute__ ((used)) \
    = (void const *)&_sym

#define TB_ENUM(_name, _type, ...) \
    typedef enum : _type { __VA_ARGS__ } __TB_ENUM_ATTR _name##_t

#define TB_CLOSED_ENUM(_name, _type, ...) \
    typedef enum : _type { __VA_ARGS__ } __TB_ENUM_ATTR_CLOSED _name##_t

#define TB_SWIFT_NAME(_name) __attribute__((__swift_name__(#_name)))

#define TB_OPTIONS(_name, _type, ...) \
    typedef enum : _type { __VA_ARGS__ } __TB_ENUM_ATTR __TB_OPTIONS_ATTR _name##_t

#if defined(__unsafe_unretained)
    #define __TB_UNSAFE_UNRETAINED __unsafe_unretained
#else
    #define __TB_UNSAFE_UNRETAINED
#endif

#if __clang__
#define TB_DEPRECATED(m) __attribute__((deprecated(m)))
#else // __clang__
#define TB_DEPRECATED(m) __attribute__((deprecated))
#endif // __clang

// Define our own versions of Block_copy and Block_release until the build
// platform gets: rdar://problem/97945654

#define TB_BLOCK_COPY(...) \
    ((__typeof(__VA_ARGS__))_Block_copy( \
        __unsafe_forge_single(const void *, (const void *)(__VA_ARGS__))) \
    )
#define TB_BLOCK_RELEASE(...) \
    _Block_release( \
        __unsafe_forge_single(const void *, (const void *)(__VA_ARGS__)) \
    )

TB_ENUM(tb_error, uint32_t,
    TB_ERROR_SUCCESS TB_SWIFT_NAME(success) = 0,
    TB_ERROR_COULD_NOT_SEND TB_SWIFT_NAME(couldNotSend),
    TB_ERROR_MESSAGE_CREATE_FAILED TB_SWIFT_NAME(messageCreateFailed),
    TB_ERROR_ACTIVATION_FAILED TB_SWIFT_NAME(activationFailed),
    TB_ERROR_MESSAGE_RECEIVE_FAILED TB_SWIFT_NAME(messageReceiveFailed),
    TB_ERROR_REPLY_SEND_FAILED TB_SWIFT_NAME(replySendFailed),
	TB_ERROR_MESSAGE_ENCODE_FAILED TB_SWIFT_NAME(messageEncodeFailed),
    TB_ERROR_MESSAGE_DECODE_FAILED TB_SWIFT_NAME(messageDecodeFailed),
    TB_ERROR_VALIDATION_FAILED TB_SWIFT_NAME(validationFailed),
    TB_ERROR_USER_FAILURE TB_SWIFT_NAME(userFailure),
	TB_ERROR_MESSAGE_FORWARD_FAILURE TB_SWIFT_NAME(forwardFailure),
    TB_ERROR_CONFIGURATION_INVALID TB_SWIFT_NAME(configurationInvalid),
    TB_ERROR_SERVICE_DISABLED TB_SWIFT_NAME(serviceDisabled),
    TB_ERROR_ALLOCATE_FAILED TB_SWIFT_NAME(allocateFailed),
    TB_ERROR_MESSAGE_DEFERRED_SEND_UNSUPPORTED TB_SWIFT_NAME(
            deferredSendUnsupported),
    TB_ERROR_REPLY_NOT_FOUND TB_SWIFT_NAME(replyNotFound),
    TB_ERROR_SELECTOR_DISALLOWED TB_SWIFT_NAME(selectorDisallowed),
    TB_ERROR_NO_NOTIFICATION_PENDING TB_SWIFT_NAME(noNotificationPending),
    TB_ERROR_NOTIFICATION_CHECKIN_FAILED TB_SWIFT_NAME(notificationCheckinFailed),
    TB_ERROR_NOTIFICATION_RECEIVE_FAILED TB_SWIFT_NAME(notificationReceiveFailed),
) TB_SWIFT_NAME(TransportError);

TB_OPTIONS(tb_connection_flags, uint64_t,
    TB_CONNECTION_FLAGS_NONE TB_SWIFT_NAME(none) = 0,
    TB_CONNECTION_FAIL_ON_WAIT TB_SWIFT_NAME(failOnWait) = 1 << 0,
	TB_CONNECTION_WAIT_FOR_REPLY TB_SWIFT_NAME(waitForReply) = 1 << 1,
);

TB_OPTIONS(tb_transport_flags, uint16_t,
    TB_TRANSPORT_FLAG_NONE TB_SWIFT_NAME(none) = 0,
    TB_TRANSPORT_FLAG_MULTIPART_MESSAGE TB_SWIFT_NAME(multipartMessage) =
            1 << 0,
    TB_TRANSPORT_FLAG_MULTIPART_MESSAGE_COMPLETE TB_SWIFT_NAME(
            multipartMessageComplete) = 1 << 1,
    TB_TRANSPORT_FLAG_MULTIPART_REPLY_POLL TB_SWIFT_NAME(multipartReplyPoll) =
            1 << 2,
    TB_TRANSPORT_FLAG_ERROR TB_SWIFT_NAME(error) =
            1 << 3,
    TB_TRANSPORT_FLAG_NO_REPLY TB_SWIFT_NAME(noReply) = 1 << 4,
    TB_TRANSPORT_FLAG_TRANSPORT_DECODED TB_SWIFT_NAME(transportDecoded) =
            1 << 5
);

#define TB_CHECK_FLAG(flags, f) ((flags & f) != 0)

#if __swift__ || !defined(__arm__) && !defined(__aarch64__)

/*
 * For all platforms, assume Swift has floating-point support
 * (merely because it is convenient to not have conditional
 * compilation clutter in our Swift source until we need it)
 *
 * For non-Swift, i.e., C, assume all non-ARM cases also have
 * floating-point (although those cases may not be relevant)
 */
#define TB_HAS_FP32 1
#define TB_HAS_FP64 1

#else /* __swift__, __arm__, __aarch64__ */

/*
 * ARM does not always support floating-point; for example,
 * `-mgeneral-regs-only` means no floating-point available
 */
#if defined(__ARM_FP) && (__ARM_FP & 0x04)
#define TB_HAS_FP32 1
#else /* __ARM_FP & 0x04 */
#define TB_HAS_FP32 0
#endif /* __ARM_FP & 0x04 */

/*
 * NB: We check the bits for supported levels individually,
 * but, technically, `__ARM_FP` cannot have only 64-bit
 */
#if defined(__ARM_FP) && (__ARM_FP & 0x08)
_Static_assert(__ARM_FP & 0x04, "Single precision must be supported as well");
#define TB_HAS_FP64 1
#else /* __ARM_FP & 0x08 */
#define TB_HAS_FP64 0
#endif /* __ARM_FP & 0x08 */

#endif  /* __swift__, __arm__, __aarch64__ */

#if TB_HAS_FP32
typedef float tb_float32_t;
#endif /* TB_HAS_FP32 */

#if TB_HAS_FP64
typedef double tb_float64_t;
#endif /* TB_HAS_FP64 */

#if __TB_TARGET_CONDITIONALS_ENABLED__

#if !TARGET_OS_MACCATALYST \
    && (TARGET_OS_IOS || TARGET_OS_OSX || TARGET_OS_TV || TARGET_OS_WATCH || TARGET_OS_VISION) \
    && __has_include(<dispatch/dispatch.h>) \
    && __has_include(<dispatch/queue.h>)

#define TB_DISPATCH_NOTIFICATION_SUPPORTED 0

#if (TARGET_OS_IOS && (__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_17_4)) \
 || (TARGET_OS_OSX && (__MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_14_4)) \
 || (TARGET_OS_TV && (__TV_OS_VERSION_MIN_REQUIRED >= __TVOS_17_4)) \
 || (TARGET_OS_WATCH && (__WATCH_OS_VERSION_MIN_REQUIRED >= __WATCHOS_12_0)) \
 || (TARGET_OS_VISION && (__VISION_OS_VERSION_MIN_REQUIRED >= __VISIONOS_3_0))
#undef TB_DISPATCH_NOTIFICATION_SUPPORTED
#define TB_DISPATCH_NOTIFICATION_SUPPORTED 1
#endif // __IPHONE_OS_VERSION_MIN_REQUIRED ...

#endif // !TARGET_OS_MACCATALYST && (TARGET_OS_IOS || TARGET_OS_OSX ...

// TB_DAEMON_NOTIFICATION_SUPPORTED: for daemon notifications via bootstrap/mach
#if __has_include(<bootstrap.h>) \
    && __has_include(<mach/mach.h>) \
    && __has_include(<dispatch/dispatch.h>)
#define TB_DAEMON_NOTIFICATION_SUPPORTED 1
#else
#define TB_DAEMON_NOTIFICATION_SUPPORTED 0
#endif

#undef __TB_TARGET_CONDITIONALS_ENABLED__
#endif // __TB_TARGET_CONDITIONALS_ENABLED__

#endif // __TIGHTBEAM_BASE_H
