﻿using System.Collections;
using Febucci.TextAnimatorForUnity;
using Febucci.TextAnimatorForUnity.TextMeshPro;
using Febucci.TextAnimatorCore.Typing;
using UnityEngine;
using UtageExtensions;

namespace Utage
{
	//NovelTextのTextAnimator版
	[AddComponentMenu("Utage/TextAnimator/NovelTextForTextAnimator")]
	public class NovelTextForTextAnimator : TextMeshProNovelText
	{
		AdvEngine Engine => this.GetAdvEngineCacheFindIfMissing(ref engine);
		[SerializeField] AdvEngine engine;

		public TextAnimator_TMP TextAnimator => this.GetComponentCache(ref textAnimator);
		[SerializeField] TextAnimator_TMP textAnimator;
		TypewriterComponent Typewriter => this.GetComponentCache(ref typewriter);
		TypewriterComponent typewriter;
		/// メッセージウィンドウの表示のルート
		[SerializeField] protected GameObject root;

		//Star時にテキストをクリアするか 
		[SerializeField] protected bool clearTextOnStart = false;


		//フォールバックフォントのバグ回避策を有効にするか
		enum FallbackFontBugFixType
		{
			Disable,
			HideEndOfFrame,	//最終フレームで非表示にする
		}
		FallbackFontBugFixType FallbackFontBugFix => fallbackFontBugFix;
		[SerializeField] FallbackFontBugFixType fallbackFontBugFix = FallbackFontBugFixType.Disable;

		bool DisableFallbackFontBugFix => FallbackFontBugFix == FallbackFontBugFixType.Disable;

		//フォールバックフォントのバグ回避策を有効にする場合、CanvasGroupを設定する
		[SerializeField, Hide(nameof(DisableFallbackFontBugFix))]
		CanvasGroup canvasGroup;

		//フォールバックフォントのバグ回避策のスキップ中の扱い
		enum FallbackFontBugFixSkipType
		{
			Disable,          //スキップ中はなにもしない
			HideSkipWaitTime, //一定時間時間を待つタイプのスキップ中の場合は非表示にする
		}
		FallbackFontBugFixSkipType FallbackFontBugFixSkipping => fallbackFontBugFixSkipping;
		[SerializeField, Hide(nameof(DisableFallbackFontBugFix))] FallbackFontBugFixSkipType fallbackFontBugFixSkipping = FallbackFontBugFixSkipType.Disable;

		//テキスト表示開始したかどうか
		bool IsStartedShowText { get; set; }
		
		//現在のテキスト
		string CurrentText { get; set; }
		//現在の表示文字数
		int CurrentCharacterCount { get; set; } = -1;

		//フォールバックフォントのバグ回避策で使用する、テキスト表示のコルーチン
		Coroutine CoroutineShowText { get; set; }
		WaitForEndOfFrame WaitForEndOfFrame { get; } = new();
		//フォールバックフォントのバグ回避策で使用する、アルファ値の保存
		float StoredAlpha { get; set; }

		public override int MaxVisibleCharacters
		{
			get
			{
				return maxVisibleCharacters;
			}
			set
			{
				maxVisibleCharacters = value;
//				Debug.Log($"frame{Time.frameCount} maxVisibleCharacters:{value}");
				SetVisibleCharacterCount(value);
			}
		}
		int maxVisibleCharacters;

#if true		
		void Start()
		{
			if (!Typewriter.localSettings.useTypeWriter)
			{
				Debug.LogError("TypewriterByCharacter is not useTypeWriter", this);
			}
			else if (Typewriter.localSettings.startTypewriterMode != StartTypewriterMode.FromScriptOnly)
			{
				Debug.LogError("TypewriterByCharacter is not StartTypewriterMode.FromScriptOnly", this);
			}

		}
#endif
		public override void SetTextDirect(string text)
		{
			base.SetTextDirect(text);
			CurrentText = text;
		}

