namespace Guru.Editor
{
    using System.Xml;
    using System.IO;
    using UnityEngine;
    using System.Collections.Generic;
    
    
    /// 
    /// Android 配置修改器
    /// 
    public class AndroidManifestDoc
    {
        private const string TargetPath = "Plugins/Android/AndroidManifest.xml";
        private const string XmlnsAndroid = "xmlns:android";
        private const string NamespaceAndroid = "http://schemas.android.com/apk/res/android";
        private const string XmlnsTools= "xmlns:tools";
        private const string NamespaceTools = "http://schemas.android.com/tools";
        
        private const string UserPermission = "uses-permission";
        private const string MetaData = "meta-data";
        private const string KName = "name";
        private XmlDocument _doc;
        public XmlDocument Doc => _doc;
        private string _docPath;
        private bool _isReady = false;
        
        private XmlElement _manifestNode;
        private XmlElement _applicationNode;
        private XmlElement _queriesNode;
        
        #region Initiallize
        
        /// 
        /// 加载文件
        /// 
        /// 
        /// 
        public static AndroidManifestDoc Load(string docPath = "")
        {
            if (string.IsNullOrEmpty(docPath))
            {
                docPath = Path.GetFullPath(Path.Combine(Application.dataPath, TargetPath));
            }
            if (!File.Exists(docPath))
            {
                Debug.LogError($"--- File not found: {docPath}");
                return null;
            }
            var mod = new AndroidManifestDoc();
            mod.ReadFromPath(docPath);
            return mod;
        }
        public static AndroidManifestDoc Read(string xmlStr, string docPath = "")
        {
            var mod = new AndroidManifestDoc();
            mod.ReadFromXml(xmlStr, docPath);
            return mod;
        }
        
        /// 
        /// 从文件路径读取
        /// 
        /// 
        public void ReadFromPath(string docPath)
        {
            _isReady = false;
            if (File.Exists(docPath))
            {
                var xmlStr = File.ReadAllText(docPath);
                ReadFromXml(xmlStr, docPath);
            }
            else
            {
                Debug.LogError($"--- File not found: {docPath}");
            }
        }
        public void ReadFromXml(string xmlStr, string docPath = "")
        {
            _doc = new XmlDocument();
            _doc.LoadXml(xmlStr);
            if(!string.IsNullOrEmpty(docPath)) _docPath = docPath;
            Init();
        }
        /// 
        /// Initializes the Doc
        /// 
        private void Init()
        {
            // --- Root Nodes ---
            _manifestNode = _doc.SelectSingleNode("manifest") as XmlElement;
            _applicationNode = _doc.SelectSingleNode("manifest/application") as XmlElement;
            _queriesNode = _doc.SelectSingleNode("manifest/queries") as XmlElement;
            
            
            AddXmlnsAndroid();
            AddXmlnsTools();
            _isReady = true;
        }
        /// 
        /// Save Doc
        /// 
        public void Save(string docPath = "")
        {
            if (_isReady)
            {
                if (!string.IsNullOrEmpty(docPath)) _docPath = docPath;
                if (!string.IsNullOrEmpty(_docPath))
                {
                    var dir = Directory.GetParent(_docPath);
                    if(!dir.Exists) dir.Create();
                    _doc.Save(_docPath);
                }
            }
        }
        #endregion
        #region Node Opreation
        
        public static bool AddAttribute(XmlElement node, string key, string value)
        {
            if (node != null)
            {
                if (node.HasAttribute(key))
                {
                    node.Attributes[key].Value = value;
                }
                else
                {
                    node.SetAttribute(key, value);
                }
                return true;
            }
            return false;
        }
        
        
        #endregion
        
        #region API
        public bool AddXmlnsAndroid()
        {
            return AddAttribute(_manifestNode, XmlnsAndroid, NamespaceAndroid);
        }
        
        public bool AddXmlnsTools()
        {
            return AddAttribute(_manifestNode, XmlnsTools, NamespaceTools);
        }
        
        /// 
        /// Add Replace Item
        /// 
        /// 
        public void AddApplicationReplaceItem(string item)
        {
            if (_applicationNode != null)
            {
                List items = new List(5);
                if (_applicationNode.HasAttribute("replace", NamespaceTools))
                {
                    var arr = _applicationNode.GetAttribute("replace",NamespaceTools).Split(',');
                    if(arr != null && arr.Length > 0)
                    {
                        items.AddRange(arr);
                    }
                }
                if (!items.Contains(item)) items.Add(item);
                
                _applicationNode.SetAttribute("replace",  NamespaceTools, string.Join(",", items));
            }
        }
        
        public void SetApplicationAttribute(string key, string value)
        {
            if (_applicationNode != null)
            {
                _applicationNode.SetAttribute(key, NamespaceAndroid, value);
            }
        }
        
        /// 
        /// Set metadata
        /// 
        /// 
        /// 
        /// 
        /// 
        public void SetMetadata(string key, string value, string valueName = "value", string keyName = KName)
        {
            if (_doc == null || !_isReady) return;
            
            XmlElement node = null;
            if (!TryGetMetadata(key, out node, keyName))
            {
                node = _doc.CreateElement(MetaData);
                _applicationNode?.AppendChild(node);
            }
            node.SetAttribute(keyName, NamespaceAndroid, key);
            node.SetAttribute(valueName, NamespaceAndroid, value);
        }
        
        /// 
        /// 添加权限
        /// 
        /// 
        /// 
        public void AddPermission(string key, string keyName = KName)
        {
            if (_doc == null || !_isReady) return;
            XmlElement node = null;
            if(!TryGetPermission(key, out node, keyName))
            {
                node = _doc.CreateElement(UserPermission);
                _manifestNode?.AppendChild(node);
            }
            node.SetAttribute(keyName, NamespaceAndroid, key);
        }
        
        /// 
        /// 删除 Permission
        /// 
        /// 
        /// 
        /// 
        /// 
        public void RemovePermission(string key, string value = "remove", string keyName = "name",  string valueName = "node")
        {
            if (_doc == null || !_isReady) return;
            
            XmlElement node = null;
            if(!TryGetPermission(key, out node, keyName))
            {
                node = _doc.CreateElement(UserPermission);
                _manifestNode?.AppendChild(node);
            }
            node.SetAttribute(keyName, NamespaceAndroid, key);
            node.SetAttribute(valueName, NamespaceTools, value);
        }
        
        public bool SetPackageName(string packageName)
        {
            if (_manifestNode != null)
            {
                _manifestNode.Attributes["package"].Value = packageName;
                return true;
            }
            return false;
        }
        
        /// 
        /// 添加 Queries Intent
        /// 
        /// 
        /// 
        /// 
        public void AddQueriesIntent(string value, string keyName = "name")
        {
            if (_queriesNode == null)
            {
                _queriesNode = _doc.CreateElement("queries");
                _manifestNode?.AppendChild(_queriesNode);
            }
            var intentList = _queriesNode.SelectNodes("intent");
            if (intentList != null)
            {
                foreach (XmlElement intent in intentList)
                {
                    var action = intent?.SelectSingleNode("action") as XmlElement;
                    
                    if (action != null 
                        && action.GetAttribute(keyName, NamespaceAndroid) == value)
                    {
                        return; // Has injected,skip ...
                    }
                }
            }
            
            // Inject new intent node
            XmlElement intentNode = _doc.CreateElement("intent");
            _queriesNode.AppendChild(intentNode);
            XmlElement actionNode = _doc.CreateElement("action");
            intentNode.AppendChild(actionNode);
            actionNode.SetAttribute(keyName, NamespaceAndroid, value);
        }
        
        
        #endregion
        #region Data Opration
        public bool TryGetMetadata(string name, out XmlElement node, string keyName = KName)
        {
            node = null;
            if(_applicationNode != null)
            {
                var list = _applicationNode.SelectNodes(MetaData);
                if (list != null)
                {
                    foreach (XmlElement e in list)
                    {
                        if (e.GetAttribute(keyName, NamespaceAndroid) == name)
                        {
                            node = e;
                            return true;
                        }
                    }
                }
            }
            return false;
        }
        
        
        public bool TryGetPermission(string name, out XmlElement node, string keyName = KName)
        {
            node = null;
            if(_manifestNode != null)
            {
                var list = _manifestNode.SelectNodes(UserPermission);
                if (list != null)
                {
                    foreach (XmlElement e in list)
                    {
                        if (e.GetAttribute(keyName, NamespaceAndroid) == name)
                        {
                            node = e;
                            return true;
                        }
                    }
                }
            }
            return false;
        }
        
        public bool TryRootNode(string nodeName, out XmlElement node)
        {
            node = null;
            if (_doc != null)
            {
                node = _doc.SelectSingleNode(nodeName) as XmlElement;
                return node != null;
            }
            return false;
        }
        
        #endregion
        
        #region Output
        public override string ToString()
        {
            if (_doc != null) return _doc.InnerXml;
            return this.ToString();
        }
        #endregion
        
    }
}