//    JSONValidator


using UnityEngine;
using UnityEditor;
using System.Text;
using Leguar.TotalJSON;

namespace Leguar.TotalJSON.Internal {

	public class JSONValidator : EditorWindow {

		private string editorAreaText="Write or copy & paste JSON object or JSON array to this text area. Example JSON:\n\n{\"name\":\"Player\",\"lastLogin\":123456789012,\"achievements\":[42,1337,1703],\"imageUrl\":null,\"have_bought\":true,\"levels\":[{\"passed\":true,\"score\":12345},{\"passed\":false}]}\n\nExtra texts like this before or after JSON object will be removed when clicking button below.";

		private string message = "";
		private string tightJSON = "";
		private string escapedJSON = "";

		private Vector2 scrollPos;

		private readonly float lineHeight=EditorGUIUtility.singleLineHeight;

		[MenuItem("Window/Total JSON/JSON Validator")]
		static void Init() {
			JSONValidator window=(JSONValidator)(GetWindow(typeof(JSONValidator)));
#if UNITY_5 || UNITY_2017
			window.titleContent = new GUIContent("JSON Validator");
#else
			window.titleContent = new GUIContent("JSON Validator",loadWindowIcon());
#endif
		}

#if !(UNITY_5 || UNITY_2017)
		internal static Texture2D loadWindowIcon() {
			string dirPath = AssetDatabase.GUIDToAssetPath("9f2fdab21a6aeaf41956a4d04ddf4dd2"); // "Assets/TotalJSON/Internal/Editor"
			string iconPath = dirPath+"/window-icon.png";
			Texture2D icon = (Texture2D)(AssetDatabase.LoadAssetAtPath(iconPath, typeof(Texture2D)));
			return icon;
		}
#endif

