using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

[ExecuteInEditMode]
public class TestSky : MonoBehaviour
{
    public bool m_isSetEnv = false;
    public bool m_isTimeRun = false;
    public float m_timeScale = 1.0f;

    public bool m_setLightmap = false;


    //原本想使用 ComputeShader 来处理过度，但是ComputeShader需要GL 3.1以上,所以最终采用RT的方式渲染
    public Shader m_lightmapLerpShader = null;
    public Material m_lightmapLerpMaterl = null;
    public float m_lerpT = 1.0f;

    public int m_lightmapCount = 2;
    public RenderTexture[] m_lightmapRTs = null;
    public Material[] m_rtMaterls = null;
    public Camera m_rtCam = null;

    private CommandBuffer m_lightmapLerpCommandBuf = null;
    

    [System.Serializable]
    public class EnviormmentSettings
    {
        public string m_name;
        public float m_time;
        public float m_beginTime; //时间
        public float m_endTime;

        public Color m_sunColor;
        public Vector3 m_sunDir;
        public Color m_ambientColor;

        public Color m_skyColor;
        public float m_skyExposure;

        public bool m_useLightMaps = true;
        public Texture2D[] m_lightMaps;

        [HideInInspector]
        public LightmapData[] m_lightData;
    }

    public Material m_skyMaterl;
    public Shader m_skyShader;

    public float m_curTime = 0;
    public EnviormmentSettings m_curEnvSettings;
    public EnviormmentSettings m_lastEnvSettings;
    public List<EnviormmentSettings> m_envs;

    [Space(10)]
    private List<EnviormmentSettings> m_runEnvs = null;

    // Start is called before the first frame update
    void Start()
    {
        m_rtCam = GetComponent<Camera>();
    }

    void initCommandBuf()
    {
        if (m_lightmapLerpCommandBuf != null)
        {
            return;
        }

        m_lightmapLerpCommandBuf = new CommandBuffer();
        m_lightmapLerpCommandBuf.name = "LightmapLerp";

        m_lightmapRTs = new RenderTexture[m_lightmapCount];
        RenderTargetIdentifier[] rtbuffersID = new RenderTargetIdentifier[m_lightmapCount];
        m_rtMaterls = new Material[m_lightmapCount];

        for (int i = 0; i < m_lightmapCount; ++i)
        {
            m_lightmapRTs[i] = RenderTexture.GetTemporary(m_curLightMaps[i].width, m_curLightMaps[i].height, 0, RenderTextureFormat.ARGB32);
            rtbuffersID[i] = new RenderTargetIdentifier(m_lightmapRTs[i]);

            m_rtMaterls[i] = new Material(m_lightmapLerpMaterl);

            m_lightmapLerpCommandBuf.Blit(BuiltinRenderTextureType.CurrentActive, rtbuffersID[i], m_rtMaterls[i]);
        }
        m_rtCam.AddCommandBuffer(CameraEvent.AfterForwardOpaque, m_lightmapLerpCommandBuf);
    }

    // Update is called once per frame
    [ContextMenu("applyEnv")]
    void Update()
    {
        m_skyMaterl = RenderSettings.skybox;
        if (m_skyMaterl)
        {
            m_skyShader = m_skyMaterl.shader;
        }

        if (m_isTimeRun)
        {
            m_curTime += Time.deltaTime * m_timeScale;

            if (m_curTime < 0.0f)
            {
                m_curTime = 24.0f + m_curTime;
            }

            if (m_curTime > 24.0f)
            {
                m_curTime = 0.0f;
            }
        }

        EnviormmentSettings curEnv = null;
        EnviormmentSettings lastEnv = null;

        float minTime = 66f;
        if (m_envs != null)
        {
            m_runEnvs = m_envs;
            m_runEnvs.Sort(
                (a, b) => 
                {
                    return a.m_time > b.m_time ? 1 : -1;
                }
            );

            lastEnv = curEnv = m_runEnvs[m_runEnvs.Count - 1];

            int idx = -1;
            for (int i = 0; i < m_runEnvs.Count; ++i)
            {
                var env = m_runEnvs[i];
                float iv = m_curTime - env.m_time;
                if (Mathf.Abs(minTime) > Mathf.Abs(iv))
                {
                    minTime = iv;
                    curEnv = env;
                    idx = i;
                }
            }

            if(minTime > 0)
            {
                idx = idx + 1;
                if(idx >= m_runEnvs.Count)
                {
                    idx = 0;
                }
            }

            curEnv = m_runEnvs[idx];
            idx = idx - 1;
            if (idx < 0)
            {
                idx = m_runEnvs.Count - 1;
            }
            lastEnv = m_runEnvs[idx];
        }

        m_curEnvSettings = curEnv;
        m_lastEnvSettings = lastEnv;
    
        if (m_isSetEnv)
        {
            applyEnv();
        }
    }

