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

namespace USA
{
    public struct RingInterval
    {
        public readonly int start, stop, max, length;
        public readonly double threshold;

        /// <summary>
        /// Create increasing interval
        /// </summary>
        /// <param name="start"></param>
        /// <param name="stop"></param>
        public RingInterval(int start, int stop)
        {
            if (start >= stop)
                throw new ArgumentException("RingInterval.ctor(): start >= stop");
            if (start < 0 || stop < 0)
                throw new ArgumentException("RingInterval.ctor(): start or stop < 0");
            this.start = start;
            this.stop = stop;
            this.length = stop - start;
            this.max = -1;
            this.threshold = 0;
        }

        /// <summary>
        /// Create any circular interval
        /// </summary>
        /// <param name="start"></param>
        /// <param name="stop"></param>
        /// <param name="max"></param>
        public RingInterval(int start, int stop, int max)
        {
            if (start < 0 || stop < 0)
                throw new ArgumentException("RingInterval.ctor(): start or stop < 0");
            if (start > max || stop > max)
                throw new ArgumentException("RingInterval.ctor(): start or stop > max");
            if (start == stop)
                throw new ArgumentException("RingInterval.ctor(): start == stop");
            this.start = start;
            this.stop = stop;
            if (start < stop)
            {
                this.max = -1;
                this.threshold = 0;
                this.length = stop - start;
            }
            else
            {
                this.max = max;
                this.threshold = (max - start) / (double)(max - start + stop);
                this.length = max - start + stop;
            }
        }
        
        /// <summary>
        /// Check to see if inside
        /// <remarks>no check for max</remarks>
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public bool Contains(int index)
        {
            if (this.max != -1)
                return (index >= start || index < stop);
            else
                return (index >= start && index < stop);
        }

        /// <summary>
        /// Generate random integer in interval
        /// </summary>
        /// <param name="random"></param>
        /// <returns></returns>
        public int RandomIndex(Random random)
        {
            if (this.max == -1)
            {
                return random.Next(this.start, this.stop);
            }
         
            if (random.NextDouble() < this.threshold)
            {
                return random.Next(this.start, this.max);
            }
            else
            {
                return random.Next(0, this.stop);
            }
        }

        /// <summary>
        /// Generate a given number of random integers and return their sorted list
        /// </summary>
        /// <param name="random"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public int[] RandomIndexSortedArray(Random random, int length)
        {
            int[] list = new int[length];
            for (int i = 0; i < length; i++)
            {
                int n = this.RandomIndex(random);
                while (Array.IndexOf<int>(list,n) >= 0)
                    n = this.RandomIndex(random);
                list[i] = n;
            }
            Array.Sort<int>(list);
            return list;
        }

        public static int ComparisonLengthDescending(RingInterval r, RingInterval q)
        {
            return q.length.CompareTo(r.length);
        }

        /// <summary>
        /// String representation
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            if (this.max == -1)
                return String.Format("{0} -> {1}", this.start, this.stop);
            else
                return String.Format("{0} -> ({1}) -> {2}", this.start, this.max, this.stop);
        }
    }
}