		protected override void ForceUpdateText(string textMeshProString,bool ignoreActiveState)
		{
			if( !IsBeginPageOrChangeText(textMeshProString))
			{
				return;
			}

			if (root != null)
			{
				//イベントタグを使う場合、UseTypeWriteがOnでなければならず
				//その場合、オブジェクトが非アクティブだとコルーチンが動かないので、アクティブにしておく
				root.SetActive(true);
			}

			BeginShowText();
			if (EnableFallbackFontBugFix(textMeshProString))
			{
				//フォントがフォールバックフォントを使っている場合のTextAnimatorのバグの回避策
				CancelCoShowText();
				CoroutineShowText = StartCoroutine(CoShowText(textMeshProString));
			}
			else
			{
				ShowTextSimple(textMeshProString);
			}
			CurrentText　= textMeshProString;
			return;
			
			//ページの冒頭か、テキストが変更されているかのチェック
			bool IsBeginPageOrChangeText(string text)
			{
				if (Engine.Page.CurrentTextLength == 0) return true;
				if (CurrentText != text) return true;
				return false;
			}
			
			//フォールバックフォントのバグ回避策で、1フレームだけの非表示処理をするかのチェック
			bool EnableFallbackFontBugFix(string text)
			{
				//プレイモード以外はやらない
				if( !Application.isPlaying) return false;
				//バグ回避策が無効ならやらない
				if (FallbackFontBugFix == FallbackFontBugFixType.Disable) return false;
				//同じテキストならやらない
				if( CurrentText == text) return false;
				//非アクティブならやらない
				if (!this.gameObject.activeInHierarchy) return false;
				
				//それ以外ならやる
				return true;
			}
		}
		

		//テキストを表示する（バグ回避策なしでシンプルに）
		void ShowTextSimple(string text)
		{
			Typewriter.ShowText(text);
			EndStartShowText();
		}

		//ShowTextの処理
		//フォントがフォールバックフォントを使っている場合のTextAnimatorのバグの回避策を含む
		//
		IEnumerator CoShowText(string text)
		{
			//TextAnimatorのバグで、フォールバックフォントを使っている場合、1フレームだけテキストが壊れて表示されるので、それを回避する

			if (canvasGroup == null)
			{
				Debug.LogError("Set CanvasGroup to NovelTextForTextAnimator", this);
				ShowTextSimple(text);
				yield break;
			}
			
			if (!CheckHide())
			{
				//スキップ中などで、何もしない場合
				ShowTextSimple(text);
				yield break;
			}
			
			//このフレームはいったん非表示にする
			Typewriter.ShowText(text);
			StoredAlpha = canvasGroup.alpha;
			canvasGroup.alpha = 0;
			
			yield return WaitForEndOfFrame;	//WaitForEndOfFrameは表示終了後に呼ばれるのでここで、表示設定を戻す
			OnEndCoShowText();
		}
		
		//フォールバックフォントのバグ回避策で、非表示処理をするかのチェック
		bool CheckHide()
		{
			//スキップ中でなければ有効
			if (!Engine.Page.CheckSkip()) return true;

			switch (FallbackFontBugFixSkipping)
			{
				//スキップ中はなにもしない
				case FallbackFontBugFixSkipType.Disable:
					return false;
				case FallbackFontBugFixSkipType.HideSkipWaitTime:
					if (Engine.Page.TypeSkipMessage == AdvPage.SkipMessageType.WaitSkippedTime)
					{
						//表示時間を待つタイプのスキップのときは、有効
						return true;
					}
					return false;
				default:
					return true;
			}
		}

		void CancelCoShowText()
		{
			if (CoroutineShowText == null) return;

			StopCoroutine(CoroutineShowText);
			OnEndCoShowText();
		}
		
		void OnEndCoShowText()
		{
			canvasGroup.alpha = StoredAlpha;
			EndStartShowText();
			CoroutineShowText = null;
		}
		
		void BeginShowText()
		{
//			Debug.Log("BeginShowText");
			IsStartedShowText　= false;
			CurrentCharacterCount = -1;
			Typewriter.StartShowingText(true);
			TextAnimator.MaxVisibleCharacters = 0;

		}
		void EndStartShowText()
		{
//			Debug.Log("StartShowText");
			IsStartedShowText　= true;
		}
		
		void SetVisibleCharacterCount(int count0)
		{
			int count = Mathf.Clamp(count0, 0, TextAnimator.CharactersCount);
//			Debug.Log($"frame:{Time.frameCount} SetVisibleCharacterCount({count0}) clamped:{count} \n IsStartedShowText ={IsStartedShowText} CurrentCharacterCount:{CurrentCharacterCount}\n TextAnimator.CharactersCount:{TextAnimator.CharactersCount} ");
			//変化がないならなにもしない
			if (CurrentCharacterCount == count) return;

			//開始前なら、なにもしない
			if (!IsStartedShowText) return;
			TextAnimator.MaxVisibleCharacters = count;
			for (int i = 0; i < TextAnimator.CharactersCount; i++)
			{
				TextAnimator.SetVisibilityChar(i, i <= count);
			}
			Typewriter.TriggerVisibleEvents();
			CurrentCharacterCount = count;
			UpdateVisibleIndex();
		}
	}
}
