using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using NuGet.Frameworks;
using NuGet.Packaging;
using NuGet.Packaging.Core;
using NuGet.Versioning;

namespace NuGet.Protocol
{
    /// <summary>
    /// Represents a V2 package entry from the OData feed. This object primarily just holds the strings parsed from XML, all parsing 
    /// and converting should be done after based on the scenario.
    /// </summary>
    public class V2FeedPackageInfo : PackageIdentity
    {
        private readonly string _title;
        private readonly string _summary;
        private readonly string[] _authors;
        private readonly string _description;
        private readonly string[] _owners;
        private readonly string _iconUrl;
        private readonly string _licenseUrl;
        private readonly string _projectUrl;
        private readonly string _reportAbuseUrl;
        private readonly string _tags;
        private readonly string _downloadCount;
        private readonly bool _requireLicenseAcceptance;
        private readonly DateTimeOffset? _published;
        private readonly string _dependencies;
        private readonly string _downloadUrl;
        private readonly string _packageHash;
        private readonly string _packageHashAlgorithm;
        private readonly NuGetVersion _minClientVersion;
        private const string NullString = "null";

        public V2FeedPackageInfo(PackageIdentity identity, string title, string summary, string description, IEnumerable<string> authors, IEnumerable<string> owners,
            string iconUrl, string licenseUrl, string projectUrl, string reportAbuseUrl,
            string tags, DateTimeOffset? published, string dependencies, bool requireLicenseAccept, string downloadUrl, string downloadCount,
            string packageHash, string packageHashAlgorithm, NuGetVersion minClientVersion)
            : base(identity.Id, identity.Version)
        {
            _summary = summary;
            _description = description;
            _authors = authors == null ? new string[0] : authors.ToArray();
            _owners = owners == null ? new string[0] : owners.ToArray();
            _iconUrl = iconUrl;
            _licenseUrl = licenseUrl;
            _projectUrl = projectUrl;
            _reportAbuseUrl = reportAbuseUrl;
            _description = description;
            _summary = summary;
            _tags = tags;
            _dependencies = dependencies;
            _requireLicenseAcceptance = requireLicenseAccept;
            _title = title;
            _downloadUrl = downloadUrl;
            _downloadCount = downloadCount;
            _published = published;
            _packageHash = packageHash;
            _packageHashAlgorithm = packageHashAlgorithm;
            _minClientVersion = minClientVersion;
        }

        public string Title
        {
            get
            {
                return _title;
            }
        }

        public string Summary
        {
            get
            {
                return _summary;
            }
        }

        public string Description
        {
            get
            {
                return _description;
            }
        }

        public IEnumerable<string> Authors
        {
            get
            {
                return _authors;
            }
        }

        public IEnumerable<string> Owners
        {
            get
            {
                return _owners;
            }
        }

        public string IconUrl
        {
            get
            {
                return _iconUrl;
            }
        }

        public string LicenseUrl
        {
            get
            {
                return _licenseUrl;
            }
        }

        public string ProjectUrl
        {
            get
            {
                return _projectUrl;
            }
        }

        public string DownloadUrl
        {
            get
            {
                return _downloadUrl;
            }
        }

        public string ReportAbuseUrl
        {
            get
            {
                return _reportAbuseUrl;
            }
        }

        public string Tags
        {
            get
            {
                return _tags;
            }
        }

        public string DownloadCount
        {
            get
            {
                return _downloadCount;
            }
        }

        /// <summary>
        /// Parse DownloadCount into an integer
        /// </summary>
        public int DownloadCountAsInt
        {
            get
            {
                int x = 0;
                Int32.TryParse(_downloadCount, out x);
                return x;
            }
        }

        public DateTimeOffset? Published
        {
            get
            {
                return _published;
            }
        }

        /// <summary>
        /// Checks the published date
        /// </summary>
        public bool IsListed
        {
            get
            {
                return !Published.HasValue || Published.Value.Year > 1901;
            }
        }

        public string Dependencies
        {
            get
            {
                return _dependencies;
            }
        }

