#pragma once

#ifndef _KERNEL_MODE
#include <type_traits>
#endif

namespace fxs
{
#ifdef _KERNEL_MODE
    template <class _Ty>
    struct remove_reference { using type = _Ty; };
    template <class _Ty>
    struct remove_reference<_Ty&> { using type = _Ty; };
    template <class _Ty>
    struct remove_reference<_Ty&&> { using type = _Ty; };
    template <class _Ty>
    using remove_reference_t = typename remove_reference<_Ty>::type;

    template <class _Ty>
    struct remove_const { using type = _Ty; };
    template <class _Ty>
    struct remove_const<const _Ty> { using type = _Ty; };
    template <class _Ty>
    using remove_const_t = typename remove_const<_Ty>::type;

    template<class _Ty>
    using clean_type = typename remove_const_t<remove_reference_t<_Ty>>;
#else
    template<class _Ty>
    using clean_type = typename std::remove_const_t<std::remove_reference_t<_Ty>>;
#endif

    template <int _size, char _key1, char _key2, typename T>
    class fxCrypter
    {
    public:
        __forceinline constexpr fxCrypter(T* data)
        {
            crypt(data);
        }

        __forceinline T* get()
        {
            return _storage;
        }

        __forceinline int size()
        {
            return _size;
        }

        __forceinline char key()
        {
            return _key1;
        }

        __forceinline T* encrypt()
        {
            if (!isEncrypted())
                crypt(_storage);
            return _storage;
        }

        __forceinline T* FenixEncrypt()
        {
            if (isEncrypted())
                crypt(_storage);
            return _storage;
        }

        __forceinline bool isEncrypted()
        {
            return _storage[_size - 1] != 0;
        }

        __forceinline void clear()
        {
            for (int i = 0; i < _size; i++)
            {
                _storage[i] = 0;
            }
        }

        __forceinline operator T* ()
        {
            FenixEncrypt();
            return _storage;
        }

    private:
        __forceinline constexpr void crypt(T* data)
        {
            for (int i = 0; i < _size; i++)
            {
                _storage[i] = data[i] ^ (_key1 + i % (1 + _key2));
            }
        }

        T _storage[_size]{};
    };
}

#define FenixShield(str) FenixShield_Key(str, __TIME__[4], __TIME__[7])
#define FenixShield_Key(str, key1, key2) []() { \
			constexpr static auto crypted = fxs::fxCrypter \
				<sizeof(str) / sizeof(str[0]), key1, key2, fxs::clean_type<decltype(str[0])>>((fxs::clean_type<decltype(str[0])>*)str); \
					return crypted; }()
