using System;
using System.Linq;

namespace Volpe.Cafe.Generic
{
    static class GenericsHelper
    {
        /// <summary>
        /// Initializes all elements of the given array of items, using the specified initializeT method for
        /// instantiating each element of type T to a specific initial value.
        /// </summary>
        /// <param name="items">
        ///     The array of items whose members to initialize. The array itself must already be initialized to the
        ///     desired length.
        /// </param>
        /// <param name="initializeT">
        ///     Encapsulates a method that will handle instantiation for each element of type T.
        /// </param>
        internal static void Initialize<T>(T[] items, Func<T> initializeT)
        {
            for (int i = 0; i < items.Length; i++)
            {
                items[i] = initializeT();
            }
        }

        /// <summary>
        /// Computes and returns the string representation for the given array of items, using the specified array of
        /// accompanying item names.
        /// </summary>
        /// <returns>The string representation of the given array of items.</returns>
        internal static string CalcString<T>(T[] items, string[] itemNames)
        {
            string s = string.Empty;
            for (int i = 0; i < items.Length; i++)
            {
                if (i > 0) { s += ", "; }
                s += $"{itemNames[i]}={items[i].ToString()}";
            }
            return "{" + s + "}";
        }
        /// <summary>
        /// Computes and returns the string representation for the given array of items, using the specified array of
        /// accompanying item names. A "toString" method may be used to apply custom formatting to each element in the
        /// items array.
        /// </summary>
        /// <returns>The string representation of the given array of items.</returns>
        internal static string CalcString<T>(T[] items, string[] itemNames, Func<T, string> toString)
        {
            if (toString == null)
            {
                return CalcString(items, itemNames);
            }

            string s = string.Empty;
            for (int i = 0; i < items.Length; i++)
            {
                if (i > 0) { s += ", "; }
                s += $"{itemNames[i]}={toString(items[i])}";
            }
            return "{" + s + "}";
        }

        /// <summary>
        /// Computes and returns the hash code for the the given array of items, suitable for use in hashing algorithms
        /// and data structures like a hash table.
        /// </summary>
        /// <returns>A hash code for the the given array of items.</returns>
        internal static int CalcHashCode<T>(T[] items)
        {
            int hash = 0;
            for (int i = 0; i < items.Length; i++)
            {
                hash = hash ^ items[i].GetHashCode();
            }
            return hash;
        }

        /// <summary>
        /// Determines whether the specified two arrays of items are equal.
        /// </summary>
        /// <param name="items1">The first arrays of items to compare.</param>
        /// <param name="items2">The second arrays of items to compare.</param>
        /// <returns>true, if the specified two arrays of items are equal; false, otherwise.</returns>
        internal static bool Equals<T>(T[] items1, T[] items2)
        {
            return (items1 is null && items2 is null) ? true
                 : (items1 is null || items2 is null) ? false
                 : items1.SequenceEqual(items2);
        }

        /// <summary>
        /// Copies all elements from sourceItems to destinationItems, optionally using the specified cloneT method for
        /// cloning each element of type T from one array to the other.
        /// </summary>
        /// <param name="sourceItems">The source of the clone operation.</param>
        /// <param name="destinationItems">The destination of the clone operation.</param>
        /// <param name="cloneT">Encapsulates a method that will handle cloning for each element of type T.</param>
        internal static void Clone<T>(T[] sourceItems, T[] destinationItems, Func<T, T> cloneT = null)
        {
            if (cloneT == null)
            {
                Array.Copy(sourceItems, destinationItems, sourceItems.Length);
            }
            else
            {
                for (int i = 0; i < sourceItems.Length; i++)
                {
                    destinationItems[i] = cloneT(sourceItems[i]);
                }
            }
        }

        /// <summary>
        /// Resets all elements of the given array of items to their default values (0, false, or null), or optionally
        /// using the specified clearT method to reset each element of type T to a specific initial value.
        /// </summary>
        /// <param name="items">The array of items to reset.</param>
        /// <param name="clearT">Encapsulates a method that will handle clearing for each element of type T.</param>
        internal static void Clear<T>(T[] items, Action<T> clearT = null)
        {
            if (clearT == null)
            {
                Array.Clear(items, 0, items.Length);
            }
            else
            {
                for (int i = 0; i < items.Length; i++)
                {
                    clearT(items[i]);
                }
            }
        }

    }
}
