// Copyright (c) 2025 The Dash Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif

#include <crypto/x11/dispatch.h>

#if !defined(DISABLE_OPTIMIZED_SHA256)
#include <compat/cpuid.h>

#if defined(ENABLE_ARM_AES) || defined(ENABLE_ARM_NEON)
#if defined(__APPLE__)
#include <sys/sysctl.h>
#include <sys/types.h>
#endif // __APPLE__

#if defined(__linux__)
#include <asm/hwcap.h>
#include <sys/auxv.h>
#endif // __linux__

#if defined(__FreeBSD__)
#include <machine/elf.h>
#include <sys/auxv.h>
#endif // __FreeBSD__

#if defined(_WIN32)
#include <processthreadsapi.h>
#include <winnt.h>
#endif // _WIN32
#endif // ENABLE_ARM_AES || ENABLE_ARM_NEON
#endif // !DISABLE_OPTIMIZED_SHA256

#include <cstddef>

namespace sapphire {
#if !defined(DISABLE_OPTIMIZED_SHA256)
#if defined(ENABLE_ARM_AES)
namespace arm_crypto_echo {
void FullStateRound(uint64_t W[16][2], uint32_t& k0, uint32_t& k1, uint32_t& k2, uint32_t& k3);
} // namespace arm_crypto_echo
namespace arm_crypto_shavite {
void Compress(sph_shavite_big_context *sc, const void *msg);
} // namespace arm_crypto_shavite
#endif // ENABLE_ARM_AES

#if defined(ENABLE_ARM_NEON)
namespace arm_neon_echo {
void ShiftAndMix(uint64_t W[16][2]);
} // namespace arm_neon_echo
#endif // ENABLE_ARM_NEON

#if defined(ENABLE_SSSE3)
namespace ssse3_echo {
void ShiftAndMix(uint64_t W[16][2]);
} // namespace ssse3_echo
#endif // ENABLE_SSSE3

#if defined(ENABLE_SSE41) && defined(ENABLE_X86_AESNI)
namespace x86_aesni_echo {
void FullStateRound(uint64_t W[16][2], uint32_t& k0, uint32_t& k1, uint32_t& k2, uint32_t& k3);
} // namespace x86_aesni_echo
namespace x86_aesni_shavite {
void Compress(sph_shavite_big_context *sc, const void *msg);
} // namespace x86_aesni_shavite
#endif // ENABLE_SSE41 && ENABLE_X86_AESNI
#endif // !DISABLE_OPTIMIZED_SHA256

namespace soft_echo {
void FullStateRound(uint64_t W[16][2], uint32_t& k0, uint32_t& k1, uint32_t& k2, uint32_t& k3);
void ShiftAndMix(uint64_t W[16][2]);
} // namespace soft_echo
namespace soft_shavite {
void Compress(sph_shavite_big_context *sc, const void *msg);
} // namespace soft_shavite
} // namespace sapphire

namespace {
#if !defined(DISABLE_OPTIMIZED_SHA256)
#if defined(ENABLE_ARM_AES) || defined(ENABLE_ARM_NEON)
#if defined(__APPLE__)
bool IsSysCtlNonZero(const char* name)
{
    int val = 0;
    size_t len = sizeof(val);
    return ::sysctlbyname(name, &val, &len, nullptr, 0) == 0 && val != 0;
}
#endif // __APPLE__
#endif // ENABLE_ARM_AES || ENABLE_ARM_NEON
#endif // !DISABLE_OPTIMIZED_SHA256
} // anonymous namespace

extern sapphire::dispatch::EchoShiftMix echo_shift_mix;
extern sapphire::dispatch::EchoRoundFn echo_round;
extern sapphire::dispatch::ShaviteCompressFn shavite_c512;