        /// <summary>
        /// Parses Dependencies into actual groups
        /// </summary>
        public IReadOnlyList<PackageDependencyGroup> DependencySets
        {
            get
            {
                // Ex: Microsoft.Data.OData:5.6.3:aspnetcore50|Microsoft.Data.Services.Client:5.6.3:aspnetcore50|System.Spatial:5.6.3:aspnetcore50|System.Collections:4.0.10-beta-22231:aspnetcore50|System.Collections.Concurrent:4.0.0-beta-22231:aspnetcore50|System.Collections.Specialized:4.0.0-beta-22231:aspnetcore50|System.Diagnostics.Debug:4.0.10-beta-22231:aspnetcore50|System.Diagnostics.Tools:4.0.0-beta-22231:aspnetcore50|System.Diagnostics.TraceSource:4.0.0-beta-22231:aspnetcore50|System.Diagnostics.Tracing:4.0.10-beta-22231:aspnetcore50|System.Dynamic.Runtime:4.0.0-beta-22231:aspnetcore50|System.Globalization:4.0.10-beta-22231:aspnetcore50|System.IO:4.0.10-beta-22231:aspnetcore50|System.IO.FileSystem:4.0.0-beta-22231:aspnetcore50|System.IO.FileSystem.Primitives:4.0.0-beta-22231:aspnetcore50|System.Linq:4.0.0-beta-22231:aspnetcore50|System.Linq.Expressions:4.0.0-beta-22231:aspnetcore50|System.Linq.Queryable:4.0.0-beta-22231:aspnetcore50|System.Net.Http:4.0.0-beta-22231:aspnetcore50|System.Net.Primitives:4.0.10-beta-22231:aspnetcore50|System.Reflection:4.0.10-beta-22231:aspnetcore50|System.Reflection.Extensions:4.0.0-beta-22231:aspnetcore50|System.Reflection.TypeExtensions:4.0.0-beta-22231:aspnetcore50|System.Runtime:4.0.20-beta-22231:aspnetcore50|System.Runtime.Extensions:4.0.10-beta-22231:aspnetcore50|System.Runtime.InteropServices:4.0.20-beta-22231:aspnetcore50|System.Runtime.Serialization.Primitives:4.0.0-beta-22231:aspnetcore50|System.Runtime.Serialization.Xml:4.0.10-beta-22231:aspnetcore50|System.Security.Cryptography.Encoding:4.0.0-beta-22231:aspnetcore50|System.Security.Cryptography.Encryption:4.0.0-beta-22231:aspnetcore50|System.Security.Cryptography.Hashing:4.0.0-beta-22231:aspnetcore50|System.Security.Cryptography.Hashing.Algorithms:4.0.0-beta-22231:aspnetcore50|System.Text.Encoding:4.0.10-beta-22231:aspnetcore50|System.Text.Encoding.Extensions:4.0.10-beta-22231:aspnetcore50|System.Text.RegularExpressions:4.0.10-beta-22231:aspnetcore50|System.Threading:4.0.0-beta-22231:aspnetcore50|System.Threading.Tasks:4.0.10-beta-22231:aspnetcore50|System.Threading.Thread:4.0.0-beta-22231:aspnetcore50|System.Threading.ThreadPool:4.0.10-beta-22231:aspnetcore50|System.Threading.Timer:4.0.0-beta-22231:aspnetcore50|System.Xml.ReaderWriter:4.0.10-beta-22231:aspnetcore50|System.Xml.XDocument:4.0.0-beta-22231:aspnetcore50|System.Xml.XmlSerializer:4.0.0-beta-22231:aspnetcore50|Microsoft.Data.OData:5.6.3:aspnet50|Microsoft.Data.Services.Client:5.6.3:aspnet50|System.Spatial:5.6.3:aspnet50|Microsoft.Data.OData:5.6.2:net40-Client|Newtonsoft.Json:5.0.8:net40-Client|Microsoft.Data.Services.Client:5.6.2:net40-Client|Microsoft.WindowsAzure.ConfigurationManager:1.8.0.0:net40-Client|Microsoft.Data.OData:5.6.2:win80|Microsoft.Data.OData:5.6.2:wpa|Microsoft.Data.OData:5.6.2:wp80|Newtonsoft.Json:5.0.8:wp80

                if (string.IsNullOrEmpty(Dependencies))
                {
                    return new List<PackageDependencyGroup>();
                }
                else
                {
                    var results = new Dictionary<NuGetFramework, List<PackageDependency>>(new NuGetFrameworkFullComparer());

                    foreach (var set in Dependencies.Split('|'))
                    {
                        var parts = set.Trim().Split(new[] { ':' }, StringSplitOptions.None);

                        if (parts.Length != 0)
                        {
                            // Defaults
                            var dependencyId = parts[0];
                            var versionRange = VersionRange.All;
                            var framework = NuGetFramework.AnyFramework;

                            if (parts.Length > 1)
                            {
                                var versionRangeString = parts[1];

                                // Nexus will write "null" when there is no depenency version range.
                                // Parse the optional version range
                                if (!string.IsNullOrWhiteSpace(versionRangeString) 
                                    && !string.Equals(NullString, versionRangeString, StringComparison.OrdinalIgnoreCase))
                                {
                                    // Attempt to parse the version
                                    versionRange = VersionRange.Parse(versionRangeString);
                                }

                                // Parse the optional framework string
                                if (parts.Length > 2)
                                {
                                    var frameworkString = parts[2];

                                    if (!string.IsNullOrWhiteSpace(frameworkString)
                                        && !string.Equals(NullString, versionRangeString, StringComparison.OrdinalIgnoreCase))
                                    {
                                        framework = NuGetFramework.Parse(frameworkString);
                                    }
                                }
                            }

                            // Group dependencies by target framework
                            List<PackageDependency> deps = null;
                            if (!results.TryGetValue(framework, out deps))
                            {
                                deps = new List<PackageDependency>();
                                results.Add(framework, deps);
                            }

                            // Validate - this should never be empty
                            if (!string.IsNullOrEmpty(dependencyId))
                            {
                                // Make sure there are no duplicate dependencies, this could happen with Unsupported
                                if (!deps.Any(p => string.Equals(p.Id, dependencyId, StringComparison.OrdinalIgnoreCase)))
                                {
                                    var packageDependency = new PackageDependency(dependencyId, versionRange);

                                    deps.Add(packageDependency);
                                }
                            }
                        }
                        else
                        {
                            Debug.Fail("Unknown dependency format: " + set);
                        }
                    }

                    return results.Select(pair => new PackageDependencyGroup(pair.Key, pair.Value)).ToList();
                }
            }
        }

        public bool RequireLicenseAcceptance
        {
            get
            {
                return _requireLicenseAcceptance;
            }
        }

        public string PackageHash
        {
            get
            {
                return _packageHash;
            }
        }

        public string PackageHashAlgorithm
        {
            get
            {
                return _packageHashAlgorithm;
            }
        }

        public NuGetVersion MinClientVersion
        {
            get
            {
                return _minClientVersion;
            }
        }
    }
}