using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Windows.Forms;
using System.Xml.Linq;

namespace Azyobuzi.Twirunrun
{
    /// <summary>
    /// 拡張メソッドなど
    /// </summary>
    public static class Extensions
    {
        #region TryCatch
        public static void TryCatch(this Action action, Action<Exception> _catch)
        {
            try
            {
                action();
            }
            catch (Exception ex)
            {
                _catch(ex);
            }
        }

        public static void TryCatch<TException>(this Action action, Action<TException> _catch)
            where TException : Exception
        {
            try
            {
                action();
            }
            catch (TException ex)
            {
                _catch(ex);
            }
        }

        public static TResult TryCatch<TResult>(this Func<TResult> action, Func<Exception, TResult> _catch)
        {
            try
            {
                return action();
            }
            catch (Exception ex)
            {
                return _catch(ex);
            }
        }

        public static TResult TryCatch<TResult, TException>(this Func<TResult> action, Func<TException, TResult> _catch)
            where TException : Exception
        {
            try
            {
                return action();
            }
            catch (TException ex)
            {
                return _catch(ex);
            }
        }
        #endregion

        #region OrderByの拡張
        public static ParallelQuery<TSource> OrderBy<TSource, TKey>(this ParallelQuery<TSource> source, Func<TSource, TKey> keySelector, SortOrder orderType)
        {
            return orderType.SelectCase()
                .Case(SortOrder.Ascending, () => source.OrderBy(keySelector))
                .Case(SortOrder.Descending, () => source.OrderByDescending(keySelector))
                .Default(() => source)
                .Result as ParallelQuery<TSource>;
        }

        public static ParallelQuery<T> OrderBy<T>(this ParallelQuery<T> source, SortOrder orderType)
        {
            return OrderBy(source, o => o, orderType);
        }
        #endregion

        #region 正規表現拡張
        //public static StringRegex Regex(this string source)
        //{
        //    return new StringRegex(source);
        //}

        public static string RegexEscape(this string source)
        {
            return System.Text.RegularExpressions.Regex.Escape(source);
        }

        public static bool RegexIsMatch(this string source, string pattern, RegexOptions options = RegexOptions.None)
        {
            return Regex.IsMatch(source, pattern, options);
        }

        public static Match RegexMatch(this string source, string pattern, RegexOptions options = RegexOptions.None)
        {
            return Regex.Match(source, pattern, options);
        }

        public static MatchCollection RegexMatches(this string source, string pattern, RegexOptions options = RegexOptions.None)
        {
            return Regex.Matches(source, pattern, options);
        }

        public static string RegexReplace(this string source, string pattern, string replacement, RegexOptions options = RegexOptions.None)
        {
            return Regex.Replace(source, pattern, replacement, options);
        }

        public static string RegexReplace(this string source, string pattern, MatchEvaluator evaluator, RegexOptions options = RegexOptions.None)
        {
            return Regex.Replace(source, pattern, evaluator, options);
        }

        public static string[] RegexSplit(this string source, string pattern, RegexOptions options = RegexOptions.None)
        {
            return Regex.Split(source, pattern, options);
        }
        #endregion

        #region SelectCase
        public static Selecting<T> SelectCase<T>(this T selectObj)
        {
            return SelectCase(selectObj, EqualityComparer<T>.Default.Equals);
        }

        public static Selecting<T> SelectCase<T>(this T selectObj, Func<T, T, bool> equalsFunc)
        {
            return new Selecting<T>(selectObj, equalsFunc);
        }
        #endregion

        #region GetString
        public static string GetString(this byte[] bs)
        {
            return Encoding.UTF8.GetString(bs);
        }

        public static string GetString(this byte[] bs, Encoding enc)
        {
            return enc.GetString(bs);
        }
        #endregion

        public static void ForEach<T>(this IEnumerable<T> collection, Action<T> action)
        {
            foreach (T item in collection) action(item);
        }

        public static bool EmptyOrNull(this IEnumerable collection)
        {
            return collection == null || !collection.GetEnumerator().MoveNext();
        }

        public static Color ConvertToColor(this string source)
        {
            return (Color)new ColorConverter().ConvertFromString(source);
        }

        public static string ConvertToString(this Color source)
        {
            return new ColorConverter().ConvertToString(source);
        }

        public static Font ConvertToFont(this string source)
        {
            return (Font)new FontConverter().ConvertFromString(source);
        }

