using System;
using System.Collections.ObjectModel;
using System.Net;
namespace MediaBrowser.Common.Net
{
    /// 
    /// Defines the .
    /// 
    public static class NetworkExtensions
    {
        /// 
        /// Add an address to the collection.
        /// 
        /// The .
        /// Item to add.
        public static void AddItem(this Collection source, IPAddress ip)
        {
            if (!source.ContainsAddress(ip))
            {
                source.Add(new IPNetAddress(ip, 32));
            }
        }
        /// 
        /// Adds a network to the collection.
        /// 
        /// The .
        /// Item to add.
        /// If true the values are treated as subnets.
        /// If false items are addresses.
        public static void AddItem(this Collection source, IPObject item, bool itemsAreNetworks = true)
        {
            if (!source.ContainsAddress(item) || !itemsAreNetworks)
            {
                source.Add(item);
            }
        }
        /// 
        /// Converts this object to a string.
        /// 
        /// The .
        /// Returns a string representation of this object.
        public static string AsString(this Collection source)
        {
            return $"[{string.Join(',', source)}]";
        }
        /// 
        /// Returns true if the collection contains an item with the ip address,
        /// or the ip address falls within any of the collection's network ranges.
        /// 
        /// The .
        /// The item to look for.
        /// True if the collection contains the item.
        public static bool ContainsAddress(this Collection source, IPAddress item)
        {
            if (source.Count == 0)
            {
                return false;
            }
            if (item == null)
            {
                throw new ArgumentNullException(nameof(item));
            }
            if (item.IsIPv4MappedToIPv6)
            {
                item = item.MapToIPv4();
            }
            foreach (var i in source)
            {
                if (i.Contains(item))
                {
                    return true;
                }
            }
            return false;
        }
        /// 
        /// Returns true if the collection contains an item with the ip address,
        /// or the ip address falls within any of the collection's network ranges.
        /// 
        /// The .
        /// The item to look for.
        /// True if the collection contains the item.
        public static bool ContainsAddress(this Collection source, IPObject item)
        {
            if (source.Count == 0)
            {
                return false;
            }
            if (item == null)
            {
                throw new ArgumentNullException(nameof(item));
            }
            foreach (var i in source)
            {
                if (i.Contains(item))
                {
                    return true;
                }
            }
            return false;
        }
        /// 
        /// Compares two Collection{IPObject} objects. The order is ignored.
        /// 
        /// The .
        /// Item to compare to.
        /// True if both are equal.
        public static bool Compare(this Collection source, Collection dest)
        {
            if (dest == null || source.Count != dest.Count)
            {
                return false;
            }
            foreach (var sourceItem in source)
            {
                bool found = false;
                foreach (var destItem in dest)
                {
                    if (sourceItem.Equals(destItem))
                    {
                        found = true;
                        break;
                    }
                }
                if (!found)
                {
                    return false;
                }
            }
            return true;
        }
        /// 
        /// Returns a collection containing the subnets of this collection given.
        /// 
        /// The .
        /// Collection{IPObject} object containing the subnets.
        public static Collection AsNetworks(this Collection source)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            Collection res = new Collection();
            foreach (IPObject i in source)
            {
                if (i is IPNetAddress nw)
                {
                    // Add the subnet calculated from the interface address/mask.
                    var na = nw.NetworkAddress;
                    na.Tag = i.Tag;
                    res.AddItem(na);
                }
                else if (i is IPHost ipHost)
                {
                    // Flatten out IPHost and add all its ip addresses.
                    foreach (var addr in ipHost.GetAddresses())
                    {
                        IPNetAddress host = new IPNetAddress(addr)
                        {
                            Tag = i.Tag
                        };
                        res.AddItem(host);
                    }
                }
            }
            return res;
        }
        /// 
        /// Excludes all the items from this list that are found in excludeList.
        /// 
        /// The .
        /// Items to exclude.
        /// Collection is a network collection.
        /// A new collection, with the items excluded.
        public static Collection Exclude(this Collection source, Collection excludeList, bool isNetwork)
        {
            if (source.Count == 0 || excludeList == null)
            {
                return new Collection(source);
            }
            Collection results = new Collection();
            bool found;
            foreach (var outer in source)
            {
                found = false;
                foreach (var inner in excludeList)
                {
                    if (outer.Equals(inner))
                    {
                        found = true;
                        break;
                    }
                }
                if (!found)
                {
                    results.AddItem(outer, isNetwork);
                }
            }
            return results;
        }
        /// 
        /// Returns all items that co-exist in this object and target.
        /// 
        /// The .
        /// Collection to compare with.
        /// A collection containing all the matches.
        public static Collection ThatAreContainedInNetworks(this Collection source, Collection target)
        {
            if (source.Count == 0)
            {
                return new Collection();
            }
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }
            Collection nc = new Collection();
            foreach (IPObject i in source)
            {
                if (target.ContainsAddress(i))
                {
                    nc.AddItem(i);
                }
            }
            return nc;
        }
    }
}