using CryptoExchange.Net.Converters.MessageParsing;
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Objects;
using ProtoBuf;
using ProtoBuf.Meta;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace CryptoExchange.Net.Converters.Protobuf
{
    /// 
    /// System.Text.Json message accessor
    /// 
#if NET5_0_OR_GREATER
    public abstract class ProtobufMessageAccessor<
        [DynamicallyAccessedMembers(
#if NET8_0_OR_GREATER
        DynamicallyAccessedMemberTypes.NonPublicConstructors |
        DynamicallyAccessedMemberTypes.PublicFields |
        DynamicallyAccessedMemberTypes.NonPublicFields |
        DynamicallyAccessedMemberTypes.NonPublicNestedTypes |
        DynamicallyAccessedMemberTypes.PublicProperties |
        DynamicallyAccessedMemberTypes.NonPublicProperties |
#endif
        DynamicallyAccessedMemberTypes.PublicNestedTypes |
        DynamicallyAccessedMemberTypes.NonPublicMethods |
        DynamicallyAccessedMemberTypes.PublicMethods
        )]
    TIntermediateType> : IMessageAccessor
#else
    public abstract class ProtobufMessageAccessor : IMessageAccessor
#endif
    {
        /// 
        /// The intermediate deserialization object
        /// 
        protected TIntermediateType? _intermediateType;
        /// 
        /// Runtime type model
        /// 
        protected RuntimeTypeModel _model;
        /// 
        public bool IsValid { get; set; }
        /// 
        public abstract bool OriginalDataAvailable { get; }
        /// 
        public object? Underlying => _intermediateType;
        /// 
        /// ctor
        /// 
        public ProtobufMessageAccessor(RuntimeTypeModel model)
        {
            _model = model;
        }
        /// 
        public NodeType? GetNodeType()
        {
            throw new NotImplementedException();
        }
        /// 
        public NodeType? GetNodeType(MessagePath path)
        {
            if (_intermediateType == null)
                throw new InvalidOperationException("Data not read");
            object? value = _intermediateType;
            foreach (var step in path)
            {
                if (value == null)
                    break;
                if (step.Type == 0)
                {
                    // array index
                }
                else if (step.Type == 1)
                {
                    // property value
#pragma warning disable IL2075 // Type is already annotated
                    value = value.GetType().GetProperty(step.Property!)?.GetValue(value);
#pragma warning restore
                }
                else
                {
                    // property name
                }
            }
            if (value == null)
                return null;
            var valueType = value.GetType();
            if (valueType.IsArray)
                return NodeType.Array;
            if (IsSimple(valueType))
                return NodeType.Value;
            return NodeType.Object;
        }
        private static bool IsSimple(Type type)
        {
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                // nullable type, check if the nested type is simple.
                return IsSimple(type.GetGenericArguments()[0]);
            }
            return type.IsPrimitive
              || type.IsEnum
              || type == typeof(string)
              || type == typeof(decimal);
        }
        /// 
#if NET5_0_OR_GREATER
        [UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2075:RequiresUnreferencedCode", Justification = "JsonSerializerOptions provided here has TypeInfoResolver set")]
#endif
        public T? GetValue(MessagePath path)
        {
            if (_intermediateType == null)
                throw new InvalidOperationException("Data not read");
            object? value = _intermediateType;
            foreach(var step in path)
            {
                if (value == null)
                    break;
                if (step.Type == 0)
                {
                    // array index
                }
                else if (step.Type == 1)
                {
                    // property value
#pragma warning disable IL2075 // Type is already annotated
                    value = value.GetType().GetProperty(step.Property!)?.GetValue(value);
#pragma warning restore
                }
                else
                {
                    // property name
                }
            }
            return (T?)value;
        }
        /// 
        public T?[]? GetValues(MessagePath path)
        {
            throw new NotImplementedException();
        }
        /// 
        public abstract string GetOriginalString();
        /// 
        public abstract void Clear();
        /// 
#if NET5_0_OR_GREATER
        [UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2092:RequiresUnreferencedCode", Justification = "JsonSerializerOptions provided here has TypeInfoResolver set")]
        [UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2095:RequiresUnreferencedCode", Justification = "JsonSerializerOptions provided here has TypeInfoResolver set")]
#endif
        public abstract CallResult