using UnityEngine;
using UnityEditor;
using System.Linq;
using System.Text.RegularExpressions;
using System;

namespace NaughtyAttributes.Editor
{
	[CustomPropertyDrawer(typeof(SceneAttribute))]
	public class ScenePropertyDrawer : PropertyDrawerBase
	{
		private const string SceneListItem = "{0} ({1})";
		private const string ScenePattern = @".+\/(.+)\.unity";
		private const string TypeWarningMessage = "{0} must be an int or a string";
		private const string BuildSettingsWarningMessage = "No scenes in the build settings";

		protected override float GetPropertyHeight_Internal(SerializedProperty property, GUIContent label)
		{
			bool validPropertyType = property.propertyType == SerializedPropertyType.String || property.propertyType == SerializedPropertyType.Integer;
			bool anySceneInBuildSettings = GetScenes().Length > 0;

			return (validPropertyType && anySceneInBuildSettings)
				? GetPropertyHeight(property)
				: GetPropertyHeight(property) + GetHelpBoxHeight();
		}

		protected override void OnGUI_Internal(Rect rect, SerializedProperty property, GUIContent label)
		{
			EditorGUI.BeginProperty(rect, label, property);

			string[] scenes = GetScenes();
			bool anySceneInBuildSettings = scenes.Length > 0;
			if (!anySceneInBuildSettings)
			{
				DrawDefaultPropertyAndHelpBox(rect, property, BuildSettingsWarningMessage, MessageType.Warning);
				return;
			}

			string[] sceneOptions = GetSceneOptions(scenes);
			switch (property.propertyType)
			{
				case SerializedPropertyType.String:
					DrawPropertyForString(rect, property, label, scenes, sceneOptions);
					break;
				case SerializedPropertyType.Integer:
					DrawPropertyForInt(rect, property, label, sceneOptions);
					break;
				default:
					string message = string.Format(TypeWarningMessage, property.name);
					DrawDefaultPropertyAndHelpBox(rect, property, message, MessageType.Warning);
					break;
			}

			EditorGUI.EndProperty();
		}

		private string[] GetScenes()
		{
			return EditorBuildSettings.scenes
				.Where(scene => scene.enabled)
				.Select(scene => Regex.Match(scene.path, ScenePattern).Groups[1].Value)
				.ToArray();
		}

		private string[] GetSceneOptions(string[] scenes)
		{
			return scenes.Select((s, i) => string.Format(SceneListItem, s, i)).ToArray();
		}

		private static void DrawPropertyForString(Rect rect, SerializedProperty property, GUIContent label, string[] scenes, string[] sceneOptions)
		{
			int index = IndexOf(scenes, property.stringValue);
			int newIndex = EditorGUI.Popup(rect, label.text, index, sceneOptions);
			string newScene = scenes[newIndex];

			if (!property.stringValue.Equals(newScene, StringComparison.Ordinal))
			{
				property.stringValue = scenes[newIndex];
			}
		}

		private static void DrawPropertyForInt(Rect rect, SerializedProperty property, GUIContent label, string[] sceneOptions)
		{
			int index = property.intValue;
			int newIndex = EditorGUI.Popup(rect, label.text, index, sceneOptions);

			if (property.intValue != newIndex)
			{
				property.intValue = newIndex;
			}
		}

		private static int IndexOf(string[] scenes, string scene)
		{
			var index = Array.IndexOf(scenes, scene);
			return Mathf.Clamp(index, 0, scenes.Length - 1);
		}
	}
}