// Scene材质会单独加载Bundle,一般物体则按GameObject流程加载。
// Scene被分解后,非平行光和其他非运行时物体都直接抛弃;Renderer材质全部修改为白模;
// 运行时,按照九宫格流程加载所需资源;加载完成材质后,替换当前Renderer中的白模材质;
// 模型网格会按照场景需求进行复制;材质、着色器和贴图会按照独一无二方式处理;

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Games.Scene;
using GCGame.Table;
using NavMeshExtension;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;

public class SceneDisassembly : EditorPerFrameActionBase
{
    public const string sceneRoot = "Assets/Res_newMS/ChangJing/Sence";
    public const string sceneDisassembledPath = "Assets/Project3D/BundleData/Scene";
    //public const string roleSelectDir = sceneRoot + "/"; //+ GlobeVar.sceneRoleSelect;
    //public const string roleSelectTargetDir = sceneDisassembledPath + "/" + GlobeVar.sceneRoleSelect;

    public const string itemRootLight = "Lights";
    public const string itemRootAnimator = "Animators";
    public const string itemRootParticle = "Particles";
    public const string itemRootSceneObj = "SceneObjs";
    public const string itemRootCollider = "OtherColliders";
    public const string itemRootMisc = "MiscItems";
    public const string sceneExtension = ".unity";
    
    private readonly List<GameObject> _parentGameObjects = new List<GameObject>();
    private readonly string _sceneAssetPath;

    private readonly string _sceneName;
    private readonly string _targetAssetPath;

    private SceneDisassemblyProcess _index;
    private Scene _newScene;
    private Scene _oldScene;
    private Tab_SceneClass _sceneClass;

    public SceneDisassembly(string sceneAssetPath)
    {
        _index = SceneDisassemblyProcess.End;
        var fullSceneName = Path.GetFileName(sceneAssetPath);
        _sceneName = Path.GetFileNameWithoutExtension(fullSceneName);
        _sceneAssetPath = sceneAssetPath;
        _targetAssetPath = sceneDisassembledPath.Open(fullSceneName);
        _index = 0;
    }

    protected override int GetCurrentIndex()
    {
        return (int) _index;
    }

    protected override int GetTotalCount()
    {
        return (int) SceneDisassemblyProcess.End;
    }