    public Texture2D[] m_curLightMaps = null;
    public Texture2D[] m_lastLightMaps = null;

    private LightmapData[] m_curLightData = null;
    public Stack<Texture2D> m_lightLerpTex2DPool = new Stack<Texture2D>();

    public Texture2D getNewLightMapLerpTexture2D(int w, int h, TextureFormat tf)
    {
        if (m_lightLerpTex2DPool.Count == 0)
        {
            var tex = new Texture2D(w, h, tf, false, true);
            return tex;
        }

        return m_lightLerpTex2DPool.Pop();
    }

    public void relaseLightMapLerpTex2D(ref Texture2D tex)
    {
        if (tex == null)
        {
            return;
        }

        m_lightLerpTex2DPool.Push(tex);
        tex = null;
    }


    void lightMapLerp(float t)
    {
        if (m_curLightData != null)
        {
            for (int i = 0; i < m_curLightData.Length; ++i)
            {
                if (m_curLightData[i] != null)
                {
                    var tex = m_curLightData[i].lightmapColor;
                    relaseLightMapLerpTex2D(ref tex);
                }
            }
        }
        else
        {
            m_curLightData = new LightmapData[m_curLightMaps.Length];
        }

        m_curLightData = new LightmapData[m_curLightMaps.Length];
        for(int i = 0; i < m_curLightData.Length; ++i)
        {
            if (m_curLightData[i] == null)
            {
                m_curLightData[i] = new LightmapData();
            }

            var curTex = m_curLightMaps[i];
            var tex = getNewLightMapLerpTexture2D(curTex.width, curTex.height, curTex.format);

            var lastTex = m_lastLightMaps[i];

            var curTexClrs = curTex.GetPixels();
            var lastTexClrs = lastTex.GetPixels();
            var texClrs = tex.GetPixels();

            for (int idx = 0; idx < curTexClrs.Length; ++idx)
            {
                texClrs[idx] = Color.Lerp(lastTexClrs[idx], curTexClrs[idx], t);
            }
            tex.SetPixels(texClrs);
            tex.Apply();

            m_curLightData[i].lightmapColor = tex;
        }
    }

