using System;
using System.Collections.Generic;
using System.Text;
using Vortex.Drawing;
using System.Globalization;
using Vortex.SceneFramework.CSS;
using Vortex.SceneFramework.Gui;
using Vortex.Debugging;

namespace Vortex.SceneFramework {
	/// <summary>
	/// Implementation of property resolver for specified domain
	/// </summary>
	public class ScenePropertyResolver : IPropertyResolver {
		private DomainNode _scene;
		private readonly Type SPRITE_TYPE = typeof(Sprite);
		private readonly Type SPRITE_SOURCE_TYPE = typeof(ISpriteSource);
		private readonly Type SPRITEFONT_TYPE = typeof(SpriteFont);
		private readonly Type GAMECURSOR_TYPE = typeof(GameCursor);

		private static readonly Dictionary<Type, ParseCallback> _typeParsers = new Dictionary<Type, ParseCallback>();

		/// <summary>
		/// Initializes a new instance of the <see cref="ScenePropertyResolver"/> class.
		/// </summary>
		/// <param name="domain">The target domain.</param>
		public ScenePropertyResolver(DomainNode scene) {
			if (null == scene) throw new ArgumentNullException("scene");
			_scene = scene;
		}

		static ScenePropertyResolver() {
			//default parser of string
			_typeParsers.Add(typeof(string), delegate(string value) {
				return value;
			});

			//default parser for boolean
			_typeParsers.Add(typeof(bool), delegate(string value) {
				return bool.Parse(value.ToLower());
			});
			//default parser of int
			_typeParsers.Add(typeof(int), delegate(string value) {
				return int.Parse(value, NumberStyles.Number, CultureInfo.InvariantCulture);
			});
			//default parser of float
			_typeParsers.Add(typeof(float), delegate(string value) {
				return float.Parse(value, NumberStyles.Number, CultureInfo.InvariantCulture);
			});
			//default parser of double
			_typeParsers.Add(typeof(double), delegate(string value) {
				return double.Parse(value, NumberStyles.Number, CultureInfo.InvariantCulture);
			});

			//Vortex Drawing struct parsers
			_typeParsers.Add(typeof(Vector2), delegate(string stringValue) {
				return StringValueConverter.GetVector2(stringValue);
			});
			_typeParsers.Add(typeof(Vector3), delegate(string stringValue) {
				return StringValueConverter.GetVector3(stringValue);
			});
			_typeParsers.Add(typeof(Rect), delegate(string stringValue) {
				return StringValueConverter.GetRect(stringValue);
			});
			_typeParsers.Add(typeof(ColorU), delegate(string stringValue) {
				return StringValueConverter.GetColorU(stringValue);
			});

			//Vortex.Drawing.* advanced types

			//blending mode parsing
			_typeParsers.Add(typeof(Blending), delegate(string value) {
				switch (value.Trim().ToLower()) {
					case "none": return Blending.None;
					case "normal": return Blending.Normal;
					case "add": return Blending.Add;
					case "subtract": return Blending.Subtract;
					default:
						throw new StyleSheetException("Unable to convert string '{0}' into Vortex.Drawing.Blending.", value);
				}
			});

			//text layout parsing
			_typeParsers.Add(typeof(TextLayout), delegate(string value) {
				TextLayout layout = TextLayout.Default;

				string[] flags = value.Trim().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
				foreach (string flag in flags) {
					TextLayout layoutFlag = (TextLayout)Enum.Parse(typeof(TextLayout), flag.Trim(), true);
					layout |= layoutFlag;
				}

				return layout;
			});

			//panel layout parsing
			_typeParsers.Add(typeof(FlowLayout), delegate(string value) {
				FlowLayout layout = FlowLayout.Default;

				string[] flags = value.Trim().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
				foreach (string flag in flags) {
					FlowLayout layoutFlag = (FlowLayout)Enum.Parse(typeof(FlowLayout), flag.Trim(), true);
					layout |= layoutFlag;
				}

				return layout;
			});

			//add dock mode parser
			_typeParsers.Add(typeof(DockMode), delegate(string value) {
				return Enum.Parse(typeof(DockMode), value, true);
			});

		}

		#region IPropertyResolver Members

		/// <summary>
		/// Resolves the property string into CLR object of specified type.
		/// </summary>
		/// <param name="propertyName">Name of the property.</param>
		/// <param name="propertyType">Type of the property object.</param>
		/// <param name="propertyValue">The string property value to resolve.</param>
		/// <returns>
		/// Property value object matching specified type; or <c>null</c> if resolving failed.
		/// </returns>
		public object ResolveProperty(string propertyName, Type propertyType, string propertyValue) {
			if (null == propertyType) throw new ArgumentNullException("propertyType");
			if (null == propertyValue) throw new ArgumentNullException("propertyValue");

			//try to resolve simple types
			ParseCallback parseCallback;
			if (_typeParsers.TryGetValue(propertyType, out parseCallback)) {
				return parseCallback(propertyValue);
			}

			propertyValue = EscapeQuotes(propertyValue.Trim());

			//try to parse domain resource types
			if (SPRITE_TYPE.Equals(propertyType)) {
				return _scene.Resources.Get<Sprite>(propertyValue);
			} if (SPRITE_SOURCE_TYPE.Equals(propertyType)) {
				return _scene.Resources.Get<ISpriteSource>(propertyValue);
			} else if (SPRITEFONT_TYPE.Equals(propertyType)) {

				if ("Default".Equals(propertyValue, StringComparison.InvariantCultureIgnoreCase)) return SpriteFont.Default;
				if ("Console".Equals(propertyValue, StringComparison.InvariantCultureIgnoreCase)) return SpriteFont.Console;
				if ("SmallConsole".Equals(propertyValue, StringComparison.InvariantCultureIgnoreCase)) return SpriteFont.SmallConsole;
				//if no default font - try read it from collection
				return _scene.Resources.Get<SpriteFont>(propertyValue);
			} else if (GAMECURSOR_TYPE.Equals(propertyType)) {
				return _scene.Resources.Get<GameCursor>(propertyValue);
			}

			return null;
		}

		/// <summary>
		/// Escapes the quotes.
		/// </summary>
		/// <param name="stringValue">The string value.</param>
		/// <returns></returns>
		private string EscapeQuotes(string stringValue) {
			int stringLength = stringValue.Length;
			if (stringLength > 2) {
				char firstChar = stringValue[0];
				if (firstChar == '\'' || firstChar == '"') {
					if (firstChar == stringValue[stringLength - 1]) {
						return stringValue.Substring(1, stringLength - 2);
					}
				}
			}
			return stringValue;
		}

		#endregion
	}
}
