using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
using SocketHttpListener.Net;
using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse;
using HttpStatusCode = SocketHttpListener.Net.HttpStatusCode;
namespace SocketHttpListener
{
    /// 
    /// Provides a set of static methods for the websocket-sharp.
    /// 
    public static class Ext
    {
        #region Private Const Fields
        private const string _tspecials = "()<>@,;:\\\"/[]?={} \t";
        #endregion
        #region Private Methods
        private static MemoryStream compress(this Stream stream)
        {
            var output = new MemoryStream();
            if (stream.Length == 0)
                return output;
            stream.Position = 0;
            using (var ds = new DeflateStream(output, CompressionMode.Compress, true))
            {
                stream.CopyTo(ds);
                //ds.Close(); // "BFINAL" set to 1.
                output.Position = 0;
                return output;
            }
        }
        private static byte[] decompress(this byte[] value)
        {
            if (value.Length == 0)
                return value;
            using (var input = new MemoryStream(value))
            {
                return input.decompressToArray();
            }
        }
        private static MemoryStream decompress(this Stream stream)
        {
            var output = new MemoryStream();
            if (stream.Length == 0)
                return output;
            stream.Position = 0;
            using (var ds = new DeflateStream(stream, CompressionMode.Decompress, true))
            {
                ds.CopyTo(output, true);
                return output;
            }
        }
        private static byte[] decompressToArray(this Stream stream)
        {
            using (var decomp = stream.decompress())
            {
                return decomp.ToArray();
            }
        }
        private static byte[] readBytes(this Stream stream, byte[] buffer, int offset, int length)
        {
            var len = stream.Read(buffer, offset, length);
            if (len < 1)
                return buffer.SubArray(0, offset);
            var tmp = 0;
            while (len < length)
            {
                tmp = stream.Read(buffer, offset + len, length - len);
                if (tmp < 1)
                    break;
                len += tmp;
            }
            return len < length
                   ? buffer.SubArray(0, offset + len)
                   : buffer;
        }
        private static bool readBytes(
          this Stream stream, byte[] buffer, int offset, int length, Stream dest)
        {
            var bytes = stream.readBytes(buffer, offset, length);
            var len = bytes.Length;
            dest.Write(bytes, 0, len);
            return len == offset + length;
        }
        #endregion
        #region Internal Methods
        internal static byte[] Append(this ushort code, string reason)
        {
            using (var buffer = new MemoryStream())
            {
                var tmp = code.ToByteArrayInternally(ByteOrder.Big);
                buffer.Write(tmp, 0, 2);
                if (reason != null && reason.Length > 0)
                {
                    tmp = Encoding.UTF8.GetBytes(reason);
                    buffer.Write(tmp, 0, tmp.Length);
                }
                return buffer.ToArray();
            }
        }
        internal static string CheckIfClosable(this WebSocketState state)
        {
            return state == WebSocketState.Closing
                   ? "While closing the WebSocket connection."
                   : state == WebSocketState.Closed
                     ? "The WebSocket connection has already been closed."
                     : null;
        }
        internal static string CheckIfOpen(this WebSocketState state)
        {
            return state == WebSocketState.Connecting
                   ? "A WebSocket connection isn't established."
                   : state == WebSocketState.Closing
                     ? "While closing the WebSocket connection."
                     : state == WebSocketState.Closed
                       ? "The WebSocket connection has already been closed."
                       : null;
        }
        internal static string CheckIfValidControlData(this byte[] data, string paramName)
        {
            return data.Length > 125
                   ? String.Format("'{0}' length must be less.", paramName)
                   : null;
        }
        internal static string CheckIfValidSendData(this byte[] data)
        {
            return data == null
                   ? "'data' must not be null."
                   : null;
        }
        internal static string CheckIfValidSendData(this string data)
        {
            return data == null
                   ? "'data' must not be null."
                   : null;
        }
        internal static Stream Compress(this Stream stream, CompressionMethod method)
        {
            return method == CompressionMethod.Deflate
                   ? stream.compress()
                   : stream;
        }
        internal static bool Contains(this IEnumerable source, Func condition)
        {
            foreach (T elm in source)
                if (condition(elm))
                    return true;
            return false;
        }
        internal static void CopyTo(this Stream src, Stream dest, bool setDefaultPosition)
        {
            var readLen = 0;
            var bufferLen = 256;
            var buffer = new byte[bufferLen];
            while ((readLen = src.Read(buffer, 0, bufferLen)) > 0)
            {
                dest.Write(buffer, 0, readLen);
            }
            if (setDefaultPosition)
                dest.Position = 0;
        }
        internal static byte[] Decompress(this byte[] value, CompressionMethod method)
        {
            return method == CompressionMethod.Deflate
                   ? value.decompress()
                   : value;
        }
        internal static byte[] DecompressToArray(this Stream stream, CompressionMethod method)
        {
            return method == CompressionMethod.Deflate
                   ? stream.decompressToArray()
                   : stream.ToByteArray();
        }
        /// 
        /// Determines whether the specified  equals the specified ,
        /// and invokes the specified Action<int> delegate at the same time.
        /// 
        /// 
        /// true if  equals ;
        /// otherwise, false.
        /// 
        /// 
        /// An  to compare.
        /// 
        /// 
        /// A  to compare.
        /// 
        /// 
        /// An Action<int> delegate that references the method(s) called at
        /// the same time as comparing. An  parameter to pass to
        /// the method(s) is .
        /// 
        /// 
        ///  isn't between 0 and 255.
        /// 
        internal static bool EqualsWith(this int value, char c, Action action)
        {
            if (value < 0 || value > 255)
                throw new ArgumentOutOfRangeException("value");
            action(value);
            return value == c - 0;
        }
        internal static string GetMessage(this CloseStatusCode code)
        {
            return code == CloseStatusCode.ProtocolError
                   ? "A WebSocket protocol error has occurred."
                   : code == CloseStatusCode.IncorrectData
                     ? "An incorrect data has been received."
                     : code == CloseStatusCode.Abnormal
                       ? "An exception has occurred."
                       : code == CloseStatusCode.InconsistentData
                         ? "An inconsistent data has been received."
                         : code == CloseStatusCode.PolicyViolation
                           ? "A policy violation has occurred."
                           : code == CloseStatusCode.TooBig
                             ? "A too big data has been received."
                             : code == CloseStatusCode.IgnoreExtension
                               ? "WebSocket client did not receive expected extension(s)."
                               : code == CloseStatusCode.ServerError
                                 ? "WebSocket server got an internal error."
                                 : code == CloseStatusCode.TlsHandshakeFailure
                                   ? "An error has occurred while handshaking."
                                   : String.Empty;
        }
        internal static string GetNameInternal(this string nameAndValue, string separator)
        {
            var i = nameAndValue.IndexOf(separator);
            return i > 0
                   ? nameAndValue.Substring(0, i).Trim()
                   : null;
        }
        internal static string GetValueInternal(this string nameAndValue, string separator)
        {
            var i = nameAndValue.IndexOf(separator);
            return i >= 0 && i < nameAndValue.Length - 1
                   ? nameAndValue.Substring(i + 1).Trim()
                   : null;
        }
        internal static bool IsCompressionExtension(this string value, CompressionMethod method)
        {
            return value.StartsWith(method.ToExtensionString());
        }
        internal static bool IsPortNumber(this int value)
        {
            return value > 0 && value < 65536;
        }
        internal static bool IsReserved(this ushort code)
        {
            return code == (ushort)CloseStatusCode.Undefined ||
                   code == (ushort)CloseStatusCode.NoStatusCode ||
                   code == (ushort)CloseStatusCode.Abnormal ||
                   code == (ushort)CloseStatusCode.TlsHandshakeFailure;
        }
        internal static bool IsReserved(this CloseStatusCode code)
        {
            return code == CloseStatusCode.Undefined ||
                   code == CloseStatusCode.NoStatusCode ||
                   code == CloseStatusCode.Abnormal ||
                   code == CloseStatusCode.TlsHandshakeFailure;
        }
        internal static bool IsText(this string value)
        {
            var len = value.Length;
            for (var i = 0; i < len; i++)
            {
                char c = value[i];
                if (c < 0x20 && !"\r\n\t".Contains(c))
                    return false;
                if (c == 0x7f)
                    return false;
                if (c == '\n' && ++i < len)
                {
                    c = value[i];
                    if (!" \t".Contains(c))
                        return false;
                }
            }
            return true;
        }
        internal static bool IsToken(this string value)
        {
            foreach (char c in value)
                if (c < 0x20 || c >= 0x7f || _tspecials.Contains(c))
                    return false;
            return true;
        }
        internal static string Quote(this string value)
        {
            return value.IsToken()
                   ? value
                   : String.Format("\"{0}\"", value.Replace("\"", "\\\""));
        }
        internal static byte[] ReadBytes(this Stream stream, int length)
        {
            return stream.readBytes(new byte[length], 0, length);
        }
        internal static byte[] ReadBytes(this Stream stream, long length, int bufferLength)
        {
            using (var result = new MemoryStream())
            {
                var count = length / bufferLength;
                var rem = (int)(length % bufferLength);
                var buffer = new byte[bufferLength];
                var end = false;
                for (long i = 0; i < count; i++)
                {
                    if (!stream.readBytes(buffer, 0, bufferLength, result))
                    {
                        end = true;
                        break;
                    }
                }
                if (!end && rem > 0)
                    stream.readBytes(new byte[rem], 0, rem, result);
                return result.ToArray();
            }
        }
        internal static async Task ReadBytesAsync(this Stream stream, int length)
        {
            var buffer = new byte[length];
            var len = await stream.ReadAsync(buffer, 0, length).ConfigureAwait(false);
            var bytes = len < 1
                ? new byte[0]
                : len < length
                  ? stream.readBytes(buffer, len, length - len)
                  : buffer;
            return bytes;
        }
        internal static string RemovePrefix(this string value, params string[] prefixes)
        {
            var i = 0;
            foreach (var prefix in prefixes)
            {
                if (value.StartsWith(prefix))
                {
                    i = prefix.Length;
                    break;
                }
            }
            return i > 0
                   ? value.Substring(i)
                   : value;
        }
        internal static T[] Reverse(this T[] array)
        {
            var len = array.Length;
            T[] reverse = new T[len];
            var end = len - 1;
            for (var i = 0; i <= end; i++)
                reverse[i] = array[end - i];
            return reverse;
        }
        internal static IEnumerable SplitHeaderValue(
          this string value, params char[] separator)
        {
            var len = value.Length;
            var separators = new string(separator);
            var buffer = new StringBuilder(32);
            var quoted = false;
            var escaped = false;
            char c;
            for (var i = 0; i < len; i++)
            {
                c = value[i];
                if (c == '"')
                {
                    if (escaped)
                        escaped = !escaped;
                    else
                        quoted = !quoted;
                }
                else if (c == '\\')
                {
                    if (i < len - 1 && value[i + 1] == '"')
                        escaped = true;
                }
                else if (separators.Contains(c))
                {
                    if (!quoted)
                    {
                        yield return buffer.ToString();
                        buffer.Length = 0;
                        continue;
                    }
                }
                else {
                }
                buffer.Append(c);
            }
            if (buffer.Length > 0)
                yield return buffer.ToString();
        }
        internal static byte[] ToByteArray(this Stream stream)
        {
            using (var output = new MemoryStream())
            {
                stream.Position = 0;
                stream.CopyTo(output);
                return output.ToArray();
            }
        }
        internal static byte[] ToByteArrayInternally(this ushort value, ByteOrder order)
        {
            var bytes = BitConverter.GetBytes(value);
            if (!order.IsHostOrder())
                Array.Reverse(bytes);
            return bytes;
        }
        internal static byte[] ToByteArrayInternally(this ulong value, ByteOrder order)
        {
            var bytes = BitConverter.GetBytes(value);
            if (!order.IsHostOrder())
                Array.Reverse(bytes);
            return bytes;
        }
        internal static string ToExtensionString(
          this CompressionMethod method, params string[] parameters)
        {
            if (method == CompressionMethod.None)
                return String.Empty;
            var m = String.Format("permessage-{0}", method.ToString().ToLower());
            if (parameters == null || parameters.Length == 0)
                return m;
            return String.Format("{0}; {1}", m, parameters.ToString("; "));
        }
        internal static List ToList(this IEnumerable source)
        {
            return new List(source);
        }
        internal static ushort ToUInt16(this byte[] src, ByteOrder srcOrder)
        {
            return BitConverter.ToUInt16(src.ToHostOrder(srcOrder), 0);
        }
        internal static ulong ToUInt64(this byte[] src, ByteOrder srcOrder)
        {
            return BitConverter.ToUInt64(src.ToHostOrder(srcOrder), 0);
        }
        internal static string TrimEndSlash(this string value)
        {
            value = value.TrimEnd('/');
            return value.Length > 0
                   ? value
                   : "/";
        }
        internal static string Unquote(this string value)
        {
            var start = value.IndexOf('\"');
            var end = value.LastIndexOf('\"');
            if (start < end)
                value = value.Substring(start + 1, end - start - 1).Replace("\\\"", "\"");
            return value.Trim();
        }
        internal static void WriteBytes(this Stream stream, byte[] value)
        {
            using (var src = new MemoryStream(value))
            {
                src.CopyTo(stream);
            }
        }
        #endregion
        #region Public Methods
        /// 
        /// Determines whether the specified  contains any of characters
        /// in the specified array of .
        /// 
        /// 
        /// true if  contains any of ;
        /// otherwise, false.
        /// 
        /// 
        /// A  to test.
        /// 
        /// 
        /// An array of  that contains characters to find.
        /// 
        public static bool Contains(this string value, params char[] chars)
        {
            return chars == null || chars.Length == 0
                   ? true
                   : value == null || value.Length == 0
                     ? false
                     : value.IndexOfAny(chars) != -1;
        }
        /// 
        /// Determines whether the specified  contains the entry
        /// with the specified .
        /// 
        /// 
        /// true if  contains the entry
        /// with ; otherwise, false.
        /// 
        /// 
        /// A  to test.
        /// 
        /// 
        /// A  that represents the key of the entry to find.
        /// 
        public static bool Contains(this QueryParamCollection collection, string name)
        {
            return collection == null || collection.Count == 0
                   ? false
                   : collection[name] != null;
        }
        /// 
        /// Determines whether the specified  contains the entry
        /// with the specified both  and .
        /// 
        /// 
        /// true if  contains the entry
        /// with both  and ;
        /// otherwise, false.
        /// 
        /// 
        /// A  to test.
        /// 
        /// 
        /// A  that represents the key of the entry to find.
        /// 
        /// 
        /// A  that represents the value of the entry to find.
        /// 
        public static bool Contains(this QueryParamCollection collection, string name, string value)
        {
            if (collection == null || collection.Count == 0)
                return false;
            var values = collection[name];
            if (values == null)
                return false;
            foreach (var v in values.Split(','))
                if (v.Trim().Equals(value, StringComparison.OrdinalIgnoreCase))
                    return true;
            return false;
        }
        /// 
        /// Emits the specified  delegate if it isn't .
        /// 
        /// 
        /// A  to emit.
        /// 
        /// 
        /// An  from which emits this .
        /// 
        /// 
        /// A  that contains no event data.
        /// 
        public static void Emit(this EventHandler eventHandler, object sender, EventArgs e)
        {
            if (eventHandler != null)
                eventHandler(sender, e);
        }
        /// 
        /// Emits the specified EventHandler<TEventArgs> delegate
        /// if it isn't .
        /// 
        /// 
        /// An EventHandler<TEventArgs> to emit.
        /// 
        /// 
        /// An  from which emits this .
        /// 
        /// 
        /// A TEventArgs that represents the event data.
        /// 
        /// 
        /// The type of the event data generated by the event.
        /// 
        public static void Emit(
          this EventHandler eventHandler, object sender, TEventArgs e)
          where TEventArgs : EventArgs
        {
            if (eventHandler != null)
                eventHandler(sender, e);
        }
        /// 
        /// Gets the collection of the HTTP cookies from the specified HTTP .
        /// 
        /// 
        /// A  that receives a collection of the HTTP cookies.
        /// 
        /// 
        /// A  that contains a collection of the HTTP headers.
        /// 
        /// 
        /// true if  is a collection of the response headers;
        /// otherwise, false.
        /// 
        public static CookieCollection GetCookies(this QueryParamCollection headers, bool response)
        {
            var name = response ? "Set-Cookie" : "Cookie";
            return headers == null || !headers.Contains(name)
                   ? new CookieCollection()
                   : CookieHelper.Parse(headers[name], response);
        }
        /// 
        /// Gets the description of the specified HTTP status .
        /// 
        /// 
        /// A  that represents the description of the HTTP status code.
        /// 
        /// 
        /// One of  enum values, indicates the HTTP status codes.
        /// 
        public static string GetDescription(this HttpStatusCode code)
        {
            return ((int)code).GetStatusDescription();
        }
        /// 
        /// Gets the name from the specified  that contains a pair of name and
        /// value separated by a separator string.
        /// 
        /// 
        /// A  that represents the name if any; otherwise, null.
        /// 
        /// 
        /// A  that contains a pair of name and value separated by a separator
        /// string.
        /// 
        /// 
        /// A  that represents a separator string.
        /// 
        public static string GetName(this string nameAndValue, string separator)
        {
            return (nameAndValue != null && nameAndValue.Length > 0) &&
                   (separator != null && separator.Length > 0)
                   ? nameAndValue.GetNameInternal(separator)
                   : null;
        }
        /// 
        /// Gets the name and value from the specified  that contains a pair of
        /// name and value separated by a separator string.
        /// 
        /// 
        /// A KeyValuePair<string, string> that represents the name and value if any.
        /// 
        /// 
        /// A  that contains a pair of name and value separated by a separator
        /// string.
        /// 
        /// 
        /// A  that represents a separator string.
        /// 
        public static KeyValuePair GetNameAndValue(
          this string nameAndValue, string separator)
        {
            var name = nameAndValue.GetName(separator);
            var value = nameAndValue.GetValue(separator);
            return name != null
                   ? new KeyValuePair(name, value)
                   : new KeyValuePair(null, null);
        }
        /// 
        /// Gets the description of the specified HTTP status .
        /// 
        /// 
        /// A  that represents the description of the HTTP status code.
        /// 
        /// 
        /// An  that represents the HTTP status code.
        /// 
        public static string GetStatusDescription(this int code)
        {
            switch (code)
            {
                case 100: return "Continue";
                case 101: return "Switching Protocols";
                case 102: return "Processing";
                case 200: return "OK";
                case 201: return "Created";
                case 202: return "Accepted";
                case 203: return "Non-Authoritative Information";
                case 204: return "No Content";
                case 205: return "Reset Content";
                case 206: return "Partial Content";
                case 207: return "Multi-Status";
                case 300: return "Multiple Choices";
                case 301: return "Moved Permanently";
                case 302: return "Found";
                case 303: return "See Other";
                case 304: return "Not Modified";
                case 305: return "Use Proxy";
                case 307: return "Temporary Redirect";
                case 400: return "Bad Request";
                case 401: return "Unauthorized";
                case 402: return "Payment Required";
                case 403: return "Forbidden";
                case 404: return "Not Found";
                case 405: return "Method Not Allowed";
                case 406: return "Not Acceptable";
                case 407: return "Proxy Authentication Required";
                case 408: return "Request Timeout";
                case 409: return "Conflict";
                case 410: return "Gone";
                case 411: return "Length Required";
                case 412: return "Precondition Failed";
                case 413: return "Request Entity Too Large";
                case 414: return "Request-Uri Too Long";
                case 415: return "Unsupported Media Type";
                case 416: return "Requested Range Not Satisfiable";
                case 417: return "Expectation Failed";
                case 422: return "Unprocessable Entity";
                case 423: return "Locked";
                case 424: return "Failed Dependency";
                case 500: return "Internal Server Error";
                case 501: return "Not Implemented";
                case 502: return "Bad Gateway";
                case 503: return "Service Unavailable";
                case 504: return "Gateway Timeout";
                case 505: return "Http Version Not Supported";
                case 507: return "Insufficient Storage";
            }
            return String.Empty;
        }
        /// 
        /// Gets the value from the specified  that contains a pair of name and
        /// value separated by a separator string.
        /// 
        /// 
        /// A  that represents the value if any; otherwise, null.
        /// 
        /// 
        /// A  that contains a pair of name and value separated by a separator
        /// string.
        /// 
        /// 
        /// A  that represents a separator string.
        /// 
        public static string GetValue(this string nameAndValue, string separator)
        {
            return (nameAndValue != null && nameAndValue.Length > 0) &&
                   (separator != null && separator.Length > 0)
                   ? nameAndValue.GetValueInternal(separator)
                   : null;
        }
        /// 
        /// Determines whether the specified  is host
        /// (this computer architecture) byte order.
        /// 
        /// 
        /// true if  is host byte order;
        /// otherwise, false.
        /// 
        /// 
        /// One of the  enum values, to test.
        /// 
        public static bool IsHostOrder(this ByteOrder order)
        {
            // true : !(true ^ true)  or !(false ^ false)
            // false: !(true ^ false) or !(false ^ true)
            return !(BitConverter.IsLittleEndian ^ (order == ByteOrder.Little));
        }
        /// 
        /// Determines whether the specified  is a predefined scheme.
        /// 
        /// 
        /// true if  is a predefined scheme; otherwise, false.
        /// 
        /// 
        /// A  to test.
        /// 
        public static bool IsPredefinedScheme(this string value)
        {
            if (value == null || value.Length < 2)
                return false;
            var c = value[0];
            if (c == 'h')
                return value == "http" || value == "https";
            if (c == 'w')
                return value == "ws" || value == "wss";
            if (c == 'f')
                return value == "file" || value == "ftp";
            if (c == 'n')
            {
                c = value[1];
                return c == 'e'
                       ? value == "news" || value == "net.pipe" || value == "net.tcp"
                       : value == "nntp";
            }
            return (c == 'g' && value == "gopher") || (c == 'm' && value == "mailto");
        }
        /// 
        /// Determines whether the specified  is a URI string.
        /// 
        /// 
        /// true if  may be a URI string; otherwise, false.
        /// 
        /// 
        /// A  to test.
        /// 
        public static bool MaybeUri(this string value)
        {
            if (value == null || value.Length == 0)
                return false;
            var i = value.IndexOf(':');
            if (i == -1)
                return false;
            if (i >= 10)
                return false;
            return value.Substring(0, i).IsPredefinedScheme();
        }
        /// 
        /// Retrieves a sub-array from the specified .
        /// A sub-array starts at the specified element position.
        /// 
        /// 
        /// An array of T that receives a sub-array, or an empty array of T if any problems
        /// with the parameters.
        /// 
        /// 
        /// An array of T that contains the data to retrieve a sub-array.
        /// 
        /// 
        /// An  that contains the zero-based starting position of a sub-array
        /// in .
        /// 
        /// 
        /// An  that contains the number of elements to retrieve a sub-array.
        /// 
        /// 
        /// The type of elements in the .
        /// 
        public static T[] SubArray(this T[] array, int startIndex, int length)
        {
            if (array == null || array.Length == 0)
                return new T[0];
            if (startIndex < 0 || length <= 0)
                return new T[0];
            if (startIndex + length > array.Length)
                return new T[0];
            if (startIndex == 0 && array.Length == length)
                return array;
            T[] subArray = new T[length];
            Array.Copy(array, startIndex, subArray, 0, length);
            return subArray;
        }
        /// 
        /// Converts the order of the specified array of  to the host byte order.
        /// 
        /// 
        /// An array of  converted from .
        /// 
        /// 
        /// An array of  to convert.
        /// 
        /// 
        /// One of the  enum values, indicates the byte order of
        /// .
        /// 
        /// 
        ///  is .
        /// 
        public static byte[] ToHostOrder(this byte[] src, ByteOrder srcOrder)
        {
            if (src == null)
                throw new ArgumentNullException("src");
            return src.Length > 1 && !srcOrder.IsHostOrder()
                   ? src.Reverse()
                   : src;
        }
        /// 
        /// Converts the specified  to a  that
        /// concatenates the each element of  across the specified
        /// .
        /// 
        /// 
        /// A  converted from ,
        /// or  if  is empty.
        /// 
        /// 
        /// An array of T to convert.
        /// 
        /// 
        /// A  that represents the separator string.
        /// 
        /// 
        /// The type of elements in .
        /// 
        /// 
        ///  is .
        /// 
        public static string ToString(this T[] array, string separator)
        {
            if (array == null)
                throw new ArgumentNullException("array");
            var len = array.Length;
            if (len == 0)
                return String.Empty;
            if (separator == null)
                separator = String.Empty;
            var buff = new StringBuilder(64);
            (len - 1).Times(i => buff.AppendFormat("{0}{1}", array[i].ToString(), separator));
            buff.Append(array[len - 1].ToString());
            return buff.ToString();
        }
        /// 
        /// Executes the specified Action<int> delegate  times.
        /// 
        /// 
        /// An  is the number of times to execute.
        /// 
        /// 
        /// An Action<int> delegate that references the method(s) to execute.
        /// An  parameter to pass to the method(s) is the zero-based count of
        /// iteration.
        /// 
        public static void Times(this int n, Action action)
        {
            if (n > 0 && action != null)
                for (int i = 0; i < n; i++)
                    action(i);
        }
        /// 
        /// Converts the specified  to a .
        /// 
        /// 
        /// A  converted from , or 
        /// if  isn't successfully converted.
        /// 
        /// 
        /// A  to convert.
        /// 
        public static Uri ToUri(this string uriString)
        {
            Uri res;
            return Uri.TryCreate(
                     uriString, uriString.MaybeUri() ? UriKind.Absolute : UriKind.Relative, out res)
                   ? res
                   : null;
        }
        /// 
        /// URL-decodes the specified .
        /// 
        /// 
        /// A  that receives the decoded string, or the 
        /// if it's  or empty.
        /// 
        /// 
        /// A  to decode.
        /// 
        public static string UrlDecode(this string value)
        {
            return value == null || value.Length == 0
                   ? value
                   : WebUtility.UrlDecode(value);
        }
        #endregion
    }
}