    protected override void PerFrameAction()
    {
        switch (_index)
        {
            case SceneDisassemblyProcess.Open:
                // 异常防御机制
                var temp = _index;
                _index = SceneDisassemblyProcess.End;
                var sceneClass = EditorTableManager.GetTable<Tab_SceneClass>();
                _sceneClass = sceneClass.Find(a => a.ResName.Equals(_sceneName, StringComparison.OrdinalIgnoreCase));
                if (_sceneClass != null)
                {
                    // 检查场景Hash,如果匹配就不执行分解
                    var lastHash = GetLastSceneHash(_sceneAssetPath);
                    var currentHash = GetCurrentScenePath(_sceneAssetPath);
                    if (lastHash != currentHash)
                    {
                        _oldScene = EditorSceneManager.OpenScene(_sceneAssetPath, OpenSceneMode.Single);
                        if (_oldScene.isLoaded)
                            _index = temp + 1;
                        else
                            Debug.LogError("场景加载失败 " + _sceneName);
                    }
                    else
                        Debug.LogWarning("场景 " + _sceneName + " Hash匹配,跳过分解流程!");
                }
                else
                {
                    Debug.LogError("场景数据获得失败 " + _sceneName);
                }

                break;
            case SceneDisassemblyProcess.Create:
                _newScene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Additive);
                _index++;
                break;
            case SceneDisassemblyProcess.Disassemble:
                MoveSceneObjs();
                SwapSceneObjs();
                EditorSceneManager.CloseScene(_newScene, true);
                _index++;
                break;
            case SceneDisassemblyProcess.CreateLightData:
                // 注:确实需要保存两次,因为SceneAsset和LightingDataAsset是循环关联的
                // 第一次创建场景资源;然后创建新的LightingData,LightingData关联场景资源;然后替换场景LightningData后再保存场景
                EditorSceneManager.SaveScene(_oldScene, _targetAssetPath);
                CreateLightingData();
                _index++;
                break;
            case SceneDisassemblyProcess.CreateAreas:
                FinalTweaks();
                CheckUnexpected();
                BuiltinAssetBuilder.ReplaceForOneScene(_oldScene);
                RemoveAreaAssets();
                _index++;
                break;
            case SceneDisassemblyProcess.Save:
                // 分解结束后第二次保存
                EditorSceneManager.SaveScene(_oldScene, _targetAssetPath);
                WriteSceneHash(_sceneAssetPath);
                _index++;
                break;
            default:
                _index++;
                break;
        }
    }

    private void MoveSceneObjs()
    {
        // 预先移除出现故障的组件
        DestroyComponentByCondition<MeshCollider>(a => a.sharedMesh == null);
        DestroyComponentByCondition<Animator>(a => a.runtimeAnimatorController == null);
        DestroyComponentByCondition<Light>(a => a.type != LightType.Directional);
        DestroyComponentByCondition<MeshRenderer>(a =>
        {
            var filter = a.GetComponent<MeshFilter>();
            return filter == null || filter.sharedMesh == null;
        });
        DestroyComponentByCondition<SkinnedMeshRenderer>(a =>
            a.sharedMesh == null || a.rootBone == null || a.bones == null || a.bones.Length < 1);
        DestroyComponentByCondition<T4MObjSC>(a => true);
        var rootObjects = _oldScene.GetRootGameObjects();
        for (var i = 0; i < rootObjects.Length; i++)
            MoveGameObjectToNewScene(rootObjects[i].transform);
    }

    private static void TweakComponent<T>(UnityAction<T> action) where T : Component
    {
        var components = Object.FindObjectsOfType<T>();
        for (var i = 0; i < components.Length; i++)
            action.Invoke(components[i]);
    }

    private void SwapSceneObjs()
    {
        var oldRoots = _oldScene.GetRootGameObjects();
        var newRoots = _newScene.GetRootGameObjects();
        for (var i = 0; i < newRoots.Length; i++)
            SceneManager.MoveGameObjectToScene(newRoots[i], _oldScene);
        for (var i = 0; i < oldRoots.Length; i++)
            Object.DestroyImmediate(oldRoots[i]);
    }

    private void FinalTweaks()
    {
        var rootItems = _oldScene.GetRootGameObjects();
        for (var i = 0; i < rootItems.Length; i++)
            PurgeAssets.PurgeOneGameObject(rootItems[i].transform);
        // 非Unity内置材质切换到空白材质
        TweakComponent<Renderer>(a =>
        {
            // 保留一些动画的投影能力
            if (!(a is SkinnedMeshRenderer))
            {
                a.shadowCastingMode = ShadowCastingMode.Off;
                a.receiveShadows = a is MeshRenderer;
            }
            a.allowOcclusionWhenDynamic = false;
            a.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion;
        });
        TweakComponent<Light>(a =>
        {
            a.lightmapBakeType = LightmapBakeType.Mixed;
            a.shadows = LightShadows.Soft;
            a.shadowResolution = LightShadowResolution.High;
            //// 仅仅用于将主角影子投射到地面
            a.cullingMask = LayerMask.GetMask("MainPlayer") | ActiveScene.terrainLayMask;
        });
        // 修复一个可能导致无动画SkinnedMeshRenderer骨骼丢失的情况 - 该情况下本身蒙皮也无法运动
        TweakComponent<SkinnedMeshRenderer>(a =>
        {
            a.skinnedMotionVectors = false;
            var error = a.rootBone == null;
            if (!error)
                for (var i = 0; i < a.bones.Length; i++)
                    if (a.bones[i] == null)
                    {
                        error = true;
                        break;
                    }

            if (error)
            {
                a.rootBone = null;
                a.bones = new Transform[0];
            }
        });

        var lightRoot = GameObject.Find(itemRootLight);
        if (lightRoot != null)
            lightRoot.SetStaticRecursively(false);
        var particleRoot = GameObject.Find(itemRootParticle);
        if (particleRoot != null)
            particleRoot.SetStaticRecursively(false);
        var animatorRoot = GameObject.Find(itemRootAnimator);
        if (animatorRoot != null)
            animatorRoot.SetStaticRecursively(false);
        var sceneObjRoot = GameObject.Find(itemRootSceneObj);
        if (sceneObjRoot != null)
            sceneObjRoot.SetStaticRecursively(true);
        var colliderRoot = GameObject.Find(itemRootCollider);
        if (colliderRoot != null)
            colliderRoot.SetStaticRecursively(true);
        RenderSettings.skybox = null;
//        var cameraRoot = new GameObject("CameraRoot");
//        var cameraObj = new GameObject("Main Camera") {tag = "MainCamera"};
//        cameraObj.transform.SetParent(cameraRoot.transform);
//        var camera = cameraObj.AddComponent<Camera>();
//        camera.allowHDR = false;
//        camera.useOcclusionCulling = false;
//        camera.allowMSAA = false;
    }

    private void CreateLightingData()
    {
        var lightingData = Lightmapping.lightingDataAsset;
        var sourcePath = AssetDatabase.GetAssetPath(lightingData);
        var extension = Path.GetExtension(sourcePath);
        var index = _targetAssetPath.LastIndexOf('.');
        var lightingDataPath = index < 0 ? _targetAssetPath + extension : _targetAssetPath.Remove(index) + extension;
        if (AssetDatabase.CopyAsset(sourcePath, lightingDataPath))
        {
            lightingData = AssetDatabase.LoadAssetAtPath<LightingDataAsset>(lightingDataPath);
            var serializedObject = new SerializedObject(lightingData);
            var property = serializedObject.FindProperty("m_Scene");
            var newSceneAsset = AssetDatabase.LoadAssetAtPath<SceneAsset>(_targetAssetPath);
            property.objectReferenceValue = newSceneAsset;
            serializedObject.ApplyModifiedProperties();
            EditorUtility.SetDirty(lightingData);
            Lightmapping.lightingDataAsset = lightingData;
        }
        else
            Debug.LogError("Lighting Data Asset Copy Failed!");
    }

    private void CheckUnexpected()
    {
        var components = Object.FindObjectsOfType<Component>();
        for (var i = 0; i < components.Length; i++)
        {
            var component = components[i];
            if (!(component is Renderer) &&
                !(component is Transform) &&
                !(component is MeshFilter) &&
                !(component is Collider) &&
                !(component is Camera) &&
                !(component is SceneLogic) &&
                !(component is Light) &&
                !(component is Animator) &&
                !(component is ParticleSystem) &&
                !(component is FogDensityLerp) &&
                !(component is DirectionalLightSetter) &&
                //!(component is uv_l_r) &&
                //!(component is uv_u_d) &&
                !(component is SceneObjItem) &&
                !(component is MovieEventRecv) &&
                !(component is AddChildAsset) &&
                !(component is DailyCopySceneAnimCtr) &&
                !(component is LoginShow) &&
                !(component is ObjectRotate))
                Debug.LogError("Unexpected Component " + component.GetType() + " on item " +
                               component.transform.GetHierarchyName());
        }
    }

    private string CheckComponent<T>(Transform root, string parentName) where T : Component
    {
        var result = root.GetComponent<T>();
        if (result != null)
        {
            var mono = result as MonoBehaviour;
            if ((mono == null || mono.enabled) && result.gameObject.activeInHierarchy)
                return parentName;
        }
        return null;
    }

    private void MoveGameObjectToNewScene(Transform root)
    {
        if (!root.gameObject.activeInHierarchy)
            return;
        var moveChildren = true;
        // parentName == null表示不需要移动
        string parentName = null;

        if (parentName == null)
        {
            var sceneLogic = root.GetComponent<SceneLogic>();
            if (sceneLogic != null)
            {
                parentName = string.Empty;
                moveChildren = false;
            }
        }

        if (parentName == null)
            parentName = CheckComponent<Light>(root, itemRootLight);

        if (parentName == null)
        {
            var animator = root.GetComponent<Animator>();
            if (animator != null && animator.isActiveAndEnabled)
            {
                // 特殊处理带有Animator的物体,不能分解物体本身
                var renderers = animator.GetComponentsInChildren<Renderer>();
                // 不具有Renderer的Animator直接执行摧毁
                if (renderers.Length > 0)
                {
                    moveChildren = false;
                    parentName = itemRootAnimator;
                }
            }
        }

        if (parentName == null)
            parentName = CheckComponent<ParticleSystem>(root, itemRootParticle);

        if (parentName == null)
        {
            var renderer = root.GetComponent<Renderer>();
            if (renderer != null && renderer.enabled && renderer.gameObject.activeInHierarchy &&
                !root.HasComponent<NavMeshObject>())
                parentName = itemRootSceneObj;
        }

        if (parentName == null)
            parentName = CheckComponent<Collider>(root, itemRootCollider);
        
        if (parentName == null)
            parentName = CheckComponent<MovieEventRecv>(root, itemRootMisc);

        if (parentName == null)
        {
            parentName = CheckComponent<DailyCopySceneAnimCtr>(root, itemRootMisc);
            if (!string.IsNullOrEmpty(parentName))
                moveChildren = false;
        }

        List<Transform> children = null;
        if (moveChildren)
        {
            children = new List<Transform>();
            var childCount = root.childCount;
            for (var i = childCount - 1; i >= 0; i--)
            {
                var child = root.GetChild(i);
                children.Add(child);
                if (parentName != null)
                {
                    RemovePrefab(child.gameObject);
                    child.SetParent(null);
                }
            }
        }

        if (parentName != null)
        {
            RemovePrefab(root.gameObject);
            if (string.IsNullOrEmpty(parentName))
            {
                root.SetParent(null);
                SceneManager.MoveGameObjectToScene(root.gameObject, _newScene);
            }
            else
            {
                var parentObj = _parentGameObjects.Find(a => a.name == parentName);
                if (parentObj == null)
                {
                    parentObj = new GameObject(parentName);
                    if (parentObj.scene != _newScene)
                        SceneManager.MoveGameObjectToScene(parentObj, _newScene);
                    _parentGameObjects.Add(parentObj);
                }

                root.SetParent(null);
                SceneManager.MoveGameObjectToScene(root.gameObject, _newScene);
                root.SetParent(parentObj.transform);
            }
        }

        if (children != null)
            foreach (var child in children)
                MoveGameObjectToNewScene(child);
    }
    
    private static void DestroyComponentByCondition<T>(Predicate<T> condition) where T : Component
    {
        var components = Object.FindObjectsOfType<T>();
        for (var i = 0; i < components.Length; i++)
            if (condition.Invoke(components[i]))
                Object.DestroyImmediate(components[i]);
    }
    // Didn't remove the interface to make grammar check possible
    private static void RemovePrefab(GameObject item)
    {
#if UNITY_2018_1_OR_NEWER
        var prefabRoot = PrefabUtility.GetNearestPrefabInstanceRoot(item);
        if (prefabRoot != null)
            PrefabUtility.UnpackPrefabInstance(prefabRoot, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
#endif
    }

    #region 分解场景物体流程
    private void RemoveAreaAssets()
    {
        var areaPath = sceneDisassembledPath.Open(_oldScene.name);
        var removeAssets = (from assetPath in AssetDatabase.GetAllAssetPaths()
            where assetPath.StartsWith(areaPath + "/")
            select assetPath).ToArray();
        for (var i = 0; i < removeAssets.Length; i++)
        {
            EditorUtility.DisplayProgressBar(title, string.Format("Delete Scene Asset: {0} / {1}", i, removeAssets.Length), (float)i / removeAssets.Length);
            AssetDatabase.DeleteAsset(removeAssets[i]);
        }
    }

    //    private void CreateAreas()
    //    {
    //        var permanentLayer = LayerMask.NameToLayer("T4MLayer");
    //        // 切割场景区域块 - 因为需要维护分解和非分解两个流程,暂时不合并两次Transform移动
    //        var particleRoot = GameObject.Find(itemRootParticle);
    //        if (particleRoot != null)
    //            CreateForOneRoot(particleRoot.transform, permanentLayer);
    //        var meshRoot = GameObject.Find(itemRootSceneObj);
    //        if (meshRoot != null)
    //            CreateForOneRoot(meshRoot.transform, permanentLayer);
    //        var animRoot = GameObject.Find(itemRootAnimator);
    //        if (animRoot != null)
    //            CreateForOneRoot(animRoot.transform, permanentLayer);
    //        var sceneAssembly = Object.FindObjectOfType<SceneAssembly>();
    //        GameObject sceneAssemblyObj;
    //        if (sceneAssembly != null)
    //        {
    //            sceneAssemblyObj = sceneAssembly.gameObject;
    //            Object.DestroyImmediate(sceneAssembly);
    //        }
    //        else
    //            sceneAssemblyObj = new GameObject("Scene Assembly");
    //        sceneAssembly = sceneAssemblyObj.AddComponent<SceneAssembly>();
    //        sceneAssembly.sceneObjs = new List<SceneObjData>();
    //        sceneAssembly.sceneMeshes = new List<SceneMeshData>();
    //        var rootItems = _oldScene.GetRootGameObjects();
    //        var areaPath = sceneDisassembledPath.Open(_oldScene.name);
    //        if (!AssetDatabase.IsValidFolder(areaPath))
    //            AssetDatabase.CreateFolder(sceneDisassembledPath, _oldScene.name);
    //        AssetDatabase.Refresh();
    //        GameObject indieRoot = null;
    //        var areaRoots = new List<GameObject>();
    //        for (var i = 0; i < rootItems.Length; i++)
    //        {
    //            var rootItem = rootItems[i];
    //            if (rootItem.name == itemRootIndie)
    //                indieRoot = rootItem;
    //            else if (rootItem.name.StartsWith("Area_"))
    //                areaRoots.Add(rootItem);
    //            else
    //                SetStaticUntilAnimator(rootItem.transform);
    //        }
    //        if (indieRoot != null)
    //        {
    //            var index = 0;
    //            var total = indieRoot.transform.childCount;
    //            foreach (Transform child in indieRoot.transform)
    //            {
    //                EditorUtility.DisplayProgressBar(title, string.Format("Create Indie Item: {0} / {1}", index, total), (float)index / total);
    //                child.name = string.Format(itemIndieFormat, index);
    //                var meshRenderer = child.GetComponent<Renderer>();
    //                var bounds = meshRenderer.bounds;
    //                var indie = new SceneMeshData
    //                {
    //                    bundlePath = GetAreaBundleName(_oldScene.name, child.name),
    //                    assetName = child.name,
    //                    localPosition = child.localPosition,
    //                    localRotation = child.localRotation,
    //                    localScale = child.localScale,
    //                    lightmapIndex = meshRenderer.lightmapIndex,
    //                    lightmapScaleOffset = meshRenderer.lightmapScaleOffset,
    //                    bounds = new Circle { center = bounds.center.RemoveY(), radius = bounds.extents.RemoveY().magnitude }
    //                };
    //                sceneAssembly.sceneMeshes.Add(indie);
    //                PrefabUtility.CreatePrefab(areaPath.Open(child.gameObject.name + ".prefab"), child.gameObject);
    //                index++;
    //            }
    //            Object.DestroyImmediate(indieRoot);
    //        }
    //        for (var i = 0; i < areaRoots.Count; i++)
    //        {
    //            var rootItem = areaRoots[i];
    //            EditorUtility.DisplayProgressBar(title, string.Format("Create Area: {0} / {1}", i, areaRoots.Count), (float)i / areaRoots.Count);
    //            var bounds = new Bounds();
    //            GetAreaBounds(ref bounds, rootItem.transform);
    //            var area = new SceneObjData
    //            {
    //                bundlePath = GetAreaBundleName(_oldScene.name, rootItem.name),
    //                assetName = rootItem.name,
    //                localPosition = rootItem.transform.localPosition,
    //                localRotation = rootItem.transform.localRotation,
    //                localScale = rootItem.transform.localScale,
    //                bounds = new Circle { center = bounds.center.RemoveY(), radius = bounds.extents.RemoveY().magnitude }
    //            };
    //            if (area.bounds.radius > _itemAreaLength)
    //                Debug.LogWarning("Scene " + _oldScene.name + " " + rootItem.name + " has a radius of " + area.bounds.radius);
    //            sceneAssembly.sceneObjs.Add(area);
    //            rootItem.SetStaticRecursively(false);
    //            var areaComponent = rootItem.AddComponent<SceneArea>();
    //            areaComponent.InitAreaData();
    //            PrefabUtility.CreatePrefab(areaPath.Open(rootItem.name + ".prefab"), rootItem);
    //            Object.DestroyImmediate(rootItem);
    //        }
    //    }

    //    private static void SetStaticUntilAnimator(Transform transform)
    //    {
    //        var animator = transform.GetComponent<Animator>();
    //        if (animator == null)
    //        {
    //            transform.gameObject.isStatic = transform.GetComponent<ParticleSystem>() == null;
    //            foreach (Transform child in transform)
    //                SetStaticUntilAnimator(child);
    //        }
    //    }

    //    private void CreateForOneRoot(Transform root, int permanentLayer)
    //    {
    //        var childCount = root.childCount;
    //        for (var i = childCount - 1; i >= 0; i--)
    //        {
    //            var child = root.GetChild(i);
    //            if (child.gameObject.layer == permanentLayer)
    //                MoveAreaItemToParent(child, itemRootPermanent);
    //            else
    //            {
    //                var bounds = new Bounds();
    //                GetAreaBounds(ref bounds, child);
    //                CreateOneAreaItem(child, bounds);
    //            }
    //        }
    //        if (root.childCount <= 0)
    //            Object.DestroyImmediate(root.gameObject);
    //    }

    //    public static string GetAreaBundleName(string sceneName, string areaName)
    //    {
    //        sceneName = Path.GetFileNameWithoutExtension(sceneName);
    //        areaName = Path.GetFileNameWithoutExtension(areaName);
    //        return string.Format(sceneAreaBundleFormat, sceneName, areaName).ToLower();
    //    }
    //
    //    private void CreateOneAreaItem(Transform item, Bounds bounds)
    //    {
    //        // 检测物体尺寸是否应该作为独立物体
    //        var rootName = bounds.size.x > _indieItemLength || bounds.size.y > _indieItemLength ||
    //                     bounds.size.z > _indieItemLength
    //            ? itemRootIndie
    //            : PositionToAreaName(bounds.center);
    //        MoveAreaItemToParent(item, rootName);
    //    }

    //    private void MoveAreaItemToParent(Transform item, string rootName)
    //    {
    //            var rootObj = GameObject.Find(rootName);
    //            if (rootObj == null)
    //                rootObj = new GameObject(rootName);
    //        item.SetParent(rootObj.transform);
    //    }
    //
    //    private string PositionToAreaName(Vector3 position)
    //    {
    //        var x = Mathf.FloorToInt((position.x - _sceneClass.BasePosX) / _itemAreaLength);
    //        var z = Mathf.FloorToInt((position.z - _sceneClass.BasePosZ) / _itemAreaLength);
    //        // 防止负号导致超链接无法生成
    //        unchecked
    //        {
    //            return string.Format(itemRootAreaFormat, (uint)x, (uint)z);
    //        }
    //    }
    //
    //    private void GetAreaBounds(ref Bounds bounds, Transform parent)
    //    {
    //        Bounds? selfBounds = null;
    //        var particle = parent.GetComponent<ParticleSystem>();
    //        if (particle != null)
    //        {
    //            // 可以正确获得渲染边界
    //            particle.Simulate(1f);
    //            var particleRenderer = particle.GetComponent<ParticleSystemRenderer>();
    //            selfBounds = particleRenderer.bounds;
    //            var main = particle.main;
    //            main.playOnAwake = false;
    //        }
    //        else
    //        {
    //            var renderer = parent.GetComponent<Renderer>();
    //            if (renderer != null)
    //                selfBounds = renderer.bounds;
    //        }
    //
    //        if (selfBounds != null)
    //        {
    //            if (bounds.size.x <= 0f)
    //                bounds = selfBounds.Value;
    //            else
    //                bounds.Encapsulate(selfBounds.Value);
    //        }
    //
    //        foreach (Transform child in parent)
    //            GetAreaBounds(ref bounds, child);
    //    }
    #endregion

    #region 读取和写入源场景Hash值

    public const string sceneHashPath = "sceneHash.txt";

    public static string GetCurrentScenePath(string scenePath)
    {
        var filePath = Application.dataPath.MoveUp().Open(scenePath);
        var result = string.Empty;
        if (File.Exists(filePath))
        {
            var md5 = MD5.Create();
            byte[] hash;
            using (var fs = File.OpenRead(filePath))
                hash = md5.ComputeHash(fs);
            result = BitConverter.ToString(hash);
        }
        return result;
    }

    public static string GetLastSceneHash(string scenePath)
    {
        var hashList = GetHashRecord();
        var sceneName = Path.GetFileNameWithoutExtension(scenePath);
        var hash = string.Empty;
        for (var i = 0; i < hashList.Count; i++)
        {
            if (hashList[i].sceneName == sceneName)
            {
                hash = hashList[i].sceneHash;
                break;
            }
        }
        return hash;
    }

    public static void WriteSceneHash(string scenePath)
    {
        var hashList = GetHashRecord();
        var sceneName = Path.GetFileNameWithoutExtension(scenePath);
        var sceneHash = GetCurrentScenePath(scenePath);
        var add = true;
        for (var i = 0; i < hashList.Count; i++)
        {
            if (hashList[i].sceneName == sceneName)
            {
                sceneHash = hashList[i].sceneHash = sceneHash;
                add = false;
                break;
            }
        }
        if (add)
            hashList.Add(new SceneHashRecord(sceneName, sceneHash));
        var builder = new StringBuilder();
        for (var i = 0; i < hashList.Count; i++)
            hashList[i].AppendLine(builder);
        var filePath = Application.dataPath.MoveUp().Open(sceneDisassembledPath).Open(sceneHashPath);
        var diretory = Path.GetDirectoryName(filePath);
        if (!Directory.Exists(diretory))
            // ReSharper disable once AssignNullToNotNullAttribute
            Directory.CreateDirectory(diretory);
        File.WriteAllText(filePath, builder.ToString());
    }

    private static List<SceneHashRecord> GetHashRecord()
    {
        var hashList = new List<SceneHashRecord>();
        var filePath = Application.dataPath.MoveUp().Open(sceneDisassembledPath).Open(sceneHashPath);
        if (File.Exists(filePath))
        {
            var lines = File.ReadAllLines(filePath);
            for (var i = 0; i < lines.Length; i++)
            {
                var hash = SceneHashRecord.CreateFromLine(lines[i]);
                if (hash != null)
                    hashList.Add(hash);
            }
        }
        return hashList;
    }
    #endregion

    private enum SceneDisassemblyProcess
    {
        Open,
        Create,
        Disassemble,
        CreateLightData,
        CreateAreas,
        Save,
        End
    }

    private class SceneHashRecord
    {
        public string sceneName;
        public string sceneHash;

        public static SceneHashRecord CreateFromLine(string line)
        {
            SceneHashRecord result = null;
            if (!string.IsNullOrEmpty(line))
            {
                var segments = line.Split('\t');
                if (segments.Length == 2)
                    result = new SceneHashRecord(segments[0], segments[1]);
            }
            return result;
        }

        public void AppendLine(StringBuilder builder)
        {
            builder.AppendLine(sceneName + "\t" + sceneHash);
        }

        public SceneHashRecord(string sceneName, string sceneHash)
        {
            this.sceneName = sceneName;
            this.sceneHash = sceneHash;
        }
    }
}