void SapphireAutoDetect()
{
    echo_round = sapphire::soft_echo::FullStateRound;
    echo_shift_mix = sapphire::soft_echo::ShiftAndMix;
    shavite_c512 = sapphire::soft_shavite::Compress;

#if !defined(DISABLE_OPTIMIZED_SHA256)
#if defined(HAVE_GETCPUID)
    uint32_t eax, ebx, ecx, edx;
    GetCPUID(1, 0, eax, ebx, ecx, edx);
#if defined(ENABLE_SSE41) && defined(ENABLE_X86_AESNI)
    const bool use_sse_4_1 = ((ecx >> 19) & 1);
    const bool use_aes_ni = ((ecx >> 25) & 1);
    if (use_sse_4_1 && use_aes_ni) {
        echo_round = sapphire::x86_aesni_echo::FullStateRound;
        shavite_c512 = sapphire::x86_aesni_shavite::Compress;
    }
#endif // ENABLE_SSE41 && ENABLE_X86_AESNI
#if defined(ENABLE_SSSE3)
    const bool use_ssse3 = ((ecx >> 9) & 1);
    if (use_ssse3) {
        echo_shift_mix = sapphire::ssse3_echo::ShiftAndMix;
    }
#endif // ENABLE_SSSE3
#endif // HAVE_GETCPUID

#if defined(ENABLE_ARM_AES) || defined(ENABLE_ARM_NEON)
    [[maybe_unused]] bool have_arm_aes = false;
    [[maybe_unused]] bool have_arm_neon = false;

#if defined(__APPLE__)
    have_arm_aes = IsSysCtlNonZero("hw.optional.arm.FEAT_AES");
    have_arm_neon = IsSysCtlNonZero("hw.optional.neon") || IsSysCtlNonZero("hw.optional.AdvSIMD") ||
                    IsSysCtlNonZero("hw.optional.arm.AdvSIMD"); // See https://github.com/google/cpu_features/issues/390
#endif // __APPLE__

#if defined(__linux__)
#if defined(__arm__)
    have_arm_aes = (::getauxval(AT_HWCAP2) & HWCAP2_AES);
    have_arm_neon = (::getauxval(AT_HWCAP) & HWCAP_NEON);
#endif // __arm__
#if defined(__aarch64__)
    have_arm_aes = (::getauxval(AT_HWCAP) & HWCAP_AES);
    have_arm_neon = (::getauxval(AT_HWCAP) & HWCAP_ASIMD);
#endif // __aarch64__
#endif // __linux__

#if defined(__FreeBSD__)
    [[maybe_unused]] unsigned long hwcap{0}, hwcap2{0};
#if defined(__arm__)
    have_arm_aes = ((::elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2)) == 0) && ((hwcap2 & HWCAP2_AES) != 0));
    have_arm_neon = ((::elf_aux_info(AT_HWCAP, &hwcap, sizeof(hwcap)) == 0) && ((hwcap & HWCAP_NEON) != 0));
#endif // __arm__
#if defined(__aarch64__)
    if (::elf_aux_info(AT_HWCAP, &hwcap, sizeof(hwcap)) == 0) {
        have_arm_aes = ((hwcap & HWCAP_AES) != 0);
        have_arm_neon = ((hwcap & HWCAP_ASIMD) != 0);
    }
#endif // __aarch64__
#endif // __FreeBSD__

#if defined(_WIN32)
    have_arm_aes = ::IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
    have_arm_neon = ::IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE);
#endif // _WIN32

#if defined(ENABLE_ARM_AES)
    if (have_arm_aes) {
        echo_round = sapphire::arm_crypto_echo::FullStateRound;
        shavite_c512 = sapphire::arm_crypto_shavite::Compress;
    }
#endif // ENABLE_ARM_AES

#if defined (ENABLE_ARM_NEON)
    if (have_arm_neon) {
        echo_shift_mix = sapphire::arm_neon_echo::ShiftAndMix;
    }
#endif // ENABLE_ARM_NEON
#endif // ENABLE_ARM_AES || ENABLE_ARM_NEON
#endif // !DISABLE_OPTIMIZED_SHA256
}
