using System;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace Cryville.Common.Unity {
	public class PropItem : MonoBehaviour {
		public object Target;
		public PropertyEditor editor;
		public DirectoryInfo ContextPath;

		public string PropertyName = "";
		InputField valueObj;
		string value;
		string desc = "";
		PropertyInfo prop;
		Type bindToType;
		TypeConverter converter;

		bool mustExpand = false;
		bool readOnly = false;
		string[] filter = null;
		FileDialog fdialog;

#pragma warning disable IDE0051
		void Awake() {
			transform.Find("Value").GetComponent<InputField>().onEndEdit.AddListener(s => OnValueChanged(s));
			var entry = new EventTrigger.Entry(){
				eventID = EventTriggerType.PointerClick,
				callback = new EventTrigger.TriggerEvent()
			};
			entry.callback.AddListener(e => OnClick(e));
			transform.Find("Value").GetComponent<EventTrigger>().triggers.Add(entry);
		}

		void Start() {
			transform.Find("Name").GetComponent<Text>().text = PropertyName;
			valueObj = transform.Find("Value").GetComponent<InputField>();
			prop = Target.GetType().GetProperty(PropertyName);
			bindToType = prop.PropertyType;
			converter = TypeDescriptor.GetConverter(bindToType);
			
			var descattr = (DescriptionAttribute[])prop.GetCustomAttributes(typeof(DescriptionAttribute), true);
			if (descattr.Length > 0) desc = descattr[0].Description;
			
			var roattr = (ReadOnlyAttribute[])prop.GetCustomAttributes(typeof(ReadOnlyAttribute), true);
			if (roattr.Length > 0) if (roattr[0].IsReadOnly == true) {
				readOnly = true;
				valueObj.enabled = false;
				valueObj.transform.Find("ValueString").GetComponent<Text>().color = Color.gray;
			}

			if (converter == null || !converter.CanConvertFrom(typeof(string)) || converter.GetPropertiesSupported()){
				mustExpand = true;
				valueObj.enabled = false;
			}
			
			var fsattr = (FileStringAttribute[])prop.GetCustomAttributes(typeof(FileStringAttribute), true);
			if (fsattr.Length > 0) {
				valueObj.enabled = false;
				filter = fsattr[0].Filter;
			}

			UpdateValue();
		}
#pragma warning restore IDE0051

		void OnFileDialogClosed() {
			var file = fdialog.GetComponent<FileDialog>().FileName;
			if (file != "") {
				var f = new FileInfo(file);
				if (f.Directory.FullName != ContextPath.FullName) {
					string targetFile = ContextPath.FullName + "\\" + f.Name;
					f.CopyTo(targetFile, true);
				}
				OnValueChanged(f.Name);
			}
		}
		
		void UpdateValue() {
			object v;
			valueObj.placeholder.GetComponent<Text>().text = "";
			v = prop.GetValue(Target, new object[]{ });
			if (v == null) {
				DefaultValueAttribute[] defvattr = (DefaultValueAttribute[])prop.GetCustomAttributes(typeof(DefaultValueAttribute), true);
				if (defvattr.Length > 0) {
					v = defvattr[0].Value;
					if (v == null) {
						v = "";
						valueObj.placeholder.GetComponent<Text>().text = "null";
					}
				}
				else {
					v = "";
					valueObj.placeholder.GetComponent<Text>().text = "null";
				}
			}
			
			if (mustExpand || readOnly || filter != null) {
				valueObj.transform.Find("ValueString").GetComponent<Text>().text = v.ToString();
			}
			else {
				valueObj.text = v.ToString();
			}
			value = valueObj.text;
		}
		
		void OnClick(BaseEventData e) {
			if (mustExpand && !readOnly) {
				GameObject subeditor = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Common/PropertyEditor"));
				object obj = prop.GetValue(Target, new object[]{ });
				subeditor.GetComponent<PropertyEditor>().TargetObject = obj;
			}
			else if (filter != null && !readOnly) {
				fdialog = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Common/FileDialog")).GetComponent<FileDialog>();
				fdialog.Filter = filter;
				fdialog.CurrentDirectory = ContextPath;
				fdialog.Callback = () => OnFileDialogClosed();
			}
			editor.SetDescription(PropertyName, desc);
			UpdateValue();
		}
		
		void OnValueChanged(string s) {
			if (s == value) return;
			object v = null;
			if (!(mustExpand || readOnly)) {
				try {
					v = converter.ConvertFrom(s);
					prop.SetValue(Target, v, new object[]{ });
				}
				catch (TargetInvocationException ex) {
					// CallHelper.ShowMessageBox(ex.InnerException.Message);
				}
				catch (Exception ex) {
					// CallHelper.ShowMessageBox(ex.Message);
				}
			}
			UpdateValue();
		}
	}
}
