using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

namespace RenderHeads.Media.AVProVideo.Editor
{
	[CanEditMultipleObjects()]
	[CustomEditor(typeof(MediaReference))]
	public class MediaReferenceEditor : UnityEditor.Editor
	{
		internal const string SettingsPrefix = "AVProVideo-MediaReferenceEditor-";

		private SerializedProperty _propMediaPath;
		private SerializedProperty _propHints;

		private SerializedProperty _propPlatformMacOS;
		private SerializedProperty _propPlatformWindows;
		private SerializedProperty _propPlatformAndroid;
		private SerializedProperty _propPlatformIOS;
		private SerializedProperty _propPlatformTvOS;
		private SerializedProperty _propPlatformWindowsUWP;
		private SerializedProperty _propPlatformWebGL;
		//private SerializedProperty _propAlias;

		void OnEnable()
		{
			_propMediaPath = this.CheckFindProperty("_mediaPath");
			_propHints = this.CheckFindProperty("_hints");

			_propPlatformMacOS = this.CheckFindProperty("_macOS");
			_propPlatformWindows = this.CheckFindProperty("_windows");
			_propPlatformAndroid = this.CheckFindProperty("_android");
			_propPlatformIOS = this.CheckFindProperty("_iOS");
			_propPlatformTvOS = this.CheckFindProperty("_tvOS");
			_propPlatformWindowsUWP = this.CheckFindProperty("_windowsUWP");
			_propPlatformWebGL = this.CheckFindProperty("_webGL");

			//_propAlias = CheckFindProperty("_alias");
			_zoomToFill = EditorPrefs.GetBool(SettingsPrefix + "ZoomToFill", _zoomToFill);
			_thumbnailTime = EditorPrefs.GetFloat(SettingsPrefix + "ThumbnailTime", _thumbnailTime);
		}

		void OnDisable()
		{
			EndGenerateThumbnails(false);
			RemoveProgress();

			EditorPrefs.SetBool(SettingsPrefix + "ZoomToFill", _zoomToFill);
			EditorPrefs.SetFloat(SettingsPrefix + "ThumbnailTime", _thumbnailTime);
		}

		public override void OnInspectorGUI()
		{
			//MediaPlayer media = (this.target) as MediaPlayer;

			//this.DrawDefaultInspector();

			serializedObject.Update();

			GUILayout.Label("Media Reference");
			EditorGUILayout.Space();
			//EditorGUILayout.PropertyField(_propAlias);
			//EditorGUILayout.Space();

			{
				string mediaName = _propMediaPath.FindPropertyRelative("_path").stringValue;	
				GUILayout.BeginVertical(GUI.skin.box);
				MediaPlayerEditor.OnInspectorGUI_CopyableFilename(mediaName);
				GUILayout.EndVertical();
			}

			EditorGUILayout.PropertyField(_propMediaPath);

			MediaPathDrawer.ShowBrowseButton(_propMediaPath);

			EditorGUILayout.Space();

			//GUILayout.Label("Media Hints", EditorStyles.boldLabel);
			EditorGUILayout.PropertyField(_propHints);

			EditorGUILayout.PropertyField(_propPlatformMacOS, new GUIContent("macOS"));
			EditorGUILayout.PropertyField(_propPlatformWindows, new GUIContent("Windows"));
			EditorGUILayout.PropertyField(_propPlatformAndroid, new GUIContent("Android"));
			EditorGUILayout.PropertyField(_propPlatformIOS, new GUIContent("iOS"));
			EditorGUILayout.PropertyField(_propPlatformTvOS, new GUIContent("tvOS"));
			EditorGUILayout.PropertyField(_propPlatformWindowsUWP, new GUIContent("UWP"));
			EditorGUILayout.PropertyField(_propPlatformWebGL, new GUIContent("WebGL"));
			EditorGUILayout.Space();
			EditorGUILayout.Space();
			
			serializedObject.ApplyModifiedProperties();

			bool beginGenerateThumbnails = false;
			
			GUILayout.FlexibleSpace();
			EditorGUI.BeginDisabledGroup(IsGeneratingThumbnails());

			GUILayout.BeginHorizontal();
			_thumbnailTime = GUILayout.HorizontalSlider(_thumbnailTime, 0f, 1f, GUILayout.ExpandWidth(true));
			_zoomToFill = GUILayout.Toggle(_zoomToFill, "Zoom And Crop", GUI.skin.button, GUILayout.ExpandWidth(false));
			GUILayout.EndHorizontal();
			if (GUILayout.Button("Generate Thumbnail"))
			{
				beginGenerateThumbnails = true;
			}
			EditorGUI.EndDisabledGroup();

			if (beginGenerateThumbnails)
			{
				BeginGenerateThumbnails();
			}

			if (IsGeneratingThumbnails())
			{
				ShowProgress();
			}
			if (!IsGeneratingThumbnails())
			{
				RemoveProgress();
			}
		}