    bool m_useLerpTexture = false;
    [ContextMenu("applyEnv")]
    public void applyEnv()
    {
        if(m_lastEnvSettings == null || m_curEnvSettings == null)
        {
            return;
        }

        float time = m_curEnvSettings.m_time;
        float curTime = m_curTime;
        if (m_curEnvSettings == m_runEnvs[0])
        {
            time += 24;
            if (curTime <= m_curEnvSettings.m_time)
            {
                curTime += 24;
            }
        }

        float iv = time - m_lastEnvSettings.m_time;
        float t = (curTime - m_lastEnvSettings.m_time) / iv;
        float v = Mathf.Lerp(0.0f, 1.0f, t);

        var sun = RenderSettings.sun;
        sun.color = Color.Lerp(m_lastEnvSettings.m_sunColor, m_curEnvSettings.m_sunColor, v);
        sun.transform.localEulerAngles = Vector3.Lerp(m_lastEnvSettings.m_sunDir, m_curEnvSettings.m_sunDir, v);
        
        var skyBox = RenderSettings.skybox;
        skyBox.SetColor("_Tint", Color.Lerp(m_lastEnvSettings.m_skyColor, m_curEnvSettings.m_skyColor, v));
        skyBox.SetFloat("_Exposure", Mathf.Lerp(m_lastEnvSettings.m_skyExposure, m_curEnvSettings.m_skyExposure, v));

        RenderSettings.ambientSkyColor = Color.Lerp(m_lastEnvSettings.m_ambientColor, m_curEnvSettings.m_ambientColor, v);

        if (!m_setLightmap)
        {
            return;
        }

        bool isSetLightmap = false;
        //替换lightmap
        if (m_lastEnvSettings.m_useLightMaps && m_lastEnvSettings.m_lightMaps != null && m_lastEnvSettings.m_lightMaps.Length > 0)
        {
            m_lastLightMaps = m_lastEnvSettings.m_lightMaps;
            isSetLightmap = true;
        }

        if (m_curEnvSettings.m_useLightMaps && m_curEnvSettings.m_lightMaps != null && m_curEnvSettings.m_lightMaps.Length > 0)
        {
            m_curLightMaps = m_curEnvSettings.m_lightMaps;
            isSetLightmap = true;
        }

        bool isLerpMap = true;
        for(int i = 0; i < m_curLightMaps.Length; ++i)
        {
            if(m_curLightMaps[i] == m_lastLightMaps[i])
            {
                isLerpMap = false;
            }
        }

        if (isLerpMap && m_lastLightMaps != null && m_lastLightMaps.Length > 0 && m_curLightMaps != null && m_curLightMaps.Length > 0)
        {
            m_lightmapCount = m_curLightMaps.Length;
            initCommandBuf();
            if (m_curLightData == null || m_curLightData.Length != m_curLightMaps.Length)
            {
                m_curLightData = new LightmapData[m_curLightMaps.Length];
            }

            for (int i = 0; i < m_lightmapCount; ++i)
            {
                m_rtMaterls[i].SetFloat("_LerpValue", t);
                m_rtMaterls[i].SetTexture("_TargetTex", m_curLightMaps[i]);
                m_rtMaterls[i].SetTexture("_LastTex", m_lastLightMaps[i]);

                if (m_curLightData[i] == null)
                {
                    m_curLightData[i] = new LightmapData();
                }
                else
                {
                    if (m_useLerpTexture)
                    {
                        var tex2 = m_curLightData[i].lightmapColor;
                        relaseLightMapLerpTex2D(ref tex2);
                    }
                }

                var origRT = RenderTexture.active;
                RenderTexture.active = m_lightmapRTs[i];
                var tex = getNewLightMapLerpTexture2D(RenderTexture.active.width, RenderTexture.active.height, TextureFormat.ARGB32);
                tex.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0);
                tex.Apply();
                RenderTexture.active = origRT;
                m_curLightData[i].lightmapColor = tex;
            }

            m_useLerpTexture = true;

            //lightMapLerp(v);
        }
        else
        {
            if (m_curLightMaps != null)
            {

                if (m_useLerpTexture)
                {
                    for (int i = 0; i < m_curLightData.Length; ++i)
                    {
                        var tex2 = m_curLightData[i].lightmapColor;
                        relaseLightMapLerpTex2D(ref tex2);
                    }
                }

                if (m_curLightData == null || m_curLightData.Length != m_curLightMaps.Length)
                {
                    m_curLightData = new LightmapData[m_curLightMaps.Length];
                }

                for (int i = 0; i < m_curLightMaps.Length; ++i)
                {
                    if (m_curLightData[i] == null)
                    {
                        m_curLightData[i] = new LightmapData();
                    }
                    m_curLightData[i].lightmapColor = m_curLightMaps[i];
                }
            }

            m_useLerpTexture = false;
        }

        if (isSetLightmap)
        {
            LightmapSettings.lightmaps = m_curLightData;
        }

        return;
        //m_curLightData = LightmapSettings.lightmaps;


        //var meshs = GameObject.FindObjectsOfType<MeshRenderer>();
        //foreach(var mesh in meshs)
        //{
        //    if (mesh.gameObject.isStatic)
        //    {
        //    }
        //}
        
        if (m_curEnvSettings.m_useLightMaps && m_curEnvSettings.m_lightMaps != null && m_curEnvSettings.m_lightMaps.Length == LightmapSettings.lightmaps.Length)
        {
            if (m_curEnvSettings.m_lightData == null)
            {
                m_curEnvSettings.m_lightData = new LightmapData[m_curEnvSettings.m_lightMaps.Length];
                for (int i = 0; i < m_curEnvSettings.m_lightMaps.Length; ++i)
                {
                    m_curEnvSettings.m_lightData[i] = new LightmapData();
                    m_curEnvSettings.m_lightData[i].lightmapColor = m_curEnvSettings.m_lightMaps[i];
                }
            }

            LightmapSettings.lightmaps = m_curEnvSettings.m_lightData;

            //for (int i = 0; i < LightmapSettings.lightmaps.Length; ++i)
            //{
            //    LightmapSettings.lightmaps[i].lightmapColor = m_curEnvSettings.m_lightMaps[i];
            //}
        }
    }
}
