// 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.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Thrift.Protocol;
using Thrift.Protocol.Entities;
using Thrift.Protocol.Utilities;

namespace Thrift.Tests.Protocols
{
    [TestClass]
    public class TJSONProtocolHelperTests
    {
        [TestMethod]
        public void GetTypeNameForTypeId_Test()
        {
            // input/output
            var sets = new List<Tuple<TType, byte[]>>
            {
                new(TType.Bool, TJSONProtocolConstants.TypeNames.NameBool),
                new(TType.Byte, TJSONProtocolConstants.TypeNames.NameByte),
                new(TType.I16, TJSONProtocolConstants.TypeNames.NameI16),
                new(TType.I32, TJSONProtocolConstants.TypeNames.NameI32),
                new(TType.I64, TJSONProtocolConstants.TypeNames.NameI64),
                new(TType.Double, TJSONProtocolConstants.TypeNames.NameDouble),
                new(TType.String, TJSONProtocolConstants.TypeNames.NameString),
                new(TType.Struct, TJSONProtocolConstants.TypeNames.NameStruct),
                new(TType.Map, TJSONProtocolConstants.TypeNames.NameMap),
                new(TType.Set, TJSONProtocolConstants.TypeNames.NameSet),
                new(TType.List, TJSONProtocolConstants.TypeNames.NameList),
            };

            foreach (var t in sets)
            {
                Assert.AreEqual(t.Item2, TJSONProtocolHelper.GetTypeNameForTypeId(t.Item1), $"Wrong mapping of TypeName {t.Item2} to TType: {t.Item1}");
            }
        }

        [TestMethod]
        public void GetTypeNameForTypeId_TStop_Test()
        {
            Assert.ThrowsExactly<TProtocolException>(() => TJSONProtocolHelper.GetTypeNameForTypeId(TType.Stop));
        }

        [TestMethod]
        public void GetTypeNameForTypeId_NonExistingTType_Test()
        {
            Assert.ThrowsExactly<TProtocolException>(() => TJSONProtocolHelper.GetTypeNameForTypeId((TType)100));
        }

        [TestMethod]
        public void GetTypeIdForTypeName_Test()
        {
            // input/output
            var sets = new List<Tuple<TType, byte[]>>
            {
                new(TType.Bool, TJSONProtocolConstants.TypeNames.NameBool),
                new(TType.Byte, TJSONProtocolConstants.TypeNames.NameByte),
                new(TType.I16, TJSONProtocolConstants.TypeNames.NameI16),
                new(TType.I32, TJSONProtocolConstants.TypeNames.NameI32),
                new(TType.I64, TJSONProtocolConstants.TypeNames.NameI64),
                new(TType.Double, TJSONProtocolConstants.TypeNames.NameDouble),
                new(TType.String, TJSONProtocolConstants.TypeNames.NameString),
                new(TType.Struct, TJSONProtocolConstants.TypeNames.NameStruct),
                new(TType.Map, TJSONProtocolConstants.TypeNames.NameMap),
                new(TType.Set, TJSONProtocolConstants.TypeNames.NameSet),
                new(TType.List, TJSONProtocolConstants.TypeNames.NameList),
            };

            foreach (var t in sets)
            {
                Assert.AreEqual(t.Item1, TJSONProtocolHelper.GetTypeIdForTypeName(t.Item2), $"Wrong mapping of TypeName {t.Item2} to TType: {t.Item1}");
            }
        }

        [TestMethod]
        public void GetTypeIdForTypeName_TStopTypeName_Test()
        {
            Assert.ThrowsExactly<TProtocolException>(() => TJSONProtocolHelper.GetTypeIdForTypeName([(byte)TType.Stop, (byte)TType.Stop]));
        }

        [TestMethod]
        public void GetTypeIdForTypeName_NonExistingTypeName_Test()
        {
            Assert.ThrowsExactly<TProtocolException>(() => TJSONProtocolHelper.GetTypeIdForTypeName([100]));
        }

        [TestMethod]
        public void GetTypeIdForTypeName_EmptyName_Test()
        {
            Assert.ThrowsExactly<TProtocolException>(() => TJSONProtocolHelper.GetTypeIdForTypeName([]));
        }

        [TestMethod]
        public void IsJsonNumeric_Test()
        {
            // input/output
            var correctJsonNumeric = "+-.0123456789Ee";
            var incorrectJsonNumeric = "AaBcDd/*\\";

            var sets = correctJsonNumeric.Select(ch => new Tuple<byte, bool>((byte) ch, true)).ToList();
            sets.AddRange(incorrectJsonNumeric.Select(ch => new Tuple<byte, bool>((byte) ch, false)));

            foreach (var t in sets)
            {
                Assert.AreEqual(t.Item2, TJSONProtocolHelper.IsJsonNumeric(t.Item1), $"Wrong mapping of Char {t.Item1} to bool: {t.Item2}");
            }
        }

        [TestMethod]
        public void ToHexVal_Test()
        {
            // input/output
            var chars = "0123456789abcdef";
            var expectedHexValues = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};

            var sets = chars.Select((ch, i) => new Tuple<char, byte>(ch, expectedHexValues[i])).ToList();

            foreach (var t in sets)
            {
                var actualResult = TJSONProtocolHelper.ToHexVal((byte)t.Item1);
                Assert.AreEqual(t.Item2, actualResult, $"Wrong mapping of char byte {t.Item1} to it expected hex value: {t.Item2}. Actual hex value: {actualResult}");
            }
        }

        [TestMethod]
        public void ToHexVal_WrongInputChar_Test()
        {
            Assert.ThrowsExactly<TProtocolException>(() => TJSONProtocolHelper.ToHexVal((byte)'s'));
        }

        [TestMethod]
        public void ToHexChar_Test()
        {
            // input/output
            var hexValues = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
            var expectedChars = "0123456789abcdef";
            

            var sets = hexValues.Select((hv, i) => new Tuple<byte, char>(hv, expectedChars[i])).ToList();

            foreach (var t in sets)
            {
                var actualResult = (char)TJSONProtocolHelper.ToHexChar(t.Item1);
                Assert.AreEqual(t.Item2, actualResult, $"Wrong mapping of hex value {t.Item1} to it expected char: {t.Item2}. Actual hex value: {actualResult}");
            }
        }
    }
}
