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

namespace Azyobuzi.Twirunrun
{
    /// <summary>
    /// 拡張メソッドなど
    /// </summary>
    internal 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 OrderedParallelQuery<TSource> OrderBy<TSource, TKey>(this ParallelQuery<TSource> source, Func<TSource, TKey> keySelector, OrderTypes orderType)
        {
            var re = (OrderedParallelQuery<TSource>)orderType.SelectCase()
                .Case(OrderTypes.Ascending, () => source.OrderBy(keySelector))
                .Case(OrderTypes.Descending, () => source.OrderByDescending(keySelector))
                .Result;

            if (re == null)
                throw new ArgumentException("orderTypeが正しくありません。");

            return re;
        }

        public static OrderedParallelQuery<T> OrderBy<T>(this ParallelQuery<T> source, OrderTypes 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 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 Match MatchUri(this string source)
        {
            return source.RegexMatch(@"^https?://[\w\d/%#$&?()~_.=+-!]+$");
        }
    }

    //public class StringRegex
    //{
    //    public StringRegex(string source)
    //    {
    //        this._source = source;
    //    }

    //    private string _source;

    //    public bool IsMatch(string pattern)
    //    {
    //        return Regex.IsMatch(_source, pattern);
    //    }

    //    public Match Match(string pattern)
    //    {
    //        return Regex.Match(_source, pattern);
    //    }

    //    public IEnumerable<Match> Matches(string pattern)
    //    {
    //        return Regex.Matches(_source, pattern).Cast<Match>();
    //    }

    //    public string Replace(string pattern, string replacement)
    //    {
    //        return Regex.Replace(_source, pattern, replacement);
    //    }

    //    public string Replace(string pattern, MatchEvaluator evaluator)
    //    {
    //        return Regex.Replace(_source, pattern, evaluator);
    //    }

    //    public string[] Split(string pattern)
    //    {
    //        return Regex.Split(_source, pattern);
    //    }

    //    public override string ToString()
    //    {
    //        return _source;
    //    }
    //}

    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)
        {
            flag = true;
            action(selectObj);
            return this;
        }

        public Selecting<T> Default(Action action)
        {
            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)
        {
            flag = true;
            Result = action(selectObj);
            return this;
        }

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