// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;

namespace Microsoft.AspNetCore.Routing.Matching
{
    public class JumpTableSingleEntryBenchmark
    {
        private JumpTable _default;
        private JumpTable _trie;
        private JumpTable _vectorTrie;
        private JumpTable _ascii;

        private string[] _strings;
        private PathSegment[] _segments;

        [GlobalSetup]
        public void Setup()
        {
            _default = new SingleEntryJumpTable(0, -1, "hello-world", 1);
            _trie = new ILEmitTrieJumpTable(0, -1, new[] { ("hello-world", 1), }, vectorize: false, _default);
            _vectorTrie = new ILEmitTrieJumpTable(0, -1, new[] { ("hello-world", 1), }, vectorize: true, _default);
            _ascii = new SingleEntryAsciiJumpTable(0, -1, "hello-world", 1);

            _strings = new string[]
            {
                "index/foo/2",
                "index/hello-world1/2",
                "index/hello-world/2",
                "index//2",
                "index/hillo-goodbye/2",
            };
            _segments = new PathSegment[]
            {
                new PathSegment(6, 3),
                new PathSegment(6, 12),
                new PathSegment(6, 11),
                new PathSegment(6, 0),
                new PathSegment(6, 13),
            };
        }

        [Benchmark(Baseline = true, OperationsPerInvoke = 5)]
        public int Baseline()
        {
            var strings = _strings;
            var segments = _segments;

            int destination = 0;
            for (var i = 0; i < strings.Length; i++)
            {
                var @string = strings[i];
                var segment = segments[i];

                if (segment.Length == 0)
                {
                    destination = -1;
                }
                else if (segment.Length != "hello-world".Length)
                {
                    destination = 1;
                }
                else
                {
                    destination = string.Compare(
                        @string,
                        segment.Start,
                        "hello-world",
                        0,
                        segment.Length,
                        StringComparison.OrdinalIgnoreCase);
                }
            }

            return destination;
        }

        [Benchmark(OperationsPerInvoke = 5)]
        public int Default()
        {
            var strings = _strings;
            var segments = _segments;

            var destination = 0;
            for (var i = 0; i < strings.Length; i++)
            {
                destination = _default.GetDestination(strings[i], segments[i]);
            }

            return destination;
        }

        [Benchmark(OperationsPerInvoke = 5)]
        public int Ascii()
        {
            var strings = _strings;
            var segments = _segments;

            var destination = 0;
            for (var i = 0; i < strings.Length; i++)
            {
                destination = _ascii.GetDestination(strings[i], segments[i]);
            }

            return destination;
        }

        [Benchmark(OperationsPerInvoke = 5)]
        public int Trie()
        {
            var strings = _strings;
            var segments = _segments;

            var destination = 0;
            for (var i = 0; i < strings.Length; i++)
            {
                destination = _trie.GetDestination(strings[i], segments[i]);
            }

            return destination;
        }

        [Benchmark(OperationsPerInvoke = 5)]
        public int VectorTrie()
        {
            var strings = _strings;
            var segments = _segments;

            var destination = 0;
            for (var i = 0; i < strings.Length; i++)
            {
                destination = _vectorTrie.GetDestination(strings[i], segments[i]);
            }

            return destination;
        }
    }
}
