using System.Runtime.InteropServices;
using System.Text;

using Unity.Profiling;

using UnityEngine;
using UnityEngine.Profiling;

#if UNITY_WEBGL || UNITY_EDITOR
public class WXProfileStatsScript : MonoBehaviour
{
    private string statsText;
#if UNITY_2021_2_OR_NEWER
    // private ProfilerRecorder m_totalUsedMemoryRecorder;
    // private ProfilerRecorder m_totalReservedMemoryRecorder;
    // private ProfilerRecorder m_gcUsedMemoryRecorder;
    // private ProfilerRecorder m_gcReservedMemoryRecorder;
    // private ProfilerRecorder m_gfxUsedMemoryRecorder;
    // private ProfilerRecorder m_gfxReservedMemoryRecorder;

    // private ProfilerRecorder m_systemUsedMemoryRecorder;
    // private ProfilerRecorder m_textureCountRecorder;
    // private ProfilerRecorder m_textureMemoryRecorder;
    // private ProfilerRecorder m_meshCountRecorder;
    // private ProfilerRecorder m_meshMemoryRecorder;
    // private ProfilerRecorder m_materialCountRecorder;
    // private ProfilerRecorder m_materialMemoryRecorder;
    // private ProfilerRecorder m_animationClipCountRecorder;
    // private ProfilerRecorder m_animationClipMemoryRecorder;

    // private ProfilerRecorder m_assetCountRecorder;
    // private ProfilerRecorder m_gameObjectsInScenesRecorder;
    // private ProfilerRecorder m_totalObjectsInScenesRecorder;
    // private ProfilerRecorder m_totalUnityObjectCountRecorder;
    // private ProfilerRecorder m_gcAllocationInFrameCountRecorder;
    // private ProfilerRecorder m_gcAllocatedInFrameRecorder;
    private ProfilerRecorder m_setPassCallsRecorder;
    private ProfilerRecorder m_drawCallsRecorder;
    private ProfilerRecorder m_verticesRecorder;
#endif
    private int m_fpsCount;
    private float m_fpsDeltaTime;
    private int fps;
    private GUIStyle m_bgStyle;
    private bool m_isShow = true;
    private System.Collections.Generic.Dictionary<string, ProfValue> profValues = new System.Collections.Generic.Dictionary<string, ProfValue>();

    public void Awake()
    {
        m_bgStyle = new GUIStyle();
        m_bgStyle.normal.background = Texture2D.whiteTexture;
    }

    public void OnEnable()
    {
#if UNITY_2021_2_OR_NEWER
        // m_totalUsedMemoryRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Total Used Memory");
        // m_totalReservedMemoryRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Total Reserved Memory");
        // m_gcUsedMemoryRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "GC Used Memory");
        // m_gcReservedMemoryRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "GC Reserved Memory");
        // m_gfxUsedMemoryRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Gfx Used Memory");
        // m_gfxReservedMemoryRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Gfx Reserved Memory");
        // m_systemUsedMemoryRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "System Used Memory");
        // m_textureCountRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Texture Count");
        // m_textureMemoryRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Texture Memory");
        // m_meshCountRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Mesh Count");
        // m_meshMemoryRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Mesh Memory");
        // m_materialCountRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Material Count");
        // m_materialMemoryRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Material Memory");
        // m_animationClipCountRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "AnimationClip Count");
        // m_animationClipMemoryRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "AnimationClip Memory");
        // m_assetCountRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Asset Count");
        // m_gameObjectsInScenesRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "GameObject Count");
        // m_totalObjectsInScenesRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Scene Object Count");
        // m_totalUnityObjectCountRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Object Count");
        // m_gcAllocationInFrameCountRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "GC Allocation In Frame Count");
        // m_gcAllocatedInFrameRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "GC Allocated In Frame");
        m_setPassCallsRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Render, "SetPass Calls Count");
        m_drawCallsRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Render, "Draw Calls Count");
        m_verticesRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Render, "Vertices Count");
