using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.SqlTypes;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web.Mvc;

namespace MileageTraker.Web.Utility
{
	public static class CustomExtensions
	{
		public static string Wordify(this string str)
		{
			return str.Aggregate(
				String.Empty, 
				(current, c) => 
					current + (Char.IsUpper(c) ? " " + c : c.ToString()));
		}

		private static T GetAttribute<T>(this Enum enumeration) where T : Attribute
		{
			var type = enumeration.GetType();
			var memInfo = type.GetMember(enumeration.ToString());
			if (memInfo.Length > 0)
			{
				var attributes = memInfo[0].GetCustomAttributes(typeof (T), false);
				if (attributes.Length > 0)
					return (T) attributes[0];
			}
			return null;
		}

		public static string GetDisplayName(this Enum enumeration)
		{
			var displayAttribute = GetAttribute<DisplayAttribute>(enumeration);
			return displayAttribute != null ? displayAttribute.Name : enumeration.ToString();
		}

		public static string GetDisplayShortName(this Enum enumeration)
		{
			var displayAttribute = GetAttribute<DisplayAttribute>(enumeration);
			return displayAttribute != null
			       	? (!String.IsNullOrEmpty(displayAttribute.ShortName) 
						? displayAttribute.ShortName 
						: displayAttribute.Name)
			       	: enumeration.ToString();
		}

		public static IEnumerable<SelectListItem> GetSelectListItems(this Enum enumeration)
		{
			var type = enumeration.GetType();
			return Enum.GetValues(type)
				.OfType<Enum>().Select(e =>
				new SelectListItem
				{
					Text = e.GetDisplayName(),
					Value = e.ToString(),
					Selected = e.Equals(enumeration)
				});
		}

		public static IEnumerable<SelectListItem> ToSelectList(this Type enumType, string selectedItem, string noItemSelected)
		{
			var items = (from item in Enum.GetValues(enumType).OfType<Enum>()
			             let title = item.GetDisplayName()
			             select new SelectListItem
			                    	{
										Value = item.ToString(),
										Text = title,
										Selected = selectedItem == item.ToString()
			                    	}).ToList();

			return new SelectList(new[]{new SelectListItem{Text = noItemSelected}}.Concat(items), "Value", "Text");
		}
		
		public static string GetName(this MemberInfo propertyMember)
		{
			var displayAttr = propertyMember.GetCustomAttributes(typeof(DisplayAttribute), true).FirstOrDefault() as DisplayAttribute;
			return displayAttr != null
				? displayAttr.Name
				: propertyMember.Name;
		}

		public static object GetValue(this PropertyInfo propertyMember, object obj)
		{
			var value = propertyMember.GetValue(obj, null);
			var displayAttr = propertyMember.GetCustomAttributes(typeof(DisplayFormatAttribute), true).FirstOrDefault() as DisplayFormatAttribute;

			if (displayAttr != null)
			{
				if (value == null && !String.IsNullOrEmpty(displayAttr.NullDisplayText))
					return displayAttr.NullDisplayText;

				if (!String.IsNullOrEmpty(displayAttr.DataFormatString))
					return String.Format(displayAttr.DataFormatString, value);
			}
			return value;
		}

		public static bool IsSqlMinValue(this DateTime dt)
		{
			return dt == SqlDateTime.MinValue.Value;
		}

		public static string ToVerboseStringHistoric(this TimeSpan ts)
		{
			if (ts.TotalDays < 1)
				return "Today";
			if (ts.TotalDays < 2)
				return "Yesterday";

			var sb = new StringBuilder();

			Action<int, string> b = (var, name) =>
			{
				if (var > 0)
				{
					if (sb.Length > 0)
						sb.Append(", ");
					sb.Append(var + " " + name);
					if (var > 1)
						sb.Append("s");
				}
			};

			var timeLeft = ts;

			var years = timeLeft.Days / 365; //no leap year accounting
			b(years, "year");
			timeLeft -= new TimeSpan(years*365, 0, 0, 0);
			
			var months = timeLeft.Days / 30; //naive guess at month size
			b(months, "month");
			timeLeft -= new TimeSpan(months * 30, 0, 0, 0);

			var weeks = timeLeft.Days / 7;
			b(weeks, "week");
			timeLeft -= new TimeSpan(weeks * 7, 0, 0, 0);

			var days = timeLeft.Days;
			b(days, "day");
		
			sb.Append(" ago");
			return sb.ToString();
		}

		public static bool IsCollection(this Type type)
		{
			Func<Type, bool> isCollection =
				t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof (ICollection<>);

			return isCollection(type) || type.GetInterfaces().Any(isCollection);
		}

		public static string LowercaseFirst(string s)
		{
			// Check for empty string.
			if (String.IsNullOrEmpty(s))
			{
				return String.Empty;
			}
			// Return char and concat substring.
			return Char.ToLower(s[0]) + s.Substring(1);
		}

		public static IEnumerable<int> GetNumbers()
		{
			var i = 0;
			while (true) yield return i++;
		}
		
		/// <summary>
		/// Users with activity date greater than this value are online
		/// </summary>
		public static DateTime UserOnlineThreshold()
		{
			return DateTime.Now.Subtract(
				TimeSpan.FromMinutes(
					Convert.ToDouble(
						System.Web.Security.Membership.UserIsOnlineTimeWindow)));
		}
	}
}