// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.ComponentModel.Design;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;

namespace System.ComponentModel
{
    /// <summary>
    /// Provides an implementation of a <see cref='System.ComponentModel.LicenseProvider'/>. The provider works in
    /// a similar fashion to Microsoft .NET Framework standard licensing module.
    /// </summary>
    public class LicFileLicenseProvider : LicenseProvider
    {
        /// <summary>
        /// Determines if the key retrieved by the <see cref='System.ComponentModel.LicFileLicenseProvider.GetLicense'/> method is valid
        /// for the specified type.
        /// </summary>
        protected virtual bool IsKeyValid(string? key, Type type)
        {
            if (key != null)
            {
                return key.StartsWith(GetKey(type));
            }
            return false;
        }


        /// <summary>
        /// Creates a key for the specified type.
        /// </summary>
        protected virtual string GetKey(Type type)
        {
            // This string should not be localized.
            return type.FullName + " is a licensed component.";
        }

        /// <summary>
        /// Gets a license for the instance of the component and determines if it is valid.
        /// </summary>
        [UnconditionalSuppressMessage("SingleFile", "IL3002:RequiresAssemblyFiles",
            Justification = "Only used for when Location is non-empty")]
        [UnconditionalSuppressMessage("SingleFile", "IL3000:RequiresAssemblyFiles",
            Justification = "Location is checked for empty")]
        public override License? GetLicense(LicenseContext context, Type type, object? instance, bool allowExceptions)
        {
            LicFileLicense? lic = null;

            Debug.Assert(context != null, "No context provided!");
            if (context != null)
            {
                if (context.UsageMode == LicenseUsageMode.Runtime)
                {
                    string? key = context.GetSavedLicenseKey(type, null);
                    if (key != null && IsKeyValid(key, type))
                    {
                        lic = new LicFileLicense(this, key);
                    }
                }

                if (lic == null)
                {
                    string? modulePath = null;

                    if (context != null)
                    {
                        ITypeResolutionService? resolver = (ITypeResolutionService?)context.GetService(typeof(ITypeResolutionService));
                        if (resolver != null)
                        {
                            modulePath = resolver.GetPathOfAssembly(type.Assembly.GetName());
                        }
                    }

                    if (type.Assembly.Location.Length != 0)
                    {
                        modulePath ??= type.Module.FullyQualifiedName;

                        string? moduleDir = Path.GetDirectoryName(modulePath);
                        string licenseFile = moduleDir + "\\" + type.FullName + ".lic";

                        Debug.WriteLine($"Looking for license in: {licenseFile}");
                        if (File.Exists(licenseFile))
                        {
                            Stream licStream = new FileStream(licenseFile, FileMode.Open, FileAccess.Read, FileShare.Read);
                            StreamReader sr = new StreamReader(licStream);
                            string? s = sr.ReadLine();
                            sr.Close();
                            if (IsKeyValid(s, type))
                            {
                                lic = new LicFileLicense(this, GetKey(type));
                            }
                        }

                        if (lic != null)
                        {
                            context!.SetSavedLicenseKey(type, lic.LicenseKey);
                        }
                    }
                }
            }
            return lic;
        }

        private sealed class LicFileLicense : License
        {
            private readonly LicFileLicenseProvider _owner;

            public LicFileLicense(LicFileLicenseProvider owner, string key)
            {
                _owner = owner;
                LicenseKey = key;
            }
            public override string LicenseKey { get; }

            public override void Dispose()
            {
                GC.SuppressFinalize(this);
            }
        }
    }
}
