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

namespace VOID.Generic.Util
{
    public static class RandomUtil
    {
        /// <summary>
        /// Reinit random with given seed.
        /// </summary>
        public static void InitState(int seed)
        {
            Random.InitState(seed);
        }
        
        /// <summary>
        /// Returns random integer between min and max.
        /// </summary>
        public static int RandomInteger(int min = 0, int max = 100)
        {
            return Mathf.RoundToInt(Random.value * (max-min)) + min;
        }
        
        /// <summary>
        /// Returns random float between min and max.
        /// </summary>
        public static float RandomFloat(float min = 0, float max = 1)
        {
            return Random.value * (max-min) + min;
        }

        /// <summary>
        /// Returns random element from given collection.
        /// </summary>
        public static T RandomElement<T>(IEnumerable<T> enumerable)
        {
            return RandomElements(enumerable, 1)[0];
        }

        /// <summary>
        /// Returns random element from given collection.
        /// </summary>
        public static List<T> RandomElements<T>(IEnumerable<T> enumerable, int count, bool repeatable = false)
        {
            var list = enumerable.ToList();
            
            // Not repeatable - we can take each value only once - sort by random and get needed amount
            if (!repeatable) return list.OrderBy(_ => RandomFloat()).Take(count).ToList();
            
            // Repeatable - randomize each time and get value from list
            return new T[count].Select(_ => list[RandomInteger(0, list.Count - 1)]).ToList();
        }

        /// <summary>
        /// Returns random element from given collection by function that determine weight.
        /// </summary>
        public static T RandomElementByWeight<T>(IEnumerable<T> enumerable, Func<T, float> getWeight)
        {
            var list = enumerable.ToList();
            var maxWeight = list.Sum(getWeight.Invoke);
            var randomWeight = RandomFloat(0, maxWeight);
            return list.FirstOrDefault(element => (randomWeight -= getWeight.Invoke(element)) <= 0f);
        }
    }
}