		private void ShowProgress()
		{
			// Show cancellable progress
			float t = (float)_targetIndex / (float)this.targets.Length;
			t = 0.25f + t * 0.75f;
			MediaReference media = (this.targets[_targetIndex]) as MediaReference;
			
			#if UNITY_2020_1_OR_NEWER
			if (_progressId < 0)
			{
				//Progress.RegisterCancelCallback(_progressId...)
				_progressId = Progress.Start("[AVProVideo] Generating Thumbnails...", null, Progress.Options.Managed);
			}
			Progress.Report(_progressId, t, media.MediaPath.Path);
			#else
			if (EditorUtility.DisplayCancelableProgressBar("[AVProVideo] Generating Thumbnails...", media.MediaPath.Path, t))
			{
				EndGenerateThumbnails(false);
			}
			#endif
		}

		private void RemoveProgress()
		{
			#if UNITY_2020_1_OR_NEWER
			if (_progressId >= 0)
			{
				Progress.Remove(_progressId);
				_progressId = -1;
			}
			#else
			EditorUtility.ClearProgressBar();
			#endif
		}

		#if UNITY_2020_1_OR_NEWER
		private int _progressId = -1;
		#endif
		private float _thumbnailTime;
		private bool _zoomToFill = false;
		private int _lastFrame;
		private BaseMediaPlayer _thumbnailPlayer;
		private int _mediaFrame = -1;
		private int _targetIndex = 0;
		private float _timeoutTimer = 0f;

		private bool IsGeneratingThumbnails()
		{
			return (_thumbnailPlayer != null);
		}

		private void BeginGenerateThumbnails()
		{
			EditorApplication.update -= UpdateGenerateThumbnail;

			Debug.Assert(_thumbnailPlayer == null);
			#if UNITY_EDITOR_WIN
			if (WindowsMediaPlayer.InitialisePlatform())
			{
				MediaPlayer.OptionsWindows options = new MediaPlayer.OptionsWindows();
				_thumbnailPlayer = new WindowsMediaPlayer(options);
			}
			#elif UNITY_EDITOR_OSX
			{
				MediaPlayer.OptionsApple options = new MediaPlayer.OptionsApple(MediaPlayer.OptionsApple.TextureFormat.BGRA, MediaPlayer.OptionsApple.Flags.None);
				_thumbnailPlayer = new PlatformMediaPlayer(options);
			}
			#endif

			if (_thumbnailPlayer != null)
			{
				_targetIndex = 0;
				BeginNextThumbnail(0);
			}
			else
			{
				EndGenerateThumbnails(false);
			}
		}

		private void BeginNextThumbnail(int index)
		{
			EditorApplication.update -= UpdateGenerateThumbnail;
			_mediaFrame = -1;
			_timeoutTimer = 0f;

			if (_thumbnailPlayer != null)
			{
				if (index < this.targets.Length)
				{
					_targetIndex = index;
					MediaReference media = (this.targets[_targetIndex]) as MediaReference;
					string path = media.MediaPath.GetResolvedFullPath();
					bool openedMedia = false;
					if (!string.IsNullOrEmpty(path))
					{
						if (_thumbnailPlayer.OpenMedia(path, 0, string.Empty, media.Hints, 0, false))
						{
							openedMedia = true;
							EditorApplication.update += UpdateGenerateThumbnail;
						}
					}

					if (!openedMedia)
					{
						// If the media failed to open, continue to the next one
						BeginNextThumbnail(_targetIndex + 1);
					}
				}
				else
				{
					EndGenerateThumbnails(true);
				}
			}
		}

		private void EndGenerateThumbnails(bool updateAssets)
		{
			EditorApplication.update -= UpdateGenerateThumbnail;
			if (_thumbnailPlayer != null)
			{
				_thumbnailPlayer.CloseMedia();
				_thumbnailPlayer.Dispose();
				_thumbnailPlayer = null;
			}
			_mediaFrame = -1;

			if (updateAssets)
			{
				// This forces the static preview to refresh
				foreach (Object o in this.targets)
				{
					EditorUtility.SetDirty(o);
					AssetPreview.GetAssetPreview(o);
				}
				AssetDatabase.SaveAssets();
			}
		}

