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

public static class RandomUtil
{
    static System.Random random = new System.Random();

    public static float RandomAngle => Range(0f, 360f);

    // Note, max is exclusive here!
    public static List<int> GenerateNonRepeatingRandoms(int count, int min, int max)
    {

        //  initialize set S to empty
        //  for J := N-M + 1 to N do
        //    T := RandInt(1, J)
        //    if T is not in S then
        //      insert T in S
        //    else
        //      insert J in S
        //
        // adapted for C# which does not have an inclusive Next(..)
        // and to make it from configurable range not just 1.

        if (max <= min || count < 0 ||
                // max - min > 0 required to avoid overflow
                (count > max - min && max - min > 0))
        {
            // need to use 64-bit to support big ranges (negative min, positive max)
            throw new ArgumentOutOfRangeException("Range " + min + " to " + max +
                    " (" + ((Int64)max - (Int64)min) + " values), or count " + count + " is illegal");
        }

        // generate count random values.
        HashSet<int> candidates = new HashSet<int>();

        // start count values before max, and end at max
        for (int top = max - count; top < max; top++)
        {
            // May strike a duplicate.
            // Need to add +1 to make inclusive generator
            // +1 is safe even for MaxVal max value because top < max
            if (!candidates.Add(random.Next(min, top + 1)))
            {
                // collision, add inclusive max.
                // which could not possibly have been added before.
                candidates.Add(top);
            }
        }

        // load them in to a list, to sort
        List<int> result = candidates.ToList();

        // shuffle the results because HashSet has messed
        // with the order, and the algorithm does not produce
        // random-ordered results (e.g. max-1 will never be the first value)
        for (int i = result.Count - 1; i > 0; i--)
        {
            int k = random.Next(i + 1);
            int tmp = result[k];
            result[k] = result[i];
            result[i] = tmp;
        }
        return result;
    }

    public static List<int> GenerateNonRepeatingRandoms(int count)
    {
        return GenerateNonRepeatingRandoms(count, 0, Int32.MaxValue);
    }

    public static bool RollChance(float chance)
    {
        return UnityEngine.Random.value <= chance;
    }
    public static int RollChance01(float chance)
    {
        return UnityEngine.Random.value <= chance ? 1 : 0;
    }

    /// <summary>
    /// Каждое число делится на сумму всех чисел, образуя шанс.
    /// Возвращает номер промежутка, в котором выпал рандом
    /// (начиная с 0)
    /// </summary>
    public static int RollChances(Func<int, bool> chanceZeroingCondition, params float[] chances)
    {
        var convertedChances = new List<float>();
        float curSum = 0;
        for (int i = 0; i < chances.Length; i++)
        {
            var chance = chances[i];
            if (chanceZeroingCondition != null)
                if (chanceZeroingCondition(i))
                    chance = 0;
            curSum += chance;
            convertedChances.Add(curSum);
        }

        float r = UnityEngine.Random.Range(0, curSum);

        for (int i = 0; i < convertedChances.Count; i++)
        {
            if (i == 0)
            {
                if (r <= convertedChances[i])
                    return i;
            }
            else if (r < convertedChances[i] && r >= convertedChances[i - 1])
                return i;
        }
        return -1;
    }

    public static int RollChances(params float[] chances)
    {
        return RollChances(null, chances);
    }

    private static System.Random _rng = new System.Random();
    public static void Shuffle<T>(this IList<T> list)
    {
        int n = list.Count;
        while (n > 1)
        {
            n--;
            int k = _rng.Next(n + 1);
            T value = list[k];
            list[k] = list[n];
            list[n] = value;
        }
    }

    public static float Range(float min, float max)
    {
        return UnityEngine.Random.Range(min, max);
    }
    public static int Range(int min, int max)
    {
        return UnityEngine.Random.Range(min, max);
    }

    public static Vector2 Vector(float min, float max)
    {
        return new Vector2(UnityEngine.Random.Range(min, max),
                           UnityEngine.Random.Range(min, max));
    }
}