#if UNITY_EDITOR
namespace NiAkaDev.EzMirror.Editor
{
    using System.Globalization;
    using System.Security.Cryptography;
    using System.Text;
    using UnityEditor;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    using VRC.Udon;

    internal static class EzMirrorRepairUtility
    {
        internal static bool RepairSafeIssues(EzMirror mirror)
        {
            if (mirror == null)
                return false;

            bool changed = false;
            if (mirror.usePersist && string.IsNullOrEmpty(mirror.persistKey))
            {
                string key = GeneratePersistKey(mirror);
                if (!string.IsNullOrEmpty(key))
                {
                    Undo.RecordObject(mirror, "Generate EzMirror Persist Key");
                    mirror.persistKey = key;
                    EditorUtility.SetDirty(mirror);
                    PrefabUtility.RecordPrefabInstancePropertyModifications(mirror);
                    changed = true;
                }
            }

            changed |= RepairProfileArrayMinimum(mirror);
            if (CanRepairGeneratedMaterials(mirror))
                changed |= EzMirrorEditor.EnsureDedicatedGeneratedProfileMaterials(mirror);
            return changed;
        }

        internal static bool GeneratePersistKeyForSelected()
        {
            EzMirror mirror = Selection.activeGameObject == null
                ? null
                : Selection.activeGameObject.GetComponentInParent<EzMirror>();
            if (mirror == null)
            {
                EditorUtility.DisplayDialog("EzMirror", "EzMirrorを選択してください。", "OK");
                return false;
            }
            return GeneratePersistKeyForMirror(mirror, true);
        }

        internal static bool GeneratePersistKeyForMirror(EzMirror mirror, bool showDialog)
        {
            if (mirror == null)
                return false;
            if (!string.IsNullOrEmpty(mirror.persistKey))
            {
                if (showDialog)
                    EditorUtility.DisplayDialog("EzMirror", "既存の保存キーは上書きしません。", "OK");
                return false;
            }
            string key = GeneratePersistKey(mirror);
            if (string.IsNullOrEmpty(key))
            {
                if (showDialog)
                    EditorUtility.DisplayDialog("EzMirror", "保存キーを生成できません。Scene保存後に再実行してください。", "OK");
                return false;
            }
            Undo.RecordObject(mirror, "Generate EzMirror Persist Key");
            mirror.persistKey = key;
            mirror.usePersist = true;
            EditorUtility.SetDirty(mirror);
            PrefabUtility.RecordPrefabInstancePropertyModifications(mirror);
            return true;
        }

        internal static string GeneratePersistKey(EzMirror mirror)
        {
            if (mirror == null)
                return null;
            string source = GetStableIdentitySource(mirror);
            if (string.IsNullOrEmpty(source))
                return null;
            byte[] bytes;
            using (SHA1 sha1 = SHA1.Create())
                bytes = sha1.ComputeHash(Encoding.UTF8.GetBytes(source));
            StringBuilder builder = new StringBuilder("EZ_MIRROR_");
            for (int i = 0; i < 5; i++)
                builder.Append(bytes[i].ToString("X2", CultureInfo.InvariantCulture));
            return builder.ToString();
        }

        private static string GetStableIdentitySource(EzMirror mirror)
        {
            string assetPath = AssetDatabase.GetAssetPath(mirror);
            if (!string.IsNullOrEmpty(assetPath))
            {
                string guid;
                long localId;
                if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(mirror, out guid, out localId))
                    return guid + ":" + localId.ToString(CultureInfo.InvariantCulture);
                return null;
            }

            Scene scene = mirror.gameObject.scene;
            if (!scene.IsValid() || string.IsNullOrEmpty(scene.path) || scene.isDirty)
                return null;

            GlobalObjectId globalId = GlobalObjectId.GetGlobalObjectIdSlow(mirror);
            string globalIdText = globalId.ToString();
            return IsStableGlobalObjectId(globalIdText) ? globalIdText : null;
        }

        private static bool IsStableGlobalObjectId(string globalIdText)
        {
            const string ZeroGuid = "00000000000000000000000000000000";
            return !string.IsNullOrEmpty(globalIdText)
                && globalIdText != "GlobalObjectId_V1-0-" + ZeroGuid + "-0-0"
                && globalIdText.IndexOf("-" + ZeroGuid + "-", System.StringComparison.Ordinal) < 0;
        }

        private static bool RepairProfileArrayMinimum(EzMirror mirror)
        {
            bool changed = false;
            if (mirror.profileMaterials == null || mirror.profileMaterials.Length < 4)
            {
                Undo.RecordObject(mirror, "Repair EzMirror Profiles");
                mirror.profileMaterials = ResizeCopy(mirror.profileMaterials, 4);
                changed = true;
            }
            if (mirror.profileBaseMaterials == null || mirror.profileBaseMaterials.Length < mirror.profileMaterials.Length)
            {
                if (!changed)
                    Undo.RecordObject(mirror, "Repair EzMirror Profiles");
                mirror.profileBaseMaterials = ResizeCopy(mirror.profileBaseMaterials, mirror.profileMaterials.Length);
                changed = true;
            }
            if (changed)
            {
                EditorUtility.SetDirty(mirror);
                PrefabUtility.RecordPrefabInstancePropertyModifications(mirror);
            }
            return changed;
        }

        private static bool CanRepairGeneratedMaterials(EzMirror mirror)
        {
            return mirror != null && mirror.GetComponent<UdonBehaviour>() != null;
        }

        private static T[] ResizeCopy<T>(T[] source, int size)
        {
            T[] result = new T[size];
            if (source == null)
                return result;
            int count = Mathf.Min(source.Length, size);
            for (int i = 0; i < count; i++)
                result[i] = source[i];
            return result;
        }
    }
}
#endif
