using System.Collections;
using System.Collections.Generic;
using System.IO;
using System;
using UnityEngine;

using Animrig;

#if UNITY_EDITOR
using UnityEditor;
#endif

namespace Animrig.Unity
{
    [ExecuteInEditMode]
    [AddComponentMenu("Animationrig/AnimationrigState")]
    public class AnimationrigStateComponent: MonoBehaviour
    {
        // property
        public AnimationrigState State { get; protected set; } = null;
        public Animationrig Data { get; protected set; } = null;
        public Preset Preset { get; protected set; } = null;

        [SerializeField] protected TextAsset fAnimrigTextAsset = null;
        public TextAsset AnimrigBinaryData
        {
            get
            {
                return fAnimrigTextAsset;
            }
            set
            {
                if (fAnimrigTextAsset == value) return;
                fAnimrigTextAsset = value;
                SetupAnimrig();
            }
        }
        [SerializeField] protected TextAsset fPresetTextAsset = null;
        public TextAsset PresetBinaryData
        {
            get
            {
                return fPresetTextAsset;
            }
            set
            {
                if (fPresetTextAsset == value) return;
                fPresetTextAsset = value;
                SetupPreset();
            }
        }
		[SerializeField] public bool AutoUpdate = true;
		[SerializeField] public bool UpdateOnce = true;
		[SerializeField] public List<GameObject> RendererObjects = new List<GameObject>(4);
        // unity
        private void Awake()
        {
            Rebuild();
        }
        void Update()
        {
#if UNITY_EDITOR
            if (!Application.isPlaying)
            {
				// if (AutoUpdate) UpdateState(0.0f,UpdateOnce);
				return;
            }
#endif
			if (AutoUpdate) UpdateState(Time.deltaTime, UpdateOnce);
        }

        // protected
        protected void SetupAnimrig()
        {
            State = null;
            Data = null;

            if (fAnimrigTextAsset == null) return;

            // load data
            {
                Data = new Animationrig();
                bool b;
                using (var ms = new MemoryStream(fAnimrigTextAsset.bytes))
                {
                    ms.Position = 0;
                    b = Data.LoadFromBinary(ms);
                }
                if (!b)
                {
                    Data = null;
                    new Exception("AnimationrigUnityCompo : can not load animationrig data.");
                }
            }

            // setup state
            {
                State = new AnimationrigState(Data);
            }

            SetRendererCount();
        }
        protected List<string> LoadStringListFromTextAsset(TextAsset ta)
        {
            var sl = new List<string>();
            string s = "";
            foreach (var c in ta.text)
            {
                if (c == '\r') continue;
                if (c == '\n')
                {
                    sl.Add(s);
                    s = "";
                    continue;
                }
                s = s + c;
            }
            if (s != "") sl.Add(s);

            return sl;
        }
        protected void SetupPreset()
        {
            Preset = null;

            if (fPresetTextAsset == null) return;

            {
                Preset = new Preset();
                var sl = LoadStringListFromTextAsset(fPresetTextAsset);
                Preset.MaskFromStringList(sl);
                // binary mode. not necessary on unity.
                /* 
                bool b;
                using (var ms = new MemoryStream(fPresetTextAsset.bytes))
                {
                    ms.Position = 0;
                    b = Preset.LoadFromStream(ms);
                }
                if (!b)
                {
                    Preset = null;
                    new Exception("AnimationrigUnityCompo : can not load preset data.");
                }
                */
            }
        }
        // public
        public AnimationrigStateComponent()
            : base()
        {
            Clear();
        }

