using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Random = UnityEngine.Random;

namespace Jovian.Utilities.Utilities {
    public static class CollectionUtility {
        public static T RandomElementFromCollection<T>(this ICollection<T> enumerableObject) {
            int count = enumerableObject.Count;
            if (count == 0) {
                throw new IndexOutOfRangeException("Cannot get RandomElement, collection size is 0.");
            }

            int index = Random.Range(0, count);
            return enumerableObject.ElementAt(index);
        }
    }
}

public static class EnumerableUtility {
    private static System.Random random;

    public static T RandomElement<T>(this IEnumerable<T> source) {
        random ??= new System.Random();
        return source.RandomElement(random);
    }

    //https://stackoverflow.com/a/648240/584774
    public static T RandomElement<T>(this IEnumerable<T> source, System.Random rng) {
        T current = default(T);
        int count = 0;
        foreach (T element in source) {
            count++;
            if (rng.Next(count) == 0) {
                current = element;
            }
        }

        if (count == 0) {
            throw new InvalidOperationException("Sequence was empty");
        }

        return current;
    }

    public static bool TryGetRandomElement<T>(this IEnumerable<T> source, out T outElement) {
        random ??= new System.Random();
        return source.TryGetRandomElement(random, out outElement);
    }

    public static bool TryGetRandomElement<T>(this IEnumerable<T> source, System.Random rng, out T outElement) {
        T current = default(T);
        int count = 0;
        foreach (T element in source) {
            count++;
            if (rng.Next(count) == 0) {
                current = element;
            }
        }

        outElement = current;
        if (count == 0) {
            return false;
        }

        return true;
    }
    
    public static string EnumerableToString(this IEnumerable enumerable, bool newLinePerEntry = false) {
        if (enumerable == null) {
            return "<NULL>";
        }

        StringBuilder sb = new();
        sb.Append("{");
        if (newLinePerEntry) {
            sb.AppendLine();
        }

        foreach (object item in enumerable) {
            sb.Append(item);
            if (newLinePerEntry) {
                sb.AppendLine(",");
            }
            else {
                sb.Append(", ");
            }
        }

        if (newLinePerEntry) {
            sb.AppendLine();
        }

        sb.Append("}");
        return sb.ToString();
    }
}