public class DisassembleAllScenesAction
{
    private readonly List<string> _sceneList;
    private int _sceneIndex;
    private SceneDisassembly _current;
    
    public DisassembleAllScenesAction(List<string> sceneList)
    {
        _sceneList = sceneList;
    }
    
    public event UnityAction<bool, DisassembleAllScenesAction> onFinish;

    public void Start()
    {
        _sceneIndex = -1;
        // 2018.12.11 - RoleSelect不复制,而是直接使用源文件。之前流程会导致RoleSelect系列场景在Bundle中存在两份。
        // 预先处理RoleSelect系列的场景
        // 预先处理RoleSelect场景
        //var roleSelectSubPaths = (from assetPath in AssetDatabase.GetAllAssetPaths()
        //    where assetPath.StartsWith(SceneDisassembly.roleSelectDir + "/", StringComparison.OrdinalIgnoreCase)
        //    where Path.GetExtension(assetPath) == SceneDisassembly.sceneExtension
        //    select assetPath).ToArray();
        //for (var i = 0; i < roleSelectSubPaths.Length; i++)
        //{
        //    var assetPath = roleSelectSubPaths[i];
        //    var scenePath = SceneDisassembly.roleSelectTargetDir.Open(Path.GetFileName(assetPath));
        //    var lastHash = SceneDisassembly.GetLastSceneHash(assetPath);
        //    var currentHash = SceneDisassembly.GetCurrentScenePath(assetPath);
        //    if (lastHash != currentHash)
        //    {
        //        var roleSelect = EditorSceneManager.OpenScene(assetPath);
        //        BuiltinAssetBuilder.ReplaceForOneScene(roleSelect);
        //        EditorSceneManager.SaveScene(roleSelect, scenePath);
        //        SceneDisassembly.WriteSceneHash(assetPath);
        //    }
        //    else
        //        Debug.LogWarning("选角场景 " + assetPath + " Hash匹配,跳过复制流程!");
        //}
        OnCurrentFinish(true, null);
    }
    
    private void OnCurrentFinish(bool success, EditorPerFrameActionBase action)
    {
        var finish = false;
        if (success)
        {
            _sceneIndex++;
            if (_sceneIndex < _sceneList.Count)
            {
                var scenePath = SceneDisassembly.sceneRoot.Open(_sceneList[_sceneIndex]);
                _current = new SceneDisassembly(scenePath);
                _current.onFinish += OnCurrentFinish;
                _current.Start();
                Debug.LogWarning("Start Scene " + _sceneIndex + " to " + scenePath);
            }
            else
            {
                finish = true;
            }
        }
        else
        {
            finish = true;
        }

        if (finish)
        {
            _current = null;
            if (success)
            {
                AssetDatabase.SaveAssets();
                AssetDatabase.Refresh();
                Debug.LogWarning("All Scene Dissemble Finish");
            }
            else
                Debug.LogError("Error in Scene Dissemble");
            if (onFinish != null)
                onFinish(success, this);
        }
    }
}