        public static string ConvertToString(this Font source)
        {
            return new FontConverter().ConvertToString(source);
        }

        public static XElement GetElementById(this XContainer source, string id)
        {
            return source.Descendants()
                .SingleOrDefault(xe =>
                {
                    var idattr = xe.Attribute("id");
                    return idattr != null && idattr.Value == id;
                });
        }

        #region IgnoreException
        public static void IgnoreException(this Action action)
        {
            try
            {
                action();
            }
            catch { }
        }

        public static TResult IgnoreException<TResult>(this Func<TResult> func)
        {
            try
            {
                return func();
            }
            catch
            {
                return default(TResult);
            }
        }

        public static TResult IgnoreException<TResult>(this Func<TResult> func, TResult @default)
        {
            try
            {
                return func();
            }
            catch
            {
                return @default;
            }
        }
        #endregion

        #region DownloadImage
        public static Image DownloadImage(this WebClient wc, string address)
        {
            using (var stream = wc.OpenRead(address))
            {
                return Image.FromStream(stream);
            }
        }

        public static Image DownloadImage(this WebClient wc, Uri address)
        {
            using (var stream = wc.OpenRead(address))
            {
                return Image.FromStream(stream);
            }
        }
        #endregion

        public static object[] CreateArray(params object[] obj)
        {
            return obj;
        }

        public static object[] ToObjectArray(this object obj)
        {
            return CreateArray(obj);
        }

        public static string HtmlEncode(this string source)
        {
            return HttpUtility.HtmlEncode(source);
        }

        public static string HtmlDecode(this string source)
        {
            return HttpUtility.HtmlDecode(source);
        }

        public static string HtmlAttributeEncode(this string source)
        {
            return HttpUtility.HtmlAttributeEncode(source);
        }

        public static Dictionary<string, string> ToDictionary(this NameValueCollection source)
        {
            return source.Keys.Cast<string>().ToDictionary(_ => _, _ => source[_]);
        }

        #region ToBytes
        public static byte[] ToBytes(this string source, Encoding enc)
        {
            return enc.GetBytes(source);
        }

        public static byte[] ToBytes(this string source)
        {
            return source.ToBytes(Encoding.UTF8);
        }
        #endregion

        public static void WriteAll(this Stream stream, byte[] buffer)
        {
            stream.Write(buffer, 0, buffer.Length);
        }

        public static ITwirunrunMainForm GetMainForm(this Control control)
        {
            return control.FindForm() as ITwirunrunMainForm;
        }
    }

    public class Selecting<T>
    {
        public Selecting(T selectObj, Func<T, T, bool> equalsFunc)
        {
            this.selectObj = selectObj;
            this.equalsFunc = equalsFunc;
        }

        private T selectObj;
        private Func<T, T, bool> equalsFunc;
        private bool flag = false;

        public object Result { private set; get; }

        public Selecting<T> Case(T caseObj, Action<T> action)
        {
            if (!flag && equalsFunc(caseObj, selectObj))
            {
                flag = true;
                action(selectObj);
            }
            return this;
        }

        public Selecting<T> Case(T caseObj, Action action)
        {
            if (!flag && equalsFunc(caseObj, selectObj))
            {
                flag = true;
                action();
            }
            return this;
        }

        public Selecting<T> Default(Action<T> action)
        {
            if (!flag)
            {
                flag = true;
                action(selectObj);
            }
            return this;
        }

        public Selecting<T> Default(Action action)
        {
            if (!flag)
            {
                flag = true;
                action();
            }
            return this;
        }

        public Selecting<T> Case(T caseObj, Func<T, object> action)
        {
            if (!flag && equalsFunc(caseObj, selectObj))
            {
                flag = true;
                Result = action(selectObj);
            }
            return this;
        }

        public Selecting<T> Case(T caseObj, Func<object> action)
        {
            if (!flag && equalsFunc(caseObj, selectObj))
            {
                flag = true;
                Result = action();
            }
            return this;
        }

        public Selecting<T> Default(Func<T, object> action)
        {
            if (!flag)
            {
                flag = true;
                Result = action(selectObj);
            }
            return this;
        }

        public Selecting<T> Default(Func<object> action)
        {
            if (!flag)
            {
                flag = true;
                Result = action();
            }
            return this;
        }
    }
}
