using System;
using System.Collections.Generic;
#if !SILVERLIGHT
using System.Diagnostics;
#endif
using System.Globalization;
using System.Net;
using Renci.SshNet.Messages;
using Renci.SshNet.Messages.Connection;
namespace Renci.SshNet.Common
{
    /// 
    /// Collection of different extension method
    /// 
    internal static partial class Extensions
    {
        internal static byte[] ToArray(this GlobalRequestName globalRequestName)
        {
            switch (globalRequestName)
            {
                case GlobalRequestName.TcpIpForward:
                    return SshData.Ascii.GetBytes("tcpip-forward");
                case GlobalRequestName.CancelTcpIpForward:
                    return SshData.Ascii.GetBytes("cancel-tcpip-forward");
                default:
                    throw new NotSupportedException(string.Format("Global request name '{0}' is not supported.", globalRequestName));
            }
        }
        internal static byte[] ToArray(this ServiceName serviceName)
        {
            switch (serviceName)
            {
                case ServiceName.UserAuthentication:
                    return SshData.Ascii.GetBytes("ssh-userauth");
                case ServiceName.Connection:
                    return SshData.Ascii.GetBytes("ssh-connection");
                default:
                    throw new NotSupportedException(string.Format("Service name '{0}' is not supported.", serviceName));
            }
        }
        internal static ServiceName ToServiceName(this byte[] data)
        {
            var sshServiceName = SshData.Ascii.GetString(data, 0, data.Length);
            switch (sshServiceName)
            {
                case "ssh-userauth":
                    return ServiceName.UserAuthentication;
                case "ssh-connection":
                    return ServiceName.Connection;
                default:
                    throw new NotSupportedException(string.Format("Service name '{0}' is not supported.", sshServiceName));
            }
        }
        internal static GlobalRequestName ToGlobalRequestName(this byte[] data)
        {
            var sshGlobalRequestName = SshData.Ascii.GetString(data, 0, data.Length);
            switch (sshGlobalRequestName)
            {
                case "tcpip-forward":
                    return GlobalRequestName.TcpIpForward;
                case "cancel-tcpip-forward":
                    return GlobalRequestName.CancelTcpIpForward;
                default:
                    throw new NotSupportedException(string.Format("Global request name '{0}' is not supported.", sshGlobalRequestName));
            }
        }
#if TUNING
        internal static BigInteger ToBigInteger(this byte[] data)
        {
            var reversed = new byte[data.Length];
            Buffer.BlockCopy(data, 0, reversed, 0, data.Length);
            return new BigInteger(reversed.Reverse());
        }
        /// 
        /// Reverses the sequence of the elements in the entire one-dimensional .
        /// 
        /// The one-dimensional  to reverse.
        /// 
        /// The  with its elements reversed.
        /// 
        internal static T[] Reverse(this T[] array)
        {
            Array.Reverse(array);
            return array;
        }
#endif
        /// 
        /// Checks whether a collection is the same as another collection
        /// 
        /// The current instance object
        /// The collection to compare with
        /// The comparer object to use to compare each item in the collection.  If null uses EqualityComparer(T).Default
        /// True if the two collections contain all the same items in the same order
        internal static bool IsEqualTo(this IEnumerable value, IEnumerable compareList, IEqualityComparer comparer)
        {
            if (value == compareList)
                return true;
            if (value == null || compareList == null)
                return false;
            if (comparer == null)
            {
                comparer = EqualityComparer.Default;
            }
            var enumerator1 = value.GetEnumerator();
            var enumerator2 = compareList.GetEnumerator();
            var enum1HasValue = enumerator1.MoveNext();
            var enum2HasValue = enumerator2.MoveNext();
            try
            {
                while (enum1HasValue && enum2HasValue)
                {
                    if (!comparer.Equals(enumerator1.Current, enumerator2.Current))
                    {
                        return false;
                    }
                    enum1HasValue = enumerator1.MoveNext();
                    enum2HasValue = enumerator2.MoveNext();
                }
                return !(enum1HasValue || enum2HasValue);
            }
            finally
            {
                enumerator1.Dispose();
                enumerator2.Dispose();
            }
        }
        /// 
        /// Checks whether a collection is the same as another collection
        /// 
        /// The current instance object
        /// The collection to compare with
        /// True if the two collections contain all the same items in the same order
        internal static bool IsEqualTo(this IEnumerable value, IEnumerable compareList)
        {
            return IsEqualTo(value, compareList, null);
        }
#if SILVERLIGHT
#else
        /// 
        /// Prints out 
        /// 
        /// The bytes.
        internal static void DebugPrint(this IEnumerable bytes)
        {
            foreach (var b in bytes)
            {
                Debug.Write(string.Format(CultureInfo.CurrentCulture, "0x{0:x2}, ", b));
            }
            Debug.WriteLine(string.Empty);
        }
#endif
        /// 
        /// Trims the leading zero from bytes array.
        /// 
        /// The data.
        /// Data without leading zeros.
        internal static IEnumerable TrimLeadingZero(this IEnumerable data)
        {
            var leadingZero = true;
            foreach (var item in data)
            {
                if (item == 0 & leadingZero)
                {
                    continue;
                }
                leadingZero = false;
                yield return item;
            }
        }
        /// 
        /// Creates an instance of the specified type using that type's default constructor.
        /// 
        /// The type to create.
        /// Type of the instance to create.
        /// A reference to the newly created object.
        internal static T CreateInstance(this Type type) where T : class
        {
            if (type == null)
                return null;
            return Activator.CreateInstance(type) as T;
        }
        /// 
        /// Returns the specified 16-bit unsigned integer value as an array of bytes.
        /// 
        /// The number to convert.
        /// An array of bytes with length 2.
        internal static byte[] GetBytes(this UInt16 value)
        {
            return new[] {(byte) (value >> 8), (byte) (value & 0xFF)};
        }
        /// 
        /// Returns the specified 32-bit unsigned integer value as an array of bytes.
        /// 
        /// The number to convert.
        /// An array of bytes with length 4.
        internal static byte[] GetBytes(this UInt32 value)
        {
#if TUNING
            var buffer = new byte[4];
            value.Write(buffer, 0);
            return buffer;
#else
            return new[] {(byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) (value & 0xFF)};
#endif
        }
        /// 
        /// Returns the specified 32-bit unsigned integer value as an array of bytes.
        /// 
        /// The number to convert.
        /// The array of bytes to write  to.
        /// The zero-based offset in  at which to begin writing.
        internal static void Write(this uint value, byte[] buffer, int offset)
        {
            buffer[offset++] = (byte) (value >> 24);
            buffer[offset++] = (byte) (value >> 16);
            buffer[offset++] = (byte)(value >> 8);
            buffer[offset] = (byte) (value & 0xFF);
        }
        /// 
        /// Returns the specified 64-bit unsigned integer value as an array of bytes.
        /// 
        /// The number to convert.
        /// An array of bytes with length 8.
        internal static byte[] GetBytes(this UInt64 value)
        {
            return new[]
                {
                    (byte) (value >> 56), (byte) (value >> 48), (byte) (value >> 40), (byte) (value >> 32),
                    (byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) (value & 0xFF)
                };
        }
        /// 
        /// Returns the specified 64-bit signed integer value as an array of bytes.
        /// 
        /// The number to convert.
        /// An array of bytes with length 8.
        internal static byte[] GetBytes(this Int64 value)
        {
            return new[]
                {
                    (byte) (value >> 56), (byte) (value >> 48), (byte) (value >> 40), (byte) (value >> 32),
                    (byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) (value & 0xFF)
                };
        }
        internal static void ValidatePort(this uint value, string argument)
        {
            if (value > IPEndPoint.MaxPort)
                throw new ArgumentOutOfRangeException(argument,
                    string.Format(CultureInfo.InvariantCulture, "Specified value cannot be greater than {0}.",
                        IPEndPoint.MaxPort));
        }
        internal static void ValidatePort(this int value, string argument)
        {
            if (value < IPEndPoint.MinPort)
                throw new ArgumentOutOfRangeException(argument,
                    string.Format(CultureInfo.InvariantCulture, "Specified value cannot be less than {0}.",
                        IPEndPoint.MinPort));
            if (value > IPEndPoint.MaxPort)
                throw new ArgumentOutOfRangeException(argument,
                    string.Format(CultureInfo.InvariantCulture, "Specified value cannot be greater than {0}.",
                        IPEndPoint.MaxPort));
        }
        /// 
        /// Returns a specified number of contiguous bytes from a given offset.
        /// 
        /// The array to return a number of bytes from.
        /// The zero-based offset in  at which to begin taking bytes.
        /// The number of bytes to take from .
        /// 
        /// A  array that contains the specified number of bytes at the specified offset
        /// of the input array.
        /// 
        internal static byte[] Take(this byte[] data, int offset, int length)
        {
            var taken = new byte[length];
            Buffer.BlockCopy(data, offset, taken, 0, length);
            return taken;
        }
    }
}