using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;

namespace Vortex.Drawing {
	/// <summary>
	/// Contains methods to parse strings into <see cref="Vortex.Drawing"/> value structures
	/// </summary>
	public static class StringValueConverter {
		private static char[] COMPONENT_SEPARATOR = new char[] { ',' };
		private static char[] PART_SEPARATOR = new char[] { ':' };

		/// <summary>
		/// Gets the <see cref="Vector2"/> value from its string representation. Supported patterns: {X,Y}
		/// </summary>
		/// <param name="value">The string value.</param>
		/// <returns><see cref="Vector2"/> value.</returns>
		public static Vector2 GetVector2(string value) {
			if (null == value) throw new ArgumentNullException("value");

			string[] components = value.Split(COMPONENT_SEPARATOR, StringSplitOptions.RemoveEmptyEntries);
			if (components.Length == 2) {
				return new Vector2(
					float.Parse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture),
					float.Parse(components[1], NumberStyles.Number, CultureInfo.InvariantCulture)
				);
			} else {
				throw new StringConverterException(value, typeof(Vector2));
			}
		}

		/// <summary>
		/// Gets the <see cref="Vector3"/> value from its string representation. Supported patterns: {X,Y,Z}
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns><see cref="Vector3"/> value.</returns>
		public static Vector3 GetVector3(string value) {
			if (null == value) throw new ArgumentNullException("value");

			string[] components = value.Split(COMPONENT_SEPARATOR, StringSplitOptions.RemoveEmptyEntries);
			if (components.Length == 3) {
				return new Vector3(
					float.Parse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture),
					float.Parse(components[1], NumberStyles.Number, CultureInfo.InvariantCulture),
					float.Parse(components[2], NumberStyles.Number, CultureInfo.InvariantCulture)
				);
			} else {
				throw new StringConverterException(value, typeof(Vector3));
			}
		}

		/// <summary>
		/// Gets the <see cref="Rect"/> value from its string representation. Supported patterns: {L,T,R,B} and {L,T,W,H}
		/// </summary>
		/// <param name="value">The string value.</param>
		/// <returns><see cref="Rect"/> value.</returns>
		public static Rect GetRect(string value) {
			if (null == value) throw new ArgumentNullException("value");

			//check format 0,0:0,0 which defines lt,wh
			if (value.IndexOf(PART_SEPARATOR[0]) > 0) {
				string[] parts = value.Split(PART_SEPARATOR, StringSplitOptions.RemoveEmptyEntries);
				try {
					return Rect.FromBox(GetVector2(parts[0]), GetVector2(parts[1]));
				} catch (Exception) {
					throw new StringConverterException(value, typeof(Rect));
				}
			} else {
				string[] components = value.Split(COMPONENT_SEPARATOR, StringSplitOptions.RemoveEmptyEntries);
				if (components.Length == 4) {
					return new Rect(
						float.Parse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture),
						float.Parse(components[1], NumberStyles.Number, CultureInfo.InvariantCulture),
						float.Parse(components[2], NumberStyles.Number, CultureInfo.InvariantCulture),
						float.Parse(components[3], NumberStyles.Number, CultureInfo.InvariantCulture)
					);
				} else {
					throw new StringConverterException(value, typeof(Rect));
				}
			}
		}

		/// <summary>
		/// Gets the <see cref="ColorU"/> value from its string representation. 
		/// Supported patterns: {#RhGhBh}, {#AhRhGhBh}, {#RRhGGhBBh}, {#AAhRRhGGhBBh}, {R,G,B}, {A,R,G,B}
		/// </summary>
		/// <param name="value">The string value.</param>
		/// <returns><see cref="ColorU"/> value.</returns>
		public static ColorU GetColorU(string value) {
			if (null == value) throw new ArgumentNullException("value");
			
			//trims value, it is important for colors
			string stringValue = value;
			value = value.Trim();

			if (value.StartsWith("#")) {
				value = value.Substring(1); //cutting # char
				int valueLength = value.Length;
				byte r = 255, g = 255, b = 255, a = 255;
				//try to parse shorthand version
				if (valueLength == 3 || valueLength == 4) {
					int iR = int.Parse(value.Substring(valueLength - 3, 1), NumberStyles.HexNumber);
					int iG = int.Parse(value.Substring(valueLength - 2, 1), NumberStyles.HexNumber);
					int iB = int.Parse(value.Substring(valueLength - 1, 1), NumberStyles.HexNumber);
					r = (byte)(iR << 4 | iR);
					g = (byte)(iG << 4 | iG);
					b = (byte)(iB << 4 | iB);
					
					if (valueLength == 4) {
						int iA = int.Parse(value.Substring(0, 1), NumberStyles.HexNumber);
						a = (byte)(iA << 4 | iA);
					}
				//another option is full hex version of color notation
				} else if (valueLength == 6 || valueLength == 8) {
					r = byte.Parse(value.Substring(valueLength - 6, 2), NumberStyles.HexNumber);
					g = byte.Parse(value.Substring(valueLength - 4, 2), NumberStyles.HexNumber);
					b = byte.Parse(value.Substring(valueLength - 2, 2), NumberStyles.HexNumber);
					
					if (valueLength == 8) {
						a = byte.Parse(value.Substring(0, 2), NumberStyles.HexNumber);
					}
				} else {
					throw new StringConverterException(stringValue, typeof(ColorU));
				}
				//make final color
				return new ColorU(r, g, b, a);
			} else {
				string[] components = value.Split(COMPONENT_SEPARATOR, StringSplitOptions.RemoveEmptyEntries);
				if (components.Length == 3) {
					return new ColorU(
						byte.Parse(components[0], NumberStyles.Integer, CultureInfo.InvariantCulture),
						byte.Parse(components[1], NumberStyles.Integer, CultureInfo.InvariantCulture),
						byte.Parse(components[2], NumberStyles.Integer, CultureInfo.InvariantCulture)
					);
				} else if (components.Length == 4) {
					return new ColorU(
						byte.Parse(components[0], NumberStyles.Integer, CultureInfo.InvariantCulture),
						byte.Parse(components[1], NumberStyles.Integer, CultureInfo.InvariantCulture),
						byte.Parse(components[2], NumberStyles.Integer, CultureInfo.InvariantCulture),
						byte.Parse(components[3], NumberStyles.Integer, CultureInfo.InvariantCulture)
					);
				}
			}

			//try to obtain color from name
			System.Drawing.Color color = System.Drawing.Color.FromName(value);
			if (color.ToArgb() != 0) {
				return (ColorU)color;
			}
			//no way to obtain proper value
			throw new StringConverterException(stringValue, typeof(ColorU));
		}

	}

	#region Exception

	/// <summary>
	/// Exception of string value converter
	/// </summary>
	public class StringConverterException : Exception {

		public StringConverterException(string sourceValue, Type targetType)
			: base(string.Format("Unable to convert string '{0}' into value of type '{1}'", sourceValue, targetType.FullName)) {
		}

		/// <summary>
		/// Gets the source value for conversion.
		/// </summary>
		/// <value>The source value.</value>
		public string SourceValue {
			get;
			private set;
		}

		/// <summary>
		/// Gets the type of the target value.
		/// </summary>
		/// <value>The type of the target value.</value>
		public Type TargetType {
			get;
			private set;
		}
	}

	#endregion
}
