// ReSharper disable InconsistentNaming

namespace Standart.Hash.xxHash
{
    using System.Runtime.CompilerServices;

    public static partial class xxHash64
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static unsafe ulong XXH64(byte* input, int len, ulong seed)
        {
            ulong h64;

            if (len >= 32)
            {
                byte* end = input + len;
                byte* limit = end - 31;

                ulong v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2;
                ulong v2 = seed + XXH_PRIME64_2;
                ulong v3 = seed + 0;
                ulong v4 = seed - XXH_PRIME64_1;

                do
                {
                    v1 = XXH64_round(v1, *(ulong*) input); input += 8;
                    v2 = XXH64_round(v2, *(ulong*) input); input += 8;
                    v3 = XXH64_round(v3, *(ulong*) input); input += 8;
                    v4 = XXH64_round(v4, *(ulong*) input); input += 8;
                } while (input < limit);

                h64 = XXH_rotl64(v1, 1) +
                      XXH_rotl64(v2, 7) +
                      XXH_rotl64(v3, 12) +
                      XXH_rotl64(v4, 18);  
                
                h64 = XXH64_mergeRound(h64, v1);
                h64 = XXH64_mergeRound(h64, v2);
                h64 = XXH64_mergeRound(h64, v3);
                h64 = XXH64_mergeRound(h64, v4);
            }
            else
            {
                h64 = seed + XXH_PRIME64_5;
            }

            h64 += (ulong)len;
            
            return XXH64_finalize(h64, input, len);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static ulong XXH64_round(ulong acc, ulong input)
        {
            acc += input * XXH_PRIME64_2;
            acc  = XXH_rotl64(acc, 31);
            acc *= XXH_PRIME64_1;
            return acc;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static ulong XXH64_mergeRound(ulong acc, ulong val)
        {
            val  = XXH64_round(0, val);
            acc ^= val;
            acc  = acc * XXH_PRIME64_1 + XXH_PRIME64_4;
            return acc;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static ulong XXH64_avalanche(ulong hash)
        {
            hash ^= hash >> 33;
            hash *= XXH_PRIME64_2;
            hash ^= hash >> 29;
            hash *= XXH_PRIME64_3;
            hash ^= hash >> 32;
            return hash;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static unsafe ulong XXH64_finalize(ulong hash, byte* ptr, int len)
        {
            len &= 31;
            while (len >= 8) {
                ulong k1 = XXH64_round(0, *(ulong*)ptr);
                ptr += 8;
                hash ^= k1;
                hash  = XXH_rotl64(hash,27) * XXH_PRIME64_1 + XXH_PRIME64_4;
                len -= 8;
            }
            if (len >= 4) {
                hash ^= *(uint*)ptr * XXH_PRIME64_1;
                ptr += 4;
                hash = XXH_rotl64(hash, 23) * XXH_PRIME64_2 + XXH_PRIME64_3;
                len -= 4;
            }
            while (len > 0) {
                hash ^= (*ptr++) * XXH_PRIME64_5;
                hash = XXH_rotl64(hash, 11) * XXH_PRIME64_1;
                --len;
            }
            return  XXH64_avalanche(hash);
        }
    }
}