using UnityEngine;
using System.Collections;
namespace Tenkoku.Core
{
    /// 
    /// Simple and fast random number generator which can reset to a specific iteration
    /// 
    public class Random
    {
        //Initialiser magic values
        private const ulong m_A_Init = 181353;
        private const ulong m_B_Init = 7;
        //Seed
        public int m_seed;
        //State
        public ulong m_stateA, m_stateB;
        /// 
        /// Contruct and initialise the RNG
        /// 
        /// 
        public Random(int seed = 1)
        {
            m_seed = seed;
            if (m_seed == 0)
            {
                m_seed = 1;
            }
            Reset();
        }
        /// 
        /// Reset it to its initial state with the existing seed
        /// 
        public void Reset()
        {
            m_stateA = m_A_Init * (uint)m_seed;
            m_stateB = m_B_Init * (uint)m_seed;
        }
        /// 
        /// Restet it to a new state with a new seed
        /// 
        /// New seed
        public void Reset(int seed)
        {
            m_seed = seed;
            if (m_seed == 0)
            {
                m_seed = 1;
            }
            Reset();
        }
        /// 
        /// Reset it to the stade defined by the state variables passed in
        /// 
        public void Reset(ulong stateA, ulong stateB)
        {
            Debug.Log("Resetting RNG State " + stateA + " " + stateB);
            m_stateA = stateA;
            m_stateB = stateB;
        }
        /// 
        /// Return the current state for serialisation
        /// 
        /// Seed
        /// State A
        /// State B
        public void GetState(out int seed, out ulong stateA, out ulong stateB)
        {
            seed = m_seed;
            stateA = m_stateA;
            stateB = m_stateB;
        }
        //Check here for wrapper functions
        //https://github.com/tucano/UnityRandom/blob/master/lib/MersenneTwister.cs
        /// 
        /// Get the next value
        /// 
        /// A value between zero and one inclusive
        public float Next()
        {
            ulong x = m_stateA;
            ulong y = m_stateB;
            m_stateA = y;
            x ^= x << 23;
            x ^= x >> 17;
            x ^= y ^ (y >> 26);
            m_stateB = x;
            return (float)(x + y) / (float)ulong.MaxValue;
        }
        /// 
        /// Return the next int
        /// 
        /// 
        public int NextInt()
        {
            return (int)(Next() * int.MaxValue);
        }
        /// 
        /// Get the next value and scale it between the min and max values supplied inclusive
        /// 
        /// Minimum value
        /// Maximum value
        /// Next value scaled beteen the range supplied
        public float Next(float min, float max)
        {
            //float xx = min + (Next() * (max - min));
            //Debug.Log(string.Format("{0:0.0000}", xx));
            //return xx;
            return min + (Next() * (max - min));
        }
        /// 
        /// Get the next value and scale it between the min and max values supplied inclusive
        /// 
        /// Minimum value
        /// Maximum value
        /// Next value scaled beteen the range supplied
        public int Next(int min, int max)
        {
            if (min == max)
            {
                return min;
            }
            return (int)Next((float)min, (float)max+0.999f);
        }
        /// 
        /// Get the next value as a vector
        /// 
        /// Next value as a vector in ranges 0..1
        public Vector3 NextVector()
        {
            return new Vector3(Next(), Next(), Next());
        }
        /// 
        /// Get the next value as a vector
        /// 
        /// Minimum value
        /// Maximum value
        /// 
        public Vector3 NextVector(float min, float max)
        {
            return new Vector3(Next(min, max), Next(min, max), Next(min, max));
        }
    }
}