using System;
using System.Buffers.Binary;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Sockets;
namespace FastGithub.Scanner
{
    /// 
    /// 表示IP范围
    /// 
    /// 
    /// • 192.168.1.0/24
    /// • 192.168.1.1-192.168.1.254
    /// 
    abstract class IPAddressRange : IEnumerable
    {
        /// 
        /// 获取ip数量
        /// 
        public abstract int Size { get; }
        /// 
        /// 获取地址族
        /// 
        public abstract AddressFamily AddressFamily { get; }
        /// 
        /// 获取迭代器
        /// 
        /// 
        public abstract IEnumerator GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
        /// 
        /// 从多个ip范围文本解析
        /// 
        /// 
        /// 
        public static IEnumerable From(IEnumerable ranges)
        {
            foreach (var item in ranges)
            {
                if (TryParse(item, out var range))
                {
                    yield return range;
                }
            }
        }
        /// 
        /// 尝试解析
        /// 
        /// 
        /// 
        /// 
        public static bool TryParse(ReadOnlySpan range, [MaybeNullWhen(false)] out IPAddressRange value)
        {
            if (range.IsEmpty == false && IPNetwork.TryParse(range.ToString(), out var ipNetwork))
            {
                value = new CidrIPAddressRange(ipNetwork);
                return true;
            }
            var index = range.IndexOf('-');
            if (index >= 0)
            {
                var start = range.Slice(0, index);
                var end = range.Slice(index + 1);
                if (IPAddress.TryParse(start, out var startIp) &&
                   IPAddress.TryParse(end, out var endIp) &&
                   startIp.AddressFamily == endIp.AddressFamily)
                {
                    value = new SplitIPAddressRange(startIp, endIp);
                    return true;
                }
            }
            value = null;
            return false;
        }
        /// 
        /// 192.168.1.0/24
        /// 
        private class CidrIPAddressRange : IPAddressRange
        {
            private readonly IPAddressCollection addressCollection;
            private readonly AddressFamily addressFamily;
            public override int Size => (int)this.addressCollection.Count;
            public override AddressFamily AddressFamily => this.addressFamily;
            public CidrIPAddressRange(IPNetwork network)
            {
                this.addressCollection = network.ListIPAddress(FilterEnum.All);
                this.addressFamily = network.AddressFamily;
            }
            public override IEnumerator GetEnumerator()
            {
                return ((IEnumerable)this.addressCollection).GetEnumerator();
            }
        }
        /// 
        /// 192.168.1.1-192.168.1.254
        /// 
        private class SplitIPAddressRange : IPAddressRange
        {
            private readonly IPAddress start;
            private readonly IPAddress end;
            private readonly AddressFamily addressFamily;
            public override AddressFamily AddressFamily => this.addressFamily;
            public SplitIPAddressRange(IPAddress start, IPAddress end)
            {
                this.start = start;
                this.end = end;
                this.addressFamily = start.AddressFamily;
            }
            public override int Size
            {
                get
                {
                    if (this.start.AddressFamily == AddressFamily.InterNetworkV6)
                    {
                        var startValue = BinaryPrimitives.ReadInt64BigEndian(this.start.GetAddressBytes());
                        var endValue = BinaryPrimitives.ReadInt64BigEndian(this.end.GetAddressBytes());
                        return (int)(endValue - startValue) + 1;
                    }
                    else
                    {
                        var startValue = BinaryPrimitives.ReadInt32BigEndian(this.start.GetAddressBytes());
                        var endValue = BinaryPrimitives.ReadInt32BigEndian(this.end.GetAddressBytes());
                        return endValue - startValue + 1;
                    }
                }
            }
            public override IEnumerator GetEnumerator()
            {
                return this.GetIPAddresses().GetEnumerator();
            }
            private IEnumerable GetIPAddresses()
            {
                for (var i = 0; i < this.Size; i++)
                {
                    var value = i;
                    yield return Add(this.start, value);
                }
            }
            /// 
            /// 添加值
            /// 
            /// 
            /// 
            /// 
            private static IPAddress Add(IPAddress address, int value)
            {
                var span = address.GetAddressBytes().AsSpan();
                var hostValue = BinaryPrimitives.ReadInt32BigEndian(span);
                BinaryPrimitives.WriteInt32BigEndian(span, hostValue + value);
                return new IPAddress(span);
            }
        }
    }
}