#endif

    }

    public void OnDisable()
    {
#if UNITY_2021_2_OR_NEWER
        // m_totalUsedMemoryRecorder.Dispose();
        // m_totalReservedMemoryRecorder.Dispose();
        // m_gcUsedMemoryRecorder.Dispose();
        // m_gcReservedMemoryRecorder.Dispose();
        // m_gfxUsedMemoryRecorder.Dispose();
        // m_gfxReservedMemoryRecorder.Dispose();
        // m_systemUsedMemoryRecorder.Dispose();
        // m_textureCountRecorder.Dispose();
        // m_textureMemoryRecorder.Dispose();
        // m_meshCountRecorder.Dispose();
        // m_meshMemoryRecorder.Dispose();
        // m_materialCountRecorder.Dispose();
        // m_materialMemoryRecorder.Dispose();
        // m_animationClipCountRecorder.Dispose();
        // m_animationClipMemoryRecorder.Dispose();
        // m_assetCountRecorder.Dispose();
        // m_gameObjectsInScenesRecorder.Dispose();
        // m_totalObjectsInScenesRecorder.Dispose();
        // m_totalUnityObjectCountRecorder.Dispose();
        // m_gcAllocationInFrameCountRecorder.Dispose();
        // m_gcAllocatedInFrameRecorder.Dispose();
        m_setPassCallsRecorder.Dispose();
        m_drawCallsRecorder.Dispose();
        m_verticesRecorder.Dispose();

#endif
    }

    public class ProfValue
    {
        public float current;
        public float max = 0;
        public float min = 9999;

        // public int avrage;
    }

    public ProfValue UpdateValue(string key, float value, StringBuilder sb, string format = "0")
    {
        ProfValue profValue = null;
        if (!profValues.TryGetValue(key, out profValue))
        {
            profValue = new ProfValue();
            profValues.Add(key, profValue);
        }

        profValue.current = value;
        profValue.max = value > profValue.max ? value : profValue.max;
        profValue.min = value < profValue.min ? value : profValue.min;
        sb.AppendLine($"{key}:[{profValue.current.ToString(format)}, {profValue.min.ToString(format)}, {profValue.max.ToString(format)}]");
        return profValue;
    }

    public void Update()
    {
        UpdateFps();
        const uint toMB = 1024 * 1024;
        var sb = new StringBuilder(500);
        sb.AppendLine($"-------------FPS---------------");

        // var key = "targetFrameRate";
        UpdateValue("TargetFramerate", Application.targetFrameRate, sb);
        UpdateValue("FPS", fps, sb);
        UpdateValue("FrameTime(ms)", WeChatWASM.WXSDKManagerHandler.Instance.GetEXFrameTime(), sb, "0.00");

        sb.AppendLine($"-------------Profiler------------");

        UpdateValue("MonoHeapReserved", Profiler.GetMonoHeapSizeLong() / toMB, sb);
        UpdateValue("MonoHeapUsed", Profiler.GetMonoUsedSizeLong() / toMB, sb);

        // UpdateValue("Graphics", Profiler.GetAllocatedMemoryForGraphicsDriver() / toMB, sb);

        // UpdateValue("TempAllocator", Profiler.GetTempAllocatorSize() / toMB, sb);
        UpdateValue("NativeReserved", Profiler.GetTotalReservedMemoryLong() / toMB, sb);
        UpdateValue("NativeUnused", Profiler.GetTotalUnusedReservedMemoryLong() / toMB, sb);
        UpdateValue("NativeAllocated", Profiler.GetTotalAllocatedMemoryLong() / toMB, sb);

#if UNITY_2021_2_OR_NEWER
        sb.AppendLine("-------------Render------------");
        UpdateValue("SetPass Calls", m_setPassCallsRecorder.LastValue, sb);
        UpdateValue("Draw Calls", m_drawCallsRecorder.LastValue, sb);
        UpdateValue("Vertices", m_verticesRecorder.LastValue, sb);
#endif
        sb.AppendLine("-------------WebAssembly----------");
        UpdateValue("TotalHeapMemory", WeChatWASM.WXSDKManagerHandler.Instance.GetTotalMemorySize() / toMB, sb);
        UpdateValue("DynamicMemory", WeChatWASM.WXSDKManagerHandler.Instance.GetDynamicMemorySize() / toMB, sb);
        UpdateValue("UsedHeap(ProfilingMemory only)", WeChatWASM.WXSDKManagerHandler.Instance.GetUsedMemorySize() / toMB, sb);
        UpdateValue("UnAllocatedMemory", WeChatWASM.WXSDKManagerHandler.Instance.GetUnAllocatedMemorySize() / toMB, sb);

        sb.AppendLine("-------------AssetBundle----------");
        UpdateValue("NumberInMemory", WeChatWASM.WXSDKManagerHandler.Instance.GetBundleNumberInMemory(), sb);
        UpdateValue("NumberOnDisk", WeChatWASM.WXSDKManagerHandler.Instance.GetBundleNumberOnDisk(), sb);
        UpdateValue("SizeInMemory", WeChatWASM.WXSDKManagerHandler.Instance.GetBundleSizeInMemory() / toMB, sb);
        UpdateValue("SizeOnDisk", WeChatWASM.WXSDKManagerHandler.Instance.GetBundleSizeOnDisk() / toMB, sb);

#if UNITY_2021_2_OR_NEWER
        // sb.AppendLine("-------------MemoryRecorder-----");
        // UpdateValue("Total Used Memory", m_totalUsedMemoryRecorder.LastValue / toMB, sb);
        // UpdateValue("Total Reserved Memory", m_totalReservedMemoryRecorder.LastValue / toMB, sb);
        // UpdateValue("GC Used Memory", m_gcUsedMemoryRecorder.LastValue / toMB, sb);
        // UpdateValue("GC Reserved Memory", m_gcReservedMemoryRecorder.LastValue / toMB, sb);
        // UpdateValue("Gfx Used Memory", m_gfxUsedMemoryRecorder.LastValue / toMB, sb);
        // UpdateValue("Gfx Reserved Memory", m_gfxReservedMemoryRecorder.LastValue / toMB, sb);
        // UpdateValue("System Used Memory", m_systemUsedMemoryRecorder.LastValue / toMB, sb);
        // UpdateValue("Texture Count", m_textureCountRecorder.LastValue, sb);
        // UpdateValue("Texture Memory", m_textureMemoryRecorder.LastValue / toMB, sb);
        // UpdateValue("Mesh Count", m_meshCountRecorder.LastValue, sb);
        // UpdateValue("Mesh Memory", m_meshMemoryRecorder.LastValue / toMB, sb);
        // UpdateValue("Material Count", m_materialCountRecorder.LastValue, sb);
        // UpdateValue("Material Memory", m_materialMemoryRecorder.LastValue / toMB, sb);
        // UpdateValue("AnimationClip Count", m_animationClipCountRecorder.LastValue, sb);
        // UpdateValue("AnimationClip Memory", m_animationClipMemoryRecorder.LastValue / toMB, sb);
        // UpdateValue("Asset Count", m_assetCountRecorder.LastValue, sb);
        // UpdateValue("GameObject Count", m_gameObjectsInScenesRecorder.LastValue, sb);
        // UpdateValue("Scene Object Count", m_totalObjectsInScenesRecorder.LastValue, sb);
        // UpdateValue("Object Count", m_totalUnityObjectCountRecorder.LastValue, sb);
        // UpdateValue("GC Allocation In Frame Count", m_gcAllocationInFrameCountRecorder.LastValue, sb);
        // UpdateValue("GC Allocated In Frame", m_gcAllocatedInFrameRecorder.LastValue / toMB, sb);
#endif
        statsText = sb.ToString();
    }

    public void UpdateFps()
    {
        m_fpsCount++;
        m_fpsDeltaTime += Time.deltaTime;

        if (m_fpsCount % 60 == 0)
        {
            m_fpsCount = 1;
            fps = (int)Mathf.Ceil(60.0f / m_fpsDeltaTime);
            m_fpsDeltaTime = 0;
        }
    }

    public void OnGUI()
    {
        GUI.backgroundColor = new Color(0, 0, 0, 0.5f);
#if UNITY_EDITOR
        GUI.skin.button.fontSize = 10;
        GUI.skin.label.fontSize = 10;
#else
        GUI.skin.button.fontSize = 30;
        GUI.skin.label.fontSize = 30;
#endif
        if (GUILayout.Button("Performence Stats", GUILayout.ExpandWidth(false)))
        {
            m_isShow = !m_isShow;
        }

        if (GUILayout.Button("ProfilingMemory Dump", GUILayout.ExpandWidth(false)))
        {
            WeChatWASM.WXSDKManagerHandler.Instance.ProfilingMemoryDump();
        }

        GUILayout.BeginVertical(m_bgStyle);
        if (m_isShow)
        {
            GUILayout.Label(statsText);
        }

        GUILayout.EndVertical();
    }

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    private static void OnGameLaunch()
    {
        WeChatWASM.WXSDKManagerHandler.SetProfileStatsScript(typeof(WXProfileStatsScript));
    }
}
#endif