		void OnGUI() {

			GUILayout.Space(20);

			scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
			EditorGUI.BeginChangeCheck();
			editorAreaText = EditorGUILayout.TextArea(editorAreaText,GUILayout.ExpandHeight(true));
			bool changes = EditorGUI.EndChangeCheck();
			EditorGUILayout.EndScrollView();

			if (changes) {
				message = "";
				tightJSON = "";
				escapedJSON = "";
			}

			GUILayout.Space(10);

			if (GUILayout.Button("Trim, validate and prettify above JSON object or array")) {

				string trimmedEditorText=editorAreaText.Trim();
				if (trimmedEditorText.Length<editorAreaText.Length) {
					editorAreaText=trimmedEditorText; // Needed in case of errors, so that line numbers will match
					GUIUtility.keyboardControl=0;
					GUIUtility.hotControl=0;
					EditorUtility.SetDirty(this);
				}

				if (string.IsNullOrEmpty(trimmedEditorText)) {
					message = "Input is empty";
					tightJSON = "";
					escapedJSON = "";

				} else {
					object objectOrError = findAndGetJSONOrJArray(trimmedEditorText);
					if (objectOrError==null) {
						message = "Can't find JSON start from input";
						tightJSON = "";
						escapedJSON = "";
					} else if (objectOrError is string) {
						message = "Invalid input: "+((string)(objectOrError));
						tightJSON = "";
						escapedJSON = "";
					} else {
						CreateStringSettings prettySettings=new CreateStringSettings() {
							HumanReadable=true,
							IndentUsingTab=true,
							NewLine=CreateStringSettings.NewLineTypes.LF // \r characters (may be part of environment default) seem to be problem in editor textarea, causing invisible linefeeds, so using plain \n
						};
						if (objectOrError is JSON[]) {
							JSON[] jsons = (JSON[])(objectOrError);
							if (jsons.Length==1) {
								message = "JSON is valid. Top level JSON key/value pair count = "+jsons[0].Count;
								editorAreaText = jsons[0].CreateString(prettySettings)+"\n";
								tightJSON = jsons[0].CreateString(new CreateStringSettings() { HumanReadable = false });
								escapedJSON = "\""+getEscapedString(tightJSON)+"\"";
							} else {
								message = "JSONs are valid. JSON object count = "+jsons.Length;
								editorAreaText = "";
								tightJSON = "";
								for (int n = 0; n<jsons.Length; n++) {
									editorAreaText += jsons[n].CreateString(prettySettings)+"\n";
									if (n<jsons.Length-1) {
										editorAreaText += '\n';
									}
									tightJSON += jsons[n].CreateString(new CreateStringSettings() { HumanReadable = false });
								}
								escapedJSON = "\""+getEscapedString(tightJSON)+"\"";
							}
						} else {
							JArray jArray = (JArray)(objectOrError);
							message = "JSON Array is valid. Top level array length = "+jArray.Length;
							editorAreaText = jArray.CreateString(prettySettings)+"\n";
							tightJSON = jArray.CreateString(new CreateStringSettings() { HumanReadable = false });
							escapedJSON = "\""+getEscapedString(tightJSON)+"\"";
						}
						GUIUtility.keyboardControl=0;
						GUIUtility.hotControl=0;
						EditorUtility.SetDirty(this);
					}
				}
			}

			GUILayout.Space(20);
			GUILayout.Label(message, EditorStyles.wordWrappedLabel);
			GUILayout.Space(20);

			EditorGUI.BeginDisabledGroup(tightJSON.Length==0);

			GUILayout.Label("JSON formatted and encoded string ("+tightJSON.Length+" bytes):", EditorStyles.wordWrappedLabel);
			GUILayout.Space(5);
			EditorGUILayout.BeginHorizontal();
			EditorGUILayout.SelectableLabel(tightJSON,EditorStyles.textField,GUILayout.Height(lineHeight+2));
			if (GUILayout.Button("Copy", GUILayout.Width(70))) {
				GUIUtility.systemCopyBuffer = tightJSON;
			}
			EditorGUILayout.EndHorizontal();
			GUILayout.Space(15);
			GUILayout.Label("Above string with escapes (to be used for example directly in c# source code):", EditorStyles.wordWrappedLabel);
			GUILayout.Space(5);
			EditorGUILayout.BeginHorizontal();
			EditorGUILayout.SelectableLabel(escapedJSON,EditorStyles.textField,GUILayout.Height(lineHeight+2));
			if (GUILayout.Button("Copy", GUILayout.Width(70))) {
				GUIUtility.systemCopyBuffer = escapedJSON;
			}
			EditorGUILayout.EndHorizontal();
			GUILayout.Space(20);

			EditorGUI.EndDisabledGroup();

		}

		private object findAndGetJSONOrJArray(string dirtySourceString) {

			// Remove any heading "trash", for example in case that JSON is pasted from Unity Console
			int jsonStartIndex=dirtySourceString.IndexOf('{');
			int jArrayStartIndex=dirtySourceString.IndexOf('[');

			// Nothing?
			if (jsonStartIndex<0 && jArrayStartIndex<0) {
				return null;
			}

			// Try parse array
			if (jArrayStartIndex>=0 && (jArrayStartIndex<jsonStartIndex || jsonStartIndex<0)) {
				try {
					return JArray.ParseString(dirtySourceString,new ParseStringSettings(){ ParseStartIndex=jArrayStartIndex, AllowNonWhiteCharactersAfterObject=true });
				}
				catch (ParseException e) {
					if (jsonStartIndex<0) {
						return e.Message;
					}
				}
			}

			// Parse one or more JSONs
			try {
				return JSON.ParseStringToMultiple(dirtySourceString,new ParseStringSettings(){ ParseStartIndex=jsonStartIndex, AllowNonWhiteCharactersAfterObject=true });
			}
			catch (ParseException e) {
				return e.Message;
			}

		}

		private static string getEscapedString(string source) {
			int length=source.Length;
			StringBuilder sb=new StringBuilder(length);
			for (int n=0; n<length; n++) {
				if (source[n]=='"') {
					sb.Append('\\');
					sb.Append('"');
				} else if (source[n]=='\\') {
					sb.Append('\\');
					sb.Append('\\');
				} else {
					sb.Append(source[n]);
				}
			}
			return sb.ToString();
		}

	}

}
