using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PrimeSieves
{
    public sealed class TurnerSieve
    {
        private ICollection<int> _knownPrimes = new List<int>();

        public Prime NextPrime()
        {
            if (_knownPrimes.Count == 0)
            {
                _knownPrimes.Add(2);
            }
            else
            {
                // Try out the different prime calculation methods by 
                // commenting and uncommenting the lines below!

                int nextPrime = BruteForceNextPrime();
                //int nextPrime = SavedListNextPrime();
                //int nextPrime = ParallelNextPrime();

                _knownPrimes.Add(nextPrime);
            }

            return new Prime(_knownPrimes.Last());
        }

        public void Reset()
        {
            _knownPrimes.Clear();
        }

        private int BruteForceNextPrime()
        {
            // Compute prime by dividing the each prime candidate by numbers down to 2
            int primeCandidate = _knownPrimes.Last();
            bool divisorFound = true;
            while (divisorFound)
            {
                // search for divisors for the next prime candidate
                primeCandidate++;
                divisorFound = false;
                for (int divisor = 2; divisor < primeCandidate; divisor++)
                {
                    if (primeCandidate % divisor == 0)
                    {
                        divisorFound = true;
                        break;
                    }
                }
            }
            return primeCandidate;        
        }

        private int SavedListNextPrime()
        {
            // Compute prime by dividing each prime candidate by only previously found primes
            int primeCandidate = _knownPrimes.Last();
            bool divisorFound = true;
            while (divisorFound)
            {
                // search for divisors for the next prime candidate
                primeCandidate++;
                divisorFound = false;
                foreach (int divisor in _knownPrimes)
                {
                    if (primeCandidate % divisor == 0)
                    {
                        divisorFound = true;
                        break;
                    }
                }
            }
            // no divisors were found for the last prime candidate
            return primeCandidate;
        }

        private int ParallelNextPrime()
        {
            // Similar to saved list algorithm, but parallelizes the division by previously found primes
            int primeCandidate = _knownPrimes.Last() + 1;
            ParallelLoopResult loopResult;
            do
            {
                loopResult = Parallel.ForEach(_knownPrimes, (p, loopState) =>
                {
                    if (primeCandidate % p == 0)
                    {
                        loopState.Break();
                    }
                });
                if (!loopResult.IsCompleted)
                {
                    // divisor was found, try another candidate
                    primeCandidate++;
                }
            } while (!loopResult.IsCompleted);
            return primeCandidate;
        }
    }
}
