using System.Collections.Generic;
using Games.LogicObj;
using Module.Log;
using UnityEngine;
using UnityEngine.Rendering;

/// <summary>
/// 用于渲染透视效果的特殊渲染器
/// </summary>
public class XRayRenderer : MonoBehaviour
{
    private static Material _xRayMaterial;
    private const string _xRayMaterialName = "PlayerXRay";

    private List<MyTuple<SkinnedMeshRenderer, SkinnedMeshRenderer>> _skinnedMeshRendererList;
    private List<MeshRenderer> _meshRendererList;
    
    // 注:不要修改为Start,需要保证在有子节点绑定前配置完成
    public void Init(List<RendererInfo> rendererInfos)
    {
        for (var i = 0; i < rendererInfos.Count; i++)
        {
            var cachedRenderer = rendererInfos[i].CachedRenderer;
            if (cachedRenderer == null ||
                cachedRenderer.sharedMaterial == null ||
                cachedRenderer.sharedMaterial.shader == null ||
                !cachedRenderer.sharedMaterial.shader.IsCharacterShader())
                continue;
            var meshRenderer = rendererInfos[i].CachedRenderer as MeshRenderer;
            if (meshRenderer == null)
            {
                var skinRenderer = rendererInfos[i].CachedRenderer as SkinnedMeshRenderer;
                if (skinRenderer != null)
                {
                    if (_skinnedMeshRendererList == null)
                        _skinnedMeshRendererList = new List<MyTuple<SkinnedMeshRenderer, SkinnedMeshRenderer>>();
                    _skinnedMeshRendererList.Add(
                        new MyTuple<SkinnedMeshRenderer, SkinnedMeshRenderer>(skinRenderer,
                            CreateFromSkinnedRenderer(skinRenderer)));
                }
            }
            else
            {
                var meshFilter = meshRenderer.GetComponent<MeshFilter>();
                if (meshFilter != null)
                {
                    if (_meshRendererList == null)
                        _meshRendererList = new List<MeshRenderer>();

                    _meshRendererList.Add(CreateFromMeshRenderer(meshRenderer, meshFilter));
                }
                #if UNITY_EDITOR
                else
                    LogModule.ErrorLog(string.Format("MeshRenderer没有对应的MeshFilter,于物体{0}", meshRenderer.transform.GetHierarchyName()));
                #endif
            }
        }
    }

    public void TrimXRaRenderer(Transform root)
    {
        if (root != null)
        {
            if (_skinnedMeshRendererList != null)
            {
                for (var i = _skinnedMeshRendererList.Count - 1; i >= 0; i--)
                {
                    if (_skinnedMeshRendererList[i].first == null ||
                        _skinnedMeshRendererList[i].first.transform.IsChildOf(root))
                        _skinnedMeshRendererList.RemoveAt(i);
                }
            }

            if (_meshRendererList != null)
            {
                for (var i = _meshRendererList.Count - 1; i >= 0; i--)
                {
                    if (_meshRendererList[i] == null || _meshRendererList[i].transform.IsChildOf(root))
                        _meshRendererList.RemoveAt(i);
                }
            }
        }
    }
    
    public void ResetBones()
    {
        if (_skinnedMeshRendererList != null)
            for (var i = _skinnedMeshRendererList.Count - 1; i >= 0; i--)
            {
                SetBones(_skinnedMeshRendererList[i].first, _skinnedMeshRendererList[i].second);
                _skinnedMeshRendererList[i].second.gameObject.SetActive(true);
            }
        if (_meshRendererList != null)
            for (var i = 0; i < _meshRendererList.Count; i++)
                _meshRendererList[i].gameObject.SetActive(true);
    }

    public void Hide()
    {
        if (_skinnedMeshRendererList != null)
            for (var i = 0; i < _skinnedMeshRendererList.Count; i++)
                _skinnedMeshRendererList[i].second.gameObject.SetActive(false);
        if (_meshRendererList != null)
            for (var i = 0; i < _meshRendererList.Count; i++)
                _meshRendererList[i].gameObject.SetActive(false);
    }
    
    private static MeshRenderer CreateFromMeshRenderer(MeshRenderer sourceRenderer, MeshFilter sourceFilter)
    {
        // 注:可能有些物体会拥有初始动画
        var xRayObject = BuildXRayObject(sourceRenderer);
        var targetFilter = xRayObject.AddComponent<MeshFilter>();
        var targetRenderer = xRayObject.AddComponent<MeshRenderer>();
        targetFilter.sharedMesh = sourceFilter.sharedMesh;
        SetRendererParameters(sourceRenderer, targetRenderer);
        return targetRenderer;
    }

    private static SkinnedMeshRenderer CreateFromSkinnedRenderer(SkinnedMeshRenderer sourceRenderer)
    {
        var xRayObject = BuildXRayObject(sourceRenderer);
        var targetRenderer = xRayObject.AddComponent<SkinnedMeshRenderer>();
        SetBones(sourceRenderer, targetRenderer);
        targetRenderer.sharedMesh = sourceRenderer.sharedMesh;
        SetRendererParameters(sourceRenderer, targetRenderer);
        return targetRenderer;
    }

    private static void SetBones(SkinnedMeshRenderer sourceRenderer, SkinnedMeshRenderer targetRenderer)
    {
        targetRenderer.rootBone = sourceRenderer.rootBone;
        targetRenderer.bones = sourceRenderer.bones;
    }

    private static GameObject BuildXRayObject(Component root)
    {
        var xRayObject = new GameObject("XRayObject");
        xRayObject.transform.SetParent(root.transform);
        xRayObject.transform.localPosition = Vector3.zero;
        xRayObject.transform.localRotation = Quaternion.identity;
        xRayObject.transform.localScale = Vector3.one;
        return xRayObject;
    }

    private static void SetRendererParameters(Renderer source, Renderer target)
    {
        if (_xRayMaterial == null)
            _xRayMaterial = CommonUtility.LoadSharedMaterial(_xRayMaterialName);
        target.shadowCastingMode = ShadowCastingMode.Off;
        target.receiveShadows = false;
        var materials = new Material[source.sharedMaterials.Length];
        for (var i = 0; i < materials.Length; i++)
            materials[i] = _xRayMaterial;
        target.sharedMaterials = materials;
        target.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion;
    }
}