        public void Clear()
        {
            fAnimrigTextAsset = null;
            fPresetTextAsset = null;

            SetupAnimrig();
            SetupPreset();
            SetRendererCount();
        }
        public void Rebuild()
        {
            SetupAnimrig();
            SetupPreset();
        }
		public void UpdateState(double deltatime, bool updateonce=true)
		{
			if (Data == null) return;
			if (State == null) return;

			// setup input renderer
			for (var i = 0; i < State.InputCount; i++)
			{
				if (i < RendererObjects.Count)
				{
					var g = RendererObjects[i];
					if (g != null)
					{
						var c = g.GetComponent<IUnityStateRenderer>();
						if (c != null)
						{
							c.UpdateStateRenderer(Data.Objects[i]);
							State.Inputs[i].Renderer = c.StateRenderer;
							continue;
						}
					}
				}

				State.Inputs[i].Renderer = null;
			}

			// update
			State.Update(deltatime,updateonce);

			// update gameobject
			for (var i = 0; i < State.InputCount; i++)
			{
				if (i < RendererObjects.Count)
				{
					var g = RendererObjects[i];
					if (g != null)
					{
						var c = g.GetComponent<IUnityStateRenderer>();
						if (c != null)
						{
							c.UpdateGameObject();
						}
					}
				}
			}
		}
        public void SetRendererCount()
        {
            var count = 2;
            if (Data != null) count = Math.Max(Data.ObjectCount, count);

            if (RendererObjects.Count == count) return;

            if (RendererObjects.Count < count)
            {
                for (var i = 0; i < count - RendererObjects.Count; i++)
                {
                    RendererObjects.Add(null);
                }
            }
            if (RendererObjects.Count > count)
            {
                RendererObjects.RemoveRange(count, RendererObjects.Count - count);
            }
        }
    }

    // inspector
#if UNITY_EDITOR

    [CustomEditor(typeof(AnimationrigStateComponent))]
    public class AnimationrigStateInspector : Editor
    {
        private bool SectionData = false;
        private Vector2 ScrollData; 
        private bool SectionState = false;
        private Vector2 ScrollState;
        private bool SectionPreset = false;
        private Vector2 ScrollPreset;

        public void OnEnable()
        {
			
		}
        public override void OnInspectorGUI()
        {
            // base.OnInspectorGUI();

            var targetclass = target as AnimationrigStateComponent;

            targetclass.AnimrigBinaryData = (TextAsset)EditorGUILayout.ObjectField("Animrig TextAsset", targetclass.AnimrigBinaryData, typeof(TextAsset), true);
            targetclass.PresetBinaryData = (TextAsset)EditorGUILayout.ObjectField("Preset TextAsset", targetclass.PresetBinaryData, typeof(TextAsset), true);

			targetclass.AutoUpdate = EditorGUILayout.Toggle(
				new GUIContent("AutoUpdate", "default:true. Called by unity update(). If you call manually, set false."), 
				targetclass.AutoUpdate
			);
			targetclass.UpdateOnce = EditorGUILayout.Toggle(
				new GUIContent("UpdateOnce", "default:true. Call object update only once. If you need accurate animation, set false."),
				targetclass.UpdateOnce
			);

			// renderer objects
			{
                var count = targetclass.RendererObjects.Count;
                {
                    var style = new GUIStyle()
                    {
                        alignment = TextAnchor.MiddleLeft,
                        fontStyle = FontStyle.Bold,
                    };
                    EditorGUILayout.LabelField("RendererObjects", style);
                }

                for (var i = 0; i < count; i++)
                {
                    var name = "empty";
                    var typename = "none";
                    if ((targetclass.Data != null) && (i < targetclass.Data.ObjectCount))
                    {
                        name = targetclass.Data.Objects[i].Name;
                        typename = Misc.ObjectTypeToString(targetclass.Data.Objects[i].ObjectType);
                    }

                    targetclass.RendererObjects[i] = EditorGUILayout.ObjectField($"{i} {name}({typename})", targetclass.RendererObjects[i], typeof(GameObject), true) as GameObject;
                }
                if (count == 0)
                {
                    var style = new GUIStyle()
                    {
                        alignment = TextAnchor.MiddleCenter,
                        fontStyle = FontStyle.Normal,
                    };
                    EditorGUILayout.LabelField("none",style);
                }
            }
			// renderer object check
			{
				var s = "";
				foreach (var go in targetclass.RendererObjects)
				{
					if (go == null) continue;
					var comp = go.GetComponent<Animrig.Unity.IUnityStateRenderer>();
					if (comp == null)
					{
						if (s != "") s = s + "\r\n";
						s = s + go.name;
					}
				}
				if (s != "")
				{
					EditorGUILayout.HelpBox("Please attach IStateRenderer component to below GameObjects.\r\n" + s, MessageType.Error);
				}
			}
            // animationrig
            SectionData = EditorGUILayout.Foldout(SectionData, "Data Info");
            if (SectionData)
            {
                var buf = "";
                if (targetclass.Data != null)
                {
                    // animations
                    {
                        buf += $"AnimationCount : {targetclass.Data.AnimationCount}" + "\r\n";
                        foreach (var o in targetclass.Data.Animations)
                        {
                            buf += o.Name + "\r\n";
                        }
                    }
                    buf += "\r\n";
                    // objects
                    {
                        buf = buf + $"ObjectCount : {targetclass.Data.ObjectCount}" + "\r\n";
                        var index = 0;
                        foreach (var o in targetclass.Data.Objects)
                        {
                            buf += $"{index} {o.Name} : {Misc.ObjectTypeToString(o.ObjectType)}" + "\r\n";
                            index++;
                        }
                    }
                }
                //ScrollData = EditorGUILayout.BeginScrollView(ScrollData);
                EditorGUILayout.TextArea(buf, GUILayout.Height(200));
                //EditorGUILayout.EndScrollView();
            }

            // animationrig state
            SectionState = EditorGUILayout.Foldout(SectionState, "State Info");
            if (SectionState)
            {
                var buf = "";
                if (targetclass.State != null)
                {
                    buf += $"TrackCount : {targetclass.State.TrackCount}" + "\r\n";
                    var index = 0;
                    foreach (var o in targetclass.State.Tracks)
                    {
                        if (o == null)
                        {
                            buf += "null \r\n";
                        }
                        else
                        {
                            if (o.Animation == null)
                            {
                                buf += "error! \r\n";
                            }
                            else
                            {
                                buf += $"{index} {o.Animation.Name} : frame {o.PlayFrame}" + "\r\n";
                            }
                        }
                        index++;
                    }
                }

                //ScrollPreset = EditorGUILayout.BeginScrollView(ScrollPreset);
                EditorGUILayout.TextArea(buf, GUILayout.Height(200));
                //EditorGUILayout.EndScrollView();
            }


            // preset
            SectionPreset = EditorGUILayout.Foldout(SectionPreset, "Preset Info");
            if (SectionPreset)
            {
                var style = new GUIStyle()
                {
                    alignment = TextAnchor.MiddleLeft,
                    fontStyle = FontStyle.Normal,
                };
                EditorGUILayout.LabelField("key = value", style);

                var buf = "";
                if (targetclass.Preset != null)
                {
                    buf = buf + $"PresetCount : {targetclass.Preset.list.Count}" + "\r\n";
                    foreach (var o in targetclass.Preset.list)
                    {
                        buf = buf + $"{o.Key} = {o.Value}" + "\r\n";
                    }
                }

                //ScrollPreset = EditorGUILayout.BeginScrollView(ScrollPreset);
                EditorGUILayout.TextArea(buf, GUILayout.Height(200));
                //EditorGUILayout.EndScrollView();
            }

            GUILayout.BeginHorizontal();
            if (GUILayout.Button("reload"))
            {
                targetclass.Rebuild();
				targetclass.SetRendererCount();
			}
            if (GUILayout.Button("clear"))
            {
                targetclass.Clear();
				targetclass.SetRendererCount();
			}
            GUILayout.EndHorizontal();

            serializedObject.Update();
            serializedObject.ApplyModifiedProperties();
        }
    }

#endif

}

