#region License

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#endregion

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Gremlin.Net.Driver;
using Gremlin.Net.Driver.Messages;
using Gremlin.Net.Process.Traversal;
using Gremlin.Net.Structure.IO.GraphBinary4;
using static Gremlin.Net.Process.Traversal.AnonymousTraversalSource;
using static Gremlin.Net.Process.Traversal.__;
using static Gremlin.Net.Process.Traversal.P;

namespace Gremlin.Net.Benchmarks
{
    [MemoryDiagnoser]
    public class MessageSerializerBenchmarks
    {
        private static readonly GremlinLang EmptyGremlinLang = new GremlinLang();

        private static readonly GremlinLang SomeGremlinLang = BuildSomeGremlinLang();

        private static GremlinLang BuildSomeGremlinLang()
        {
            var dt = DateTimeOffset.Parse("1980-01-01 00:00:00");
            return Traversal().With(null).WithComputer().V()
                .Has("Name", "marko")
                .Where(
                    Out("knows")
                    .Has("name", Within(new List<string> {"stephen", "peter", "josh"}))
                    .Count()
                    .Is(Gt(3)))
                .Has("birthDate", Lt(dt))
                .GremlinLang;
        }

        private static readonly RequestMessage RequestMessageWithEmptyGremlinLang =
            RequestMessageFor(EmptyGremlinLang);

        private static readonly RequestMessage RequestMessage =
            RequestMessageFor(SomeGremlinLang);

        private static RequestMessage RequestMessageFor(GremlinLang gremlinLang) =>
            Gremlin.Net.Driver.Messages.RequestMessage.Build(gremlinLang.GetGremlin())
                .AddG("g")
                .AddParameters(gremlinLang.Parameters)
                .Create();

        private static readonly GraphBinary4MessageSerializer BinaryMessageSerializer = new();

        [Benchmark]
        public async Task<byte[]> TestWriteEmptyGremlinLangBinary() =>
            await BinaryMessageSerializer.SerializeMessageAsync(RequestMessageWithEmptyGremlinLang)
                .ConfigureAwait(false);

        [Benchmark]
        public async Task<byte[]> TestWriteGremlinLangBinary() =>
            await BinaryMessageSerializer.SerializeMessageAsync(RequestMessage)
                .ConfigureAwait(false);

        [Benchmark]
        public async Task<List<object>> TestReadSmallBinary()
        {
            using var stream = new MemoryStream(TestMessages.SmallBinaryResponseMessageBytes);
            var results = new List<object>();
            await foreach (var item in BinaryMessageSerializer.DeserializeMessageAsync(stream)
                .ConfigureAwait(false))
            {
                results.Add(item);
            }
            return results;
        }

        [Benchmark]
        public async Task<List<object>> TestReadBigBinary()
        {
            using var stream = new MemoryStream(TestMessages.BigBinaryResponseMessageBytes);
            var results = new List<object>();
            await foreach (var item in BinaryMessageSerializer.DeserializeMessageAsync(stream)
                .ConfigureAwait(false))
            {
                results.Add(item);
            }
            return results;
        }
    }
}
