// Copyright (C) 2026 Kiyotsugu Arai
// SPDX-License-Identifier: LGPL-3.0-or-later

// endian_utils.hpp
// エンディアン変換ユーティリティ
//
// このファイルでは、異なるエンディアンの環境間でのデータ交換を
// サポートするためのユーティリティ関数を提供します。
//
// 主な機能:
// - エンディアン検出
// - バイトスワップ関数
// - プラットフォーム間の変換ヘルパー

#ifndef CALX_ENDIAN_UTILS_HPP
#define CALX_ENDIAN_UTILS_HPP

#include <cstdint>
#include <cstring>
#include <type_traits>

// コンパイラ依存のエンディアン検出
#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__)
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define CALX_LITTLE_ENDIAN 1
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define CALX_BIG_ENDIAN 1
#else
#error "Unknown endianness"
#endif
#elif defined(_MSC_VER) // Microsoft Visual C++の場合は常にリトルエンディアン
#define CALX_LITTLE_ENDIAN 1
#else
  // 実行時検出フォールバック
#warning "Compile-time endianness detection not available, using runtime detection"
#define CALX_RUNTIME_ENDIAN_DETECTION 1
#endif

namespace calx {
    namespace endian {

        /**
         * @brief エンディアン型の列挙型
         */
        enum class Endianness {
            Little,  ///< リトルエンディアン（下位バイトが先）
            Big      ///< ビッグエンディアン（上位バイトが先）
        };

        /**
         * @brief 現在のプラットフォームのエンディアン形式
         */
#if defined(CALX_LITTLE_ENDIAN)
        constexpr Endianness platform_endianness = Endianness::Little;
#elif defined(CALX_BIG_ENDIAN)
        constexpr Endianness platform_endianness = Endianness::Big;
#else
         // 実行時検出が必要
        inline Endianness detect_platform_endianness() {
            const uint16_t test = 0x0102;
            const uint8_t* ptr = reinterpret_cast<const uint8_t*>(&test);
            return (ptr[0] == 0x02) ? Endianness::Little : Endianness::Big;
        }
        const Endianness platform_endianness = detect_platform_endianness();
#endif

        /**
         * @brief 16ビット整数のバイト順を反転
         * @param value 反転する値
         * @return バイト順を反転した値
         */
        inline uint16_t byte_swap(uint16_t value) {
            return ((value & 0x00FF) << 8) | ((value & 0xFF00) >> 8);
        }

        /**
         * @brief 32ビット整数のバイト順を反転
         * @param value 反転する値
         * @return バイト順を反転した値
         */
        inline uint32_t byte_swap(uint32_t value) {
            return ((value & 0x000000FF) << 24) |
                ((value & 0x0000FF00) << 8) |
                ((value & 0x00FF0000) >> 8) |
                ((value & 0xFF000000) >> 24);
        }

        /**
         * @brief 64ビット整数のバイト順を反転
         * @param value 反転する値
         * @return バイト順を反転した値
         */
        inline uint64_t byte_swap(uint64_t value) {
            return ((value & 0x00000000000000FFULL) << 56) |
                ((value & 0x000000000000FF00ULL) << 40) |
                ((value & 0x0000000000FF0000ULL) << 24) |
                ((value & 0x00000000FF000000ULL) << 8) |
                ((value & 0x000000FF00000000ULL) >> 8) |
                ((value & 0x0000FF0000000000ULL) >> 24) |
                ((value & 0x00FF000000000000ULL) >> 40) |
                ((value & 0xFF00000000000000ULL) >> 56);
        }

        /**
         * @brief 符号付き整数のバイト順を反転（符号なし版を利用）
         * @param value 反転する値
         * @return バイト順を反転した値
         */
        template<typename T>
            requires std::is_signed_v<T>
        inline T byte_swap(T value) {
            using UT = typename std::make_unsigned<T>::type;
            return static_cast<T>(byte_swap(static_cast<UT>(value)));
        }

        /**
         * @brief 特定のエンディアンから現在のプラットフォームのエンディアンに変換
         * @param value 変換する値
         * @param from 元のエンディアン形式
         * @return 変換後の値
         */
        template<typename T>
        inline T to_native(T value, Endianness from) {
            if (from == platform_endianness) {
                return value;
            }
            return byte_swap(value);
        }

        /**
         * @brief 現在のプラットフォームのエンディアンから特定のエンディアンに変換
         * @param value 変換する値
         * @param to 変換先のエンディアン形式
         * @return 変換後の値
         */
        template<typename T>
        inline T from_native(T value, Endianness to) {
            if (to == platform_endianness) {
                return value;
            }
            return byte_swap(value);
        }

        /**
         * @brief リトルエンディアンから現在のプラットフォームのエンディアンに変換
         * @param value 変換する値
         * @return 変換後の値
         */
        template<typename T>
        inline T from_little_endian(T value) {
            return to_native(value, Endianness::Little);
        }

        /**
         * @brief ビッグエンディアンから現在のプラットフォームのエンディアンに変換
         * @param value 変換する値
         * @return 変換後の値
         */
        template<typename T>
        inline T from_big_endian(T value) {
            return to_native(value, Endianness::Big);
        }

        /**
         * @brief 現在のプラットフォームのエンディアンからリトルエンディアンに変換
         * @param value 変換する値
         * @return 変換後の値
         */
        template<typename T>
        inline T to_little_endian(T value) {
            return from_native(value, Endianness::Little);
        }

        /**
         * @brief 現在のプラットフォームのエンディアンからビッグエンディアンに変換
         * @param value 変換する値
         * @return 変換後の値
         */
        template<typename T>
        inline T to_big_endian(T value) {
            return from_native(value, Endianness::Big);
        }

        /**
         * @brief バッファのバイト順を反転
         * @param buffer 反転するバッファ
         * @param size バッファのサイズ（バイト単位）
         * @param element_size 各要素のサイズ（バイト単位）
         */
        inline void byte_swap_buffer(void* buffer, size_t size, size_t element_size) {
            if (buffer == nullptr || size == 0 || element_size <= 1) {
                return;
            }

            uint8_t* bytes = static_cast<uint8_t*>(buffer);

            for (size_t i = 0; i < size; i += element_size) {
                for (size_t j = 0; j < element_size / 2; ++j) {
                    std::swap(bytes[i + j], bytes[i + element_size - 1 - j]);
                }
            }
        }

    } // namespace endian
} // namespace calx

#endif // CALX_ENDIAN_UTILS_HPP