﻿using JLGames.RocketDriver.Actions.Utils;
using UnityEditor;
using UnityEngine;

namespace JLGames.RocketDriver.Editor.Infra
{
    public static class EditorAssetReferenceUtil
    {
        /// <summary>
        /// Compare the index record before the reference is lost with the current index record,
        /// and use the reference with the same name to repair the lost
        /// 对比引用丢失前的索引记录与现在的索引记录，使用名称相同的引用进行修复丢失
        /// </summary>
        /// <param name="go"></param>
        /// <param name="oldIndexPath"></param>
        /// <param name="fixIndexPath"></param>
        /// <param name="saveNow"></param>
        public static void FixReferenceMissing(GameObject go, string oldIndexPath, string fixIndexPath, bool saveNow = true)
        {
            if (null == go || string.IsNullOrEmpty(oldIndexPath) || string.IsNullOrEmpty(fixIndexPath)) return;
            var oldIndex = AssetDatabase.LoadAssetAtPath<EditorAssetIndex>(oldIndexPath);
            var fixIndex = AssetDatabase.LoadAssetAtPath<EditorAssetIndex>(fixIndexPath);
            FixReferenceMissing(go, oldIndex, fixIndex, saveNow);
        }

        /// <summary>
        /// Compare the index record before the reference is lost with the current index record,
        /// and use the reference with the same name to repair the lost
        /// 对比引用丢失前的索引记录与现在的索引记录，使用名称相同的引用进行修复丢失
        /// </summary>
        /// <param name="go"></param>
        /// <param name="oldIndex"></param>
        /// <param name="fixIndex"></param>
        /// <param name="saveNow"></param>
        public static void FixReferenceMissing(GameObject go, EditorAssetIndex oldIndex, EditorAssetIndex fixIndex, bool saveNow = true)
        {
            if (null == go || null == oldIndex || null == fixIndex) return;
            var missingResult = FindMissingReferences<Component>(go);
            missingResult.Foreach(missingPaths =>
            {
                var so = new SerializedObject(missingPaths.Component);
                foreach (var path in missingPaths.Paths)
                {
                    var missProperty = so.FindProperty(path);
                    string guid;
                    long localFileId;
                    AssetDatabase.TryGetGUIDAndLocalFileIdentifier(missProperty.objectReferenceInstanceIDValue, out guid, out localFileId);
                    var oldInfo = oldIndex.FindByGuid(guid, localFileId);
                    if (null == oldInfo)
                    {
                        DebugUtil.LogWarning($"Fix ignore by guid[{guid},{localFileId}] not found in OldAssetIndex!");
                        continue;
                    }

                    var newInfo = fixIndex.FindByName(oldInfo.AssetName);
                    if (null == newInfo)
                    {
                        DebugUtil.LogWarning($"Fix ignore by AssetName[{oldInfo.AssetName}] not found in FixAssetIndex!");
                        continue;
                    }

                    var asset = newInfo.LoadAsset();
                    if (null == asset)
                    {
                        DebugUtil.LogWarning($"Fix ignore by Asset[{oldInfo.AssetName}] not found in project!");
                        continue;
                    }

                    missProperty.objectReferenceInstanceIDValue = asset.GetInstanceID();
                }

                so.ApplyModifiedProperties();
            });
            if (saveNow) AssetDatabase.SaveAssets();
        }

        /// <summary>
        /// Compares two reference indices "EditorAssetIndex" and replaces references with the same name
        /// 对比两个引用索引， 替换名称相同的引用
        /// </summary>
        /// <param name="go"></param>
        /// <param name="srcIndexPath"></param>
        /// <param name="tarIndexPath"></param>
        /// <param name="saveNow"></param>
        public static void ReplaceReferences(GameObject go, string srcIndexPath, string tarIndexPath, bool saveNow = true)
        {
            if (null == go || string.IsNullOrEmpty(srcIndexPath) || string.IsNullOrEmpty(tarIndexPath)) return;
            var srcIndex = AssetDatabase.LoadAssetAtPath<EditorAssetIndex>(srcIndexPath);
            var tarIndex = AssetDatabase.LoadAssetAtPath<EditorAssetIndex>(tarIndexPath);
            ReplaceReferences(go, srcIndex, tarIndex, saveNow);
        }

        /// <summary>
        /// Compares two reference indices "EditorAssetIndex" and replaces references with the same name
        /// 对比两个引用索引， 替换名称相同的引用
        /// </summary>
        /// <param name="go"></param>
        /// <param name="srcIndex"></param>
        /// <param name="tarIndex"></param>
        /// <param name="saveNow"></param>
        public static void ReplaceReferences(GameObject go, EditorAssetIndex srcIndex, EditorAssetIndex tarIndex, bool saveNow = true)
        {
            if (null == go || null == srcIndex || null == tarIndex) return;
            var cps = go.GetComponentsInChildren<Component>(true);
            foreach (var component in cps)
            {
                var so = new SerializedObject(component);
                var iter = so.GetIterator();
                while (iter.NextVisible(true))
                {
                    if (iter.propertyType != SerializedPropertyType.ObjectReference || 0 == iter.objectReferenceInstanceIDValue) continue;
                    string guid;
                    long localFileId;
                    AssetDatabase.TryGetGUIDAndLocalFileIdentifier(iter.objectReferenceInstanceIDValue, out guid, out localFileId);
                    var srcInfo = srcIndex.FindByGuid(guid, localFileId);
                    if (null == srcInfo)
                    {
                        DebugUtil.LogWarning($"Repalce ignore by guid[{guid},{localFileId}] not found in SourceAssetIndex!");
                        continue;
                    }

                    var atlasInfo = tarIndex.FindByName(srcInfo.AssetName);
                    if (null == atlasInfo)
                    {
                        DebugUtil.LogWarning($"Repalce ignore by AssetName[{srcInfo.AssetName}] not found in TargetAssetIndex!");
                        continue;
                    }

                    var asset = atlasInfo.LoadAsset();
                    if (null == asset)
                    {
                        DebugUtil.LogWarning($"Repalce ignore by Asset[{atlasInfo.AssetName}] not found in project!");
                        continue;
                    }

                    iter.objectReferenceInstanceIDValue = asset.GetInstanceID();
                }

                so.ApplyModifiedProperties();
            }

            if (saveNow) AssetDatabase.SaveAssets();
        }

        /// <summary>
        /// Find missing references to components on Unity objects
        /// 查看Unity对象上组件丢失的引用
        /// </summary>
        /// <param name="go"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static MissingReferences FindMissingReferences<T>(GameObject go) where T : Component
        {
            var cps = go.GetComponentsInChildren<T>(true);
            var rs = new MissingReferences();
            foreach (var component in cps)
            {
                var so = new SerializedObject(component);
                var iter = so.GetIterator();
                while (iter.NextVisible(true))
                {
                    if (iter.propertyType != SerializedPropertyType.ObjectReference) continue;
                    if (iter.objectReferenceValue == null && iter.objectReferenceInstanceIDValue != 0) // 对象为空，引用却不为0
                    {
                        rs.AppendMissingPath(component, iter.propertyPath);
                    }
                }
            }

            return rs;
        }
    }
}