//-----------------------------------------------------------------------
// 
//     Copyright (c) Microsoft Corporation.
// 
//-----------------------------------------------------------------------
namespace Microsoft.Isam.Esent.Interop
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Text;
    /// 
    /// Static utility methods.
    /// 
    internal static class Util
    {
        /// 
        /// Compare two byte arrays to see if they have the same content.
        /// 
        /// The first array.
        /// The second array.
        /// The offset to start comparing at.
        /// The number of bytes to compare.
        /// True if the arrays are equal, false otherwise.
        public static bool ArrayEqual(IList a, IList b, int offset, int count)
        {
            if (null == a || null == b)
            {
                return ReferenceEquals(a, b);
            }
            for (int i = 0; i < count; ++i)
            {
                if (a[offset + i] != b[offset + i])
                {
                    return false;
                }
            }
            return true;
        }
        /// 
        /// Return a string containing (some of) the bytes.
        /// 
        /// The data to dump.
        /// The starting offset.
        /// The count.
        /// A string version of the data.
        public static string DumpBytes(byte[] data, int offset, int count)
        {
            if (null == data)
            {
                return "";
            }
            if (0 == count)
            {
                return string.Empty;
            }
            if (offset < 0 || count < 0 || offset >= data.Length || offset + count > data.Length)
            {
                return "";
            }
            const int MaxBytesToPrint = 8;
            StringBuilder sb = new StringBuilder();
            sb.Append(BitConverter.ToString(data, offset, Math.Min(count, MaxBytesToPrint)));
            if (count > MaxBytesToPrint)
            {
                // The output was truncated
                sb.AppendFormat(CultureInfo.InvariantCulture, "... ({0} bytes)", count);
            }
            return sb.ToString();
        }
        /// 
        /// Compares two objects with ContentEquals.
        /// If both are null, there are considered equal.
        /// 
        /// A type that implements IContentEquatable.
        /// First object to compare.
        /// Second object to compare.
        /// Whether the two objects are equal.
        public static bool ObjectContentEquals(T left, T right)
            where T : class, IContentEquatable
        {
            if (null == left || null == right)
            {
                return ReferenceEquals(left, right);
            }
            return left.ContentEquals(right);
        }
        /// 
        /// Compares two objects with ContentEquals.
        /// If both are null, there are considered equal.
        /// 
        /// A type that implements IContentEquatable.
        /// First object to compare.
        /// Second object to compare.
        /// The number of entries to compare.
        /// Whether the two objects are equal.
        public static bool ArrayObjectContentEquals(T[] left, T[] right, int length)
            where T : class, IContentEquatable
        {
            if (null == left || null == right)
            {
                return ReferenceEquals(left, right);
            }
            for (int i = 0; i < length; ++i)
            {
                if (!ObjectContentEquals(left[i], right[i]))
                {
                    return false;
                }
            }
            // All the individual members are equal, all of the elements of the arrays are
            // equal, so they must be equal!
            return true;
        }
        /// 
        /// Compares items in two arrays using Equals.
        /// If both arrays are null, there are considered equal.
        /// 
        /// A value type.
        /// First array to compare.
        /// Second array to compare.
        /// The number of entries to compare.
        /// Whether the two arrays are equal.
        public static bool ArrayStructEquals(T[] left, T[] right, int length)
            where T : struct
        {
            if (null == left || null == right)
            {
                return ReferenceEquals(left, right);
            }
            for (int i = 0; i < length; ++i)
            {
                if (!left[i].Equals(right[i]))
                {
                    return false;
                }
            }
            // All the individual members are equal, all of the elements of the arrays are
            // equal, so they must be equal!
            return true;
        }
        /// 
        /// Clone an array of objects.
        /// 
        /// The type of object in the array.
        /// The values to clone.
        /// A clone of the values.
        public static T[] DeepCloneArray(T[] value) where T : class, IDeepCloneable
        {
            T[] clone = null;
            if (null != value)
            {
                clone = new T[value.Length];
                for (int i = 0; i < clone.Length; ++i)
                {
                    clone[i] = (null == value[i]) ? null : value[i].DeepClone();
                }
            }
            return clone;
        }
        /// 
        /// Given a list of hash codes calculate a hash of the hashes.
        /// 
        /// The sub hash codes.
        /// A hash of the hash codes.
        public static int CalculateHashCode(IEnumerable hashes)
        {
            int hash = 0;
            foreach (int h in hashes)
            {
                hash ^= h;
                unchecked
                {
                    hash *= 33;
                }
            }
            return hash;
        }
        /// 
        /// Add a trailing directory separator character to the string.
        /// 
        /// The directory.
        /// The directory with a separator character added (if necessary).
        public static string AddTrailingDirectorySeparator(string dir)
        {
            if (!string.IsNullOrEmpty(dir))
            {
                var sepChars = new[] { LibraryHelpers.DirectorySeparatorChar, LibraryHelpers.AltDirectorySeparatorChar };
                return string.Concat(dir.TrimEnd(sepChars), LibraryHelpers.DirectorySeparatorChar);
            }
            return dir;
        }
        /// 
        /// Converts a unicode string to a null-terminated Ascii byte array.
        /// 
        /// The unicode string to be converted.
        /// The byte array with a null-terminated Ascii representation of the given string.
        public static byte[] ConvertToNullTerminatedAsciiByteArray(string value)
        {
            if (value == null)
            {
                return null;
            }
            byte[] output = new byte[value.Length + 1];
            LibraryHelpers.EncodingASCII.GetBytes(value, 0, value.Length, output, 0);
            output[output.Length - 1] = (byte)0;
            return output;
        }
        /// 
        /// Converts a unicode string to a null-terminated Unicode byte array.
        /// 
        /// The unicode string to be converted.
        /// The byte array with a null-terminated Unicode representation of the given string.
        public static byte[] ConvertToNullTerminatedUnicodeByteArray(string value)
        {
            if (value == null)
            {
                return null;
            }
            int byteArrayLength = Encoding.Unicode.GetByteCount(value); 
            byte[] output = new byte[byteArrayLength + 2];
            Encoding.Unicode.GetBytes(value, 0, value.Length, output, 0);
            output[output.Length - 2] = (byte)0;
            output[output.Length - 1] = (byte)0;
            return output;
        }
        /// 
        /// Converts a unicode string to an Ascii byte array.
        /// CAUTION: The array doesn't have a null-terminator at the end.
        /// 
        /// The unicode string to be converted.
        /// The byte array with an Ascii representation of the given string.
        public static byte[] ConvertToAsciiByteArray(string value)
        {
            if (value == null)
            {
                return null;
            }
            byte[] output = new byte[value.Length];
            LibraryHelpers.EncodingASCII.GetBytes(value, 0, value.Length, output, 0);
            return output;
        }
    }
}