#if UNITY_ANDROID
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using UnityEngine;
using UnityEditor;
using UnityEditor.Android;

namespace Topon_Adapter.Editor
{
    public class AD_BuildAndroidProcess : IPostGenerateGradleAndroidProject
    {
        private static readonly XNamespace AndroidNamespace = "http://schemas.android.com/apk/res/android";
        private static readonly XNamespace ToolsNamespace   = "http://schemas.android.com/tools";


        public void OnPostGenerateGradleAndroidProject (string path)
        {
            ProcessAndroidManifest (path);
        }


        public static void ProcessAndroidManifest (string path)
        {
#if UNITY_2019_3_OR_NEWER
            var manifestPath         = Path.Combine (path, "src/main/AndroidManifest.xml");
            var launcherManifestPath = Path.Combine (path, "../launcher/src/main/AndroidManifest.xml");
#else
            var manifestPath = Path.Combine(path, "unityLibrary/src/main/AndroidManifest.xml");
#endif
            // var manifestPath = Path.Combine(path, "src/main/AndroidManifest.xml");
            XDocument manifest;
            XDocument launcherManifest;
            try
            {
                manifest         = XDocument.Load (manifestPath);
                launcherManifest = XDocument.Load (launcherManifestPath);
            }
#pragma warning disable 0168
            catch (IOException exception)
#pragma warning restore 0168
            {
                ATLog.log ("[BuildAndroidProcess] AndroidManifest.xml is missing.");
                return;
            }

#if UNITY_2019_3_OR_NEWER
            var resXmlPath = Path.Combine(path, "src/main/res/xml");
#else
            var resXmlPath = Path.Combine(path, "unityLibrary/src/main/res/xml");
#endif
            if (!Directory.Exists(resXmlPath))
            {
                Directory.CreateDirectory(resXmlPath);
            }
            var xmlPath = GetScriptsPath("AD_BuildAndroidProcess");
            saveFile($"{xmlPath}/alex_tt_file_path.xml", resXmlPath);
            saveFile($"{xmlPath}/anythink_bk_tt_file_path.xml", resXmlPath);
            
            // Get the `manifest` element.
            var elementManifest = manifest.Element ("manifest");
            if (elementManifest == null)
            {
                ATLog.log ("[BuildAndroidProcess] AndroidManifest.xml is invalid.");
                return;
            }

            var elementApplication = elementManifest.Element ("application");
            if (elementApplication == null)
            {
                ATLog.log ("[BuildAndroidProcess] AndroidManifest.xml is invalid.");
                return;
            }
            
            ToSingleTop (elementManifest);

            var      xManifestElements    = elementManifest.Descendants ();
            var      xApplicationElements = elementApplication.Descendants ();
            var      useQueries           = xManifestElements.Where (element => element.Name.LocalName.Equals ("package"));
            XElement tencentElement       = GetElementByName (useQueries, "com.tencent.mm");
            if (tencentElement == null)
            {
                elementManifest.Add (CreateQueries ());
            }

            var wxElements = xApplicationElements
                .Where (element => element.Name.LocalName.Equals ("activity"))
                .Where (element => element.Attribute (AndroidNamespace + "name")?.Value == ".wxapi.WXEntryActivity")
                .ToList ();
            foreach (var wxElement in wxElements)
            {
                wxElement.Remove ();
            }

            elementApplication.Add (CreateActivityXML ());


            var elementlauncherManifest    = launcherManifest.Element ("manifest");
            if (elementlauncherManifest == null)
            {
                ATLog.log ("[BuildAndroidProcess] Launcher AndroidManifest.xml is invalid.");
                return;
            }

            var elementlauncherApplication = elementlauncherManifest.Element ("application");
            if (elementlauncherApplication == null)
            {
                ATLog.log ("[BuildAndroidProcess] Launcher AndroidManifest.xml is invalid.");
                return;
            }

            var launcherProviders = elementlauncherApplication.Descendants ()
                .Where (element => element.Name.LocalName.Equals ("provider"))
                .Where (element => element.Attribute (AndroidNamespace + "name")?.Value == "com.bytedance.sdk.openadsdk.TTFileProvider")
                .ToList ();
            foreach (var launcherProvider in launcherProviders)
            {
                launcherProvider.Remove ();
            }

            elementlauncherApplication.Add (CreateCSJGromore ());
            SetOrAddAttribute (elementlauncherApplication, AndroidNamespace + "allowBackup", "false");
            MergeToolsReplaceAttribute (elementlauncherApplication, GetLauncherApplicationReplaceValues (elementlauncherApplication));
            
            var elementMainManifest    = manifest.Element ("manifest");
            var elementMainApplication = elementMainManifest.Element ("application");
            
            var      mainElements = elementMainApplication.Descendants ().Where (element => element.Name.LocalName.Equals ("provider"));
            XElement mainElement  = GetElementByName (mainElements, "com.bytedance.sdk.openadsdk.TTFileProvider");
            if (mainElement == null)
            {
                //增加穿山甲配置
                // elementMainApplication.Add (CreateCSJGromore ());
            }

            // Save the updated manifest file.
            manifest.Save (manifestPath);
            launcherManifest.Save (launcherManifestPath);
        }

        public static void saveFile(string filePathName , string toFilesPath)
        {
            if (string.IsNullOrWhiteSpace(filePathName) || !File.Exists(filePathName))
            {
                ATLog.log ($"[BuildAndroidProcess] Skip missing file copy: {filePathName}");
                return;
            }

            FileInfo file        = new FileInfo(filePathName);
            string   newFileName = file.Name;
            file.CopyTo(toFilesPath + "/" + newFileName, true);
        }
        
        /// <summary>
        /// 增加微信开放平台的适配
        /// </summary>
        public static XElement CreateActivityXML ()
        {
            var metaData = new XElement ("activity");
            metaData.Add (new XAttribute (AndroidNamespace + "name",  ".wxapi.WXEntryActivity"));
            metaData.Add (new XAttribute (AndroidNamespace + "label", "@string/app_name"));
            metaData.Add (new XAttribute (AndroidNamespace + "theme", "@android:style/Theme.Translucent.NoTitleBar"));
            metaData.Add (new XAttribute (AndroidNamespace + "exported", "true"));
            metaData.Add (new XAttribute (AndroidNamespace + "taskAffinity", Application.identifier));
            metaData.Add (new XAttribute (AndroidNamespace + "launchMode", "singleTop"));
            return metaData;
        }

        public static XElement CreateQueries ()
        {
            var metaData    = new XElement ("queries");
            var packageData = new XElement ("package");
            packageData.Add (new XAttribute (AndroidNamespace + "name", "com.tencent.mm"));
            metaData.Add (packageData);
            return metaData;
        }

        private static XElement GetElementByName (IEnumerable<XElement> elements, string name)
        {
            foreach (var element in elements)
            {
                var attributes = element.Attributes ();
                if (attributes.Any (attribute => attribute.Name.Namespace.Equals (AndroidNamespace)
                                                 && attribute.Name.LocalName.Equals ("name")
                                                 && attribute.Value.Equals (name)))
                {
                    return element;
                }
            }

            return null;
        }

        public static string GetScriptsPath (string scriptName)
        {
            string[] path = UnityEditor.AssetDatabase.FindAssets(scriptName);
            if(path.Length >1)
            {
                // Debug.LogError("有同名文件"+_scriptName+"获取路径失败");
                return null;
            }
            //将字符串中得脚本名字和后缀统统去除掉
            string _path = AssetDatabase.GUIDToAssetPath(path[0]).Replace((@"/" +scriptName +".cs"),"");
            return _path;
        }

        public static XElement CreateCSJGromore ()
        {
            // ReSharper disable once InvalidXmlDocComment
            /**
             <provider
                android:name="com.bytedance.sdk.openadsdk.TTFileProvider"
                android:authorities="${applicationId}.TTFileProvider"
                android:exported="false"
                android:grantUriPermissions="true"
                tools:replace="android:authorities">
            <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/anythink_bk_tt_file_path"/>
            </provider>
             * 
             */
            var providerData = new XElement ("provider");

            providerData.Add (new XAttribute (AndroidNamespace + "name" , "com.bytedance.sdk.openadsdk.TTFileProvider"));
            providerData.Add (new XAttribute (AndroidNamespace + "authorities" , "${applicationId}.TTFileProvider"));
            providerData.Add (new XAttribute (AndroidNamespace + "exported" , "false"));
            providerData.Add (new XAttribute (AndroidNamespace + "grantUriPermissions" , "true"));
            providerData.Add (new XAttribute (ToolsNamespace   + "replace" , "android:authorities"));
            var metaData = new XElement ("meta-data");
            metaData.Add (new XAttribute (AndroidNamespace + "name" , "android.support.FILE_PROVIDER_PATHS"));
            metaData.Add (new XAttribute (AndroidNamespace + "resource" , "@xml/anythink_bk_tt_file_path"));
            providerData.Add (metaData);
            return providerData;
        }

        public static void ToSingleTop (XElement manifest)
        {
            
            var elements = manifest.Descendants ().Where (element => element.Name.LocalName.Equals ("activity"));
            // 如果这个activity已经有了 android:launchMode="singleTask"，则不再添加
            XElement elementByName = GetElementByName (elements , "com.unity3d.player.UnityPlayerActivity");
            if (elementByName == null)
            {
                ATLog.log ("[BuildAndroidProcess] UnityPlayerActivity not found, skip launchMode patch.");
                return;
            }

            //此处将 android:launchMode="singleTask" 改为 android:launchMode="singleTop"
            SetOrAddAttribute (elementByName, AndroidNamespace + "launchMode", "singleTop");
        }

        private static void SetOrAddAttribute (XElement element, XName attributeName, string value)
        {
            var attribute = element.Attribute (attributeName);
            if (attribute == null)
            {
                element.Add (new XAttribute (attributeName, value));
                return;
            }

            attribute.SetValue (value);
        }

        private static string[] GetLauncherApplicationReplaceValues (XElement element)
        {
            List<string> replaceValues = new List<string> { "android:allowBackup" };
            if (element.Attribute (AndroidNamespace + "appComponentFactory") != null)
            {
                replaceValues.Add ("android:appComponentFactory");
            }

            return replaceValues.ToArray ();
        }

        private static void MergeToolsReplaceAttribute (XElement element, params string[] replaceValues)
        {
            XName replaceAttributeName = ToolsNamespace + "replace";
            List<string> mergedValues = new List<string> ();

            var currentValue = element.Attribute (replaceAttributeName)?.Value;
            if (string.IsNullOrWhiteSpace (currentValue) == false)
            {
                mergedValues.AddRange (currentValue.Split (',')
                    .Select (value => value.Trim ())
                    .Where (value => string.IsNullOrWhiteSpace (value) == false));
            }

            foreach (var replaceValue in replaceValues)
            {
                if (mergedValues.Contains (replaceValue) == false)
                {
                    mergedValues.Add (replaceValue);
                }
            }

            element.SetAttributeValue (replaceAttributeName, string.Join (",", mergedValues));
        }

        public int callbackOrder { get; }
    }
}

#endif