		private void UpdateGenerateThumbnail()
		{
			if (Time.renderedFrameCount == _lastFrame)
			{
				// In at least Unity 5.6 we have to force refresh of the UI otherwise the render thread doesn't run to update the textures
				this.Repaint();
				UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
				return;
			}

			// Wait for a frame to be rendered
			Debug.Assert(_thumbnailPlayer != null);
			if (_thumbnailPlayer != null)
			{
				_timeoutTimer += Time.unscaledDeltaTime;
				bool nextVideo = false;
				_thumbnailPlayer.Update();
				_thumbnailPlayer.Render();

				if (_mediaFrame < 0 && _thumbnailPlayer.CanPlay())
				{
					_thumbnailPlayer.MuteAudio(true);
					_thumbnailPlayer.Play();
					_thumbnailPlayer.Seek(_thumbnailPlayer.GetDuration() * _thumbnailTime);
					_mediaFrame = _thumbnailPlayer.GetTextureFrameCount();
				}
				if (_thumbnailPlayer.GetTexture() != null)
				{
					if (_mediaFrame != _thumbnailPlayer.GetTextureFrameCount() && _thumbnailPlayer.GetTextureFrameCount() > 3)
					{
						bool prevSRGB = GL.sRGBWrite;
						GL.sRGBWrite = false;

						RenderTexture rt2 = null;
						// TODO: move this all into VideoRender as a resolve method
						{
							Material materialResolve = new Material(Shader.Find(VideoRender.Shader_Resolve));
							VideoRender.SetupVerticalFlipMaterial(materialResolve, _thumbnailPlayer.RequiresVerticalFlip());
							VideoRender.SetupAlphaPackedMaterial(materialResolve, _thumbnailPlayer.GetTextureAlphaPacking());
							VideoRender.SetupGammaMaterial(materialResolve, !_thumbnailPlayer.PlayerSupportsLinearColorSpace());

							RenderTexture prev = RenderTexture.active;

							// Scale to fit and downsample
							rt2 = RenderTexture.GetTemporary(128, 128, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);

							RenderTexture.active = rt2;
							GL.Clear(false, true, new Color(0f, 0f, 0f, 0f));
							ScaleMode scaleMode = ScaleMode.ScaleToFit;
							if (_zoomToFill)
							{
								scaleMode = ScaleMode.ScaleAndCrop;
							}
							VideoRender.DrawTexture(new Rect(0f, 0f, 128f, 128f), _thumbnailPlayer.GetTexture(), scaleMode, _thumbnailPlayer.GetTextureAlphaPacking(), _thumbnailPlayer.GetTexturePixelAspectRatio(), materialResolve);
							RenderTexture.active = prev;

							Material.DestroyImmediate(materialResolve); materialResolve = null;
						}
				
						Texture2D readTexture = new Texture2D(128, 128, TextureFormat.RGBA32, true, false);
						Helper.GetReadableTexture(rt2, readTexture);
						MediaReference mediaRef = (this.targets[_targetIndex]) as MediaReference;
						mediaRef.GeneratePreview(readTexture);
						DestroyImmediate(readTexture); readTexture = null;

						RenderTexture.ReleaseTemporary(rt2);

						GL.sRGBWrite = prevSRGB;
						nextVideo = true;
						Debug.Log("Thumbnail Written");
					}
				}
				if (!nextVideo)
				{
					// If there is an error or it times out, then skip this media
					if (_timeoutTimer > 10f || _thumbnailPlayer.GetLastError() != ErrorCode.None)
					{
						MediaReference mediaRef = (this.targets[_targetIndex]) as MediaReference;
						mediaRef.GeneratePreview(null);
						nextVideo = true;
					}
				}

				if (nextVideo)
				{
					BeginNextThumbnail(_targetIndex + 1);
				}
			}
			_lastFrame = Time.renderedFrameCount;
		}

		public override bool HasPreviewGUI()
		{
			return true;
		}

		public override void OnPreviewGUI(Rect r, GUIStyle background)
		{
			Texture texture = RenderStaticPreview(string.Empty, null, 128, 128);
			if (texture)
			{
				GUI.DrawTexture(r, texture, ScaleMode.ScaleToFit, true);
			}
		}

		public override Texture2D RenderStaticPreview(string assetPath, Object[] subAssets, int width, int height)
		{
			MediaReference media = this.target as MediaReference;
			if (media)
			{
				bool isLinear = false;
				#if !UNITY_2018_1_OR_NEWER
				// NOTE: These older versions of Unity don't handle sRGB in the editor correctly so a workaround is to create texture as linear
				isLinear = true;
				#endif
				Texture2D result = new Texture2D(width, height, TextureFormat.RGBA32, true, isLinear);
				if (!media.GetPreview(result))
				{
					DestroyImmediate(result); result = null;
				}
				return result;
			}
			return null;
		}
	}
}