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

public class GFRandom
{
    public static void Init()
    {
        Random.InitState((int)System.DateTime.Now.Ticks);
    }
    public static float value { get { return Random.value; } }

    // closed interval [a,b]
    public static int RangeClose(int a, int b)
    {
        return Random.Range(a, b + 1);
    }

    // closed interval [a,b]
    public static float RangeClose(float a, float b)
    {
        return Random.Range(a, b);
    }

    // closed interval [0,1]
    public static float RangeClose()
    {
        return Random.value;
    }

    // open interval [a,b)
    public static int RangeOpen(int a, int b)
    {
        return Random.Range(a, b);
    }

    // open interval [a,b)
    public static float RangeOpen(float a, float b)
    {
        return a + (Random.value * 0.99999f) * (b - a);
    }
    
    // open interval [0,1)
    public static float RangeOpen()
    {
        return Random.value * 0.99999f;
    }

    // interval [0,360)
    public static float Angle()
    {
        return RangeOpen(0.0f, 360.0f);
    }

    // if a random chance [0,1) less than given threshold [0=never, 1=always]
    public static bool Chance(float chance)
    {
        return RangeOpen() < chance;
    }

    public static void Shuffle<T>(ref T[] list)
    {
        int n = list.Length;
        while (n > 1)
        {
            n--;
            int k = Random.Range(0, n + 1);
            T value = list[k];
            list[k] = list[n];
            list[n] = value;
        }
    }

    public static void Shuffle<T>(ref List<T> list)
    {
        int n = list.Count;
        while (n > 1)
        {
            n--;
            int k = Random.Range(0, n + 1);
            T value = list[k];
            list[k] = list[n];
            list[n] = value;
        }
    }

}

[System.Serializable]
public class RangeInt
{
    public int min;
    public int max;

    public RangeInt(int _min, int _max)
    {
        min = _min;
        max = _max;
    }

    public int GetLength()
    {
        return max - min;
    }

    public float GetFactor(int value)
    {
        return (float)(value - min) / (float)(max - min);
    }

    public float GetClampFactor(int value)
    {
        return Mathf.Clamp01((float)(value - min) / (float)(max - min));
    }

    public float TransferValue(int value, RangeFloat range)
    {
        float t = GetFactor(value);
        return Mathf.Lerp(range.min, range.max, t);
    }

    public float TransferClampValue(int value, RangeFloat range)
    {
        float t = GetClampFactor(value);
        return Mathf.Lerp(range.min, range.max, t);
    }

    public int RandomClose()
    {
        return Random.Range(min, max + 1);
    }

    public int RandomOpen()
    {
        return Random.Range(min, max);
    }
}

[System.Serializable]
public class RangeFloat
{
    public float min;
    public float max;

    public RangeFloat(float _min, float _max)
    {
        min = _min;
        max = _max;
    }

    public float GetLength()
    {
        return max - min;
    }

    public float GetFactor(int value)
    {
        return (value - min) / (max - min);
    }

    public float GetClampFactor(int value)
    {
        return Mathf.Clamp01((value - min) / (max - min));
    }

    public float TransferValue(int value, RangeFloat range)
    {
        float t = GetFactor(value);
        return Mathf.Lerp(range.min, range.max, t);
    }

    public float TransferClampValue(int value, RangeFloat range)
    {
        float t = GetClampFactor(value);
        return Mathf.Lerp(range.min, range.max, t);
    }

    public float RandomClose()
    {
        return Random.Range(min, max);
    }

    public float RandomOpen()
    {
        return min + (Random.value * 0.99999f) * (max - min);
    }
}

[System.Serializable]
public class RandomInt
{
    // range inclusive [min, max]
    public int min;
    public int max;

    [System.NonSerialized]
    public int value;

    public RandomInt(int _min, int _max)
    {
        min = _min;
        max = _max;
    }

    public int Value()
    {
        value = Random.Range(min, max+1);
        return value;
    }

    public void Init()
    {
        Value();
    }
}

[System.Serializable]
public class RandomFloat
{
    // range inclusive [min, max]
    public float min;
    public float max;

    [System.NonSerialized]
    public float value;

    public RandomFloat(float _min, float _max)
    {
        min = _min;
        max = _max;
    }

    public float Value()
    {
        value = Random.Range(min, max);
        return value;
    }

    public void Init()
    {
        Value();
    }
}

[System.Serializable]
public class RandomWeightValuesList
{
    public Vector2[] m_values;

    private float m_chanceSum;
    private List<int> m_sorted;
    
    public void Init()
    {
        m_chanceSum = 0;
        m_sorted = new List<int>(m_values.Length);
        for (int i=0; i<m_values.Length; i++)
        {
            m_sorted.Add(i);
            m_chanceSum += m_values[i].y;
        }
        m_sorted.Sort((a, b) => (m_values[a].y < m_values[b].y) ? -1 : (m_values[a].y > m_values[b].y ? 1 : 0)); // inc
    }

    public bool HasValues()
    {
        return m_values.Length > 0;
    }

    public float GetValue()
    {
        float r = GFRandom.RangeOpen(0.0f, m_chanceSum);
        for (int i = 0; i < m_sorted.Count; i++)
        {
            int idx = m_sorted[i];
            if (r < m_values[idx].y)
                return m_values[idx].x;
            r -= m_values[i].y;
        }
        Debug.LogError("why?");
        return m_values[0].x;
    }

    public int GetIntegerValue()
    {
        float r = GFRandom.RangeOpen(0.0f, m_chanceSum);
        for (int i = 0; i < m_sorted.Count; i++)
        {
            int idx = m_sorted[i];
            if (r < m_values[idx].y)
                return Mathf.RoundToInt(m_values[idx].x);
            r -= m_values[i].y;
        }
        Debug.LogError("why?");
        return Mathf.RoundToInt(m_values[0].x);
    }

    public float GetChanceOfValue(float v)
    {
        for (int i = 0; i < m_values.Length; i++)
        {
            if (m_values[i].x == v)
                return m_values[i].y;
        }
        return 0;
    }

    public float GetChanceOfValue(int v)
    {
        for (int i = 0; i < m_values.Length; i++)
        {
            if (Mathf.RoundToInt(m_values[i].x) == v)
                return m_values[i].y;
        }
        return 0;
    }
}

[System.Serializable]
public class RandomRangedChances
{
    public Vector2 m_range;
    public Vector2 m_chances;

    public float GetChance(int value)
    {
        float t = GFMath.GetClampedFactorOfValueInRange((float)value, m_range.x, m_range.y);
        return Mathf.Lerp(m_chances.x, m_chances.y, t);
    }

    public bool Chance(int value)
    {
        float t = GFMath.GetClampedFactorOfValueInRange((float)value, m_range.x, m_range.y);
        float c = Mathf.Lerp(m_chances.x, m_chances.y, t);
        return GFRandom.Chance(c);
    }

    public bool Chance(float value)
    {
        float t = GFMath.GetClampedFactorOfValueInRange(value, m_range.x, m_range.y);
        float c = Mathf.Lerp(m_chances.x, m_chances.y, t);
        return GFRandom.Chance(c);
    }
}