using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace VisiMath
{
    /// <summary>
    /// Encapsulated standard .NET random generator
    /// </summary>
    public class RandomGenerator
    {                
        /// <summary>
        /// Internal standard NET random generator
        /// </summary>
        protected Random _rnd;

        /// <summary>
        /// Creates random generator with seed based on current time
        /// </summary>
        public RandomGenerator()
        {
            _rnd = new Random();
        }

        /// <summary>
        /// Creates randomg generator with specified seed
        /// </summary>
        /// <param name="seed">RGEN seed</param>
        public RandomGenerator(int seed)
        {
            _rnd = new Random(seed);
        }

        /// <summary>
        /// Returns float number 0.0 to 1.0
        /// </summary>
        /// <returns>random number</returns>
        public float GetFloat()
        {
            return (float)_rnd.NextDouble();
        }

        /// <summary>
        /// Returns scaled float number 0 to scale
        /// </summary>
        /// <param name="scale">scale</param>
        /// <returns>random number</returns>
        public float GetFloatScaled(float scale)
        {
            return GetFloat() * scale;
        }

        /// <summary>
        /// Returns float number from minimum to maximum
        /// </summary>
        /// <param name="min">Minimal possible value</param>
        /// <param name="max">Maximal possible value</param>
        /// <returns>random number</returns>
        public float GetFloat(float min, float max)
        {
            return min + GetFloatScaled(max - min);
        }

        /// <summary>
        /// Returns int random number from zero to (max-1)
        /// </summary>
        /// <param name="max">maximal number</param>
        /// <returns>random number</returns>
        public int GetInt(int max)
        {
            return _rnd.Next(max);
        }

        /// <summary>
        /// Returns random positive int number
        /// </summary>
        /// <returns>random number</returns>
        public int GetInt()
        {
            return _rnd.Next();
        }

        /// <summary>
        /// Returns random positive from minimum to maximum -1
        /// </summary>
        /// <returns>random number</returns>
        public int GetInt(int minimum, int maximum)
        {
            return _rnd.Next(minimum, maximum);
        }

        /// <summary>
        /// Returns random byte
        /// </summary>
        /// <returns>random number</returns>
        public byte GetByte()
        {
            return (byte)_rnd.Next(256);
        }

        /// <summary>
        /// Returns binomial float number
        /// </summary>
        /// <param name="scale"></param>
        /// <returns>random number</returns>
        public float GetBinomial(float scale = 1)
        {
            return (GetFloat() - GetFloat()) * scale;
        }

        /// <summary>
        /// Returns random Vector
        /// </summary>
        /// <param name="scale">Required scale</param>
        /// <param name="normalized">if true, vector will be normalized</param>
        /// <returns>Random vector</returns>
        public Vector2 GetVector2(float scale = 1, bool normalized = false)
        {
            Vector2 rval = new Vector2(GetBinomial(scale), GetBinomial(scale));
            if (normalized)
                rval.Normalize();
            return rval;
        }

        /// <summary>
        /// Returns random Vector
        /// </summary>
        /// <param name="scale">Required scale</param>
        /// <param name="normalized">if true, vector will be normalized</param>
        /// <returns>Random vector</returns>
        public Vector3 GetVector3(float scale = 1, bool normalized = false)
        {
            Vector3 rval = new Vector3(GetBinomial(scale), GetBinomial(scale), GetBinomial(scale));
            if (normalized)
                rval.Normalize();
            return rval;
        }

        /// <summary>
        /// Returns random Vector
        /// </summary>
        /// <param name="scale">Required scale</param>
        /// <param name="normalized">if true, vector will be normalized</param>
        /// <returns>Random vector</returns>
        public Vector4 GetVector4(float scale = 1, bool normalized = false)
        {
            Vector4 rval = new Vector4(GetBinomial(scale), GetBinomial(scale), GetBinomial(scale), GetBinomial(scale));
            if (normalized)
                rval.Normalize();
            return rval;
        }

        /// <summary>
        /// Returns random Vector
        /// </summary>
        /// <param name="scale">Required scale</param>
        /// <returns>Random vector</returns>
        public Vector2 GetVector2(Vector2 scale)
        {
            return GetVector2(ref scale);
        }

        /// <summary>
        /// Returns random Vector
        /// </summary>
        /// <param name="scale">Required scale</param>
        /// <returns>Random vector</returns>
        public Vector3 GetVector3(Vector3 scale)
        {
            return GetVector3(ref scale);
        }

        /// <summary>
        /// Returns random Vector
        /// </summary>
        /// <param name="scale">Required scale</param>
        /// <returns>Random vector</returns>
        public Vector4 GetVector4(Vector4 scale)
        {
            return GetVector4(ref scale);
        }

        /// <summary>
        /// Returns random Vector
        /// </summary>
        /// <param name="scale">Required scale</param>
        /// <returns>Random vector</returns>
        public Vector2 GetVector2(ref Vector2 scale)
        {
            return new Vector2(GetBinomial(scale.X), GetBinomial(scale.Y));
        }

        /// <summary>
        /// Returns random Vector
        /// </summary>
        /// <param name="scale">Required scale</param>
        /// <returns>Random vector</returns>
        public Vector3 GetVector3(ref Vector3 scale)
        {
            return new Vector3(GetBinomial(scale.X), GetBinomial(scale.Y), GetBinomial(scale.Z));
        }

        /// <summary>
        /// Returns random Vector
        /// </summary>
        /// <param name="scale">Required scale</param>
        /// <returns>Random vector</returns>
        public Vector4 GetVector4(ref Vector4 scale)
        {
            return new Vector4(GetBinomial(scale.X), GetBinomial(scale.Y), GetBinomial(scale.Z), GetBinomial(scale.W));
        }

        /// <summary>
        /// Returns random Vector with values from min to max
        /// </summary>
        /// <param name="max">maximal possible values for axes</param>
        /// <param name="min">minimal possible values for axes</param>
        /// <returns>Random vector</returns>
        public Vector2 GetVector2(Vector2 min, Vector2 max)
        {
            return GetVector2(ref min, ref max);
        }

        /// <summary>
        /// Returns random Vector with values from min to max
        /// </summary>
        /// <param name="max">maximal possible values for axes</param>
        /// <param name="min">minimal possible values for axes</param>
        /// <returns>Random vector</returns>
        public Vector3 GetVector3(Vector3 min, Vector3 max)
        {
            return GetVector3(ref min, ref max);
        }

        /// <summary>
        /// Returns random Vector with values from min to max
        /// </summary>
        /// <param name="max">maximal possible values for axes</param>
        /// <param name="min">minimal possible values for axes</param>
        /// <returns>Random vector</returns>
        public Vector4 GetVector4(Vector4 min, Vector4 max)
        {
            return GetVector4(ref min, ref max);
        }

        /// <summary>
        /// Returns random Vector with values from min to max
        /// </summary>
        /// <param name="max">maximal possible values for axes</param>
        /// <param name="min">minimal possible values for axes</param>
        /// <returns>Random vector</returns>
        public Vector2 GetVector2(ref Vector2 min, ref Vector2 max)
        {
            return new Vector2(GetFloat(min.X, max.X), GetFloat(min.Y, max.Y));
        }

        /// <summary>
        /// Returns random Vector with values from min to max
        /// </summary>
        /// <param name="max">maximal possible values for axes</param>
        /// <param name="min">minimal possible values for axes</param>
        /// <returns>Random vector</returns>
        public Vector3 GetVector3(ref Vector3 min, ref Vector3 max)
        {
            return new Vector3(GetFloat(min.X, max.X), GetFloat(min.Y, max.Y), GetFloat(min.Z, max.Z));
        }

        /// <summary>
        /// Returns random Vector with values from min to max
        /// </summary>
        /// <param name="max">maximal possible values for axes</param>
        /// <param name="min">minimal possible values for axes</param>
        /// <returns>Random vector</returns>
        public Vector4 GetVector4(ref Vector4 min, ref Vector4 max)
        {
            return new Vector4(GetFloat(min.X, max.X), GetFloat(min.Y, max.Y), GetFloat(min.Z, max.Z), GetFloat(min.W, max.W));
        }

        /// <summary>
        /// Returns random quaternion rotation
        /// </summary>
        /// <returns>Random rotation</returns>
        public Quaternion GetQuaternion()
        {
            Quaternion q = new Quaternion(GetFloat(), GetFloat(), GetFloat(), GetFloat());
            q.Normalize();
            return q;
        }
    }
}
