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

using System.Collections;
using System.Linq.Expressions;

namespace LINQ_to_Random
{
    class Query<T> : IEnumerable, IEnumerable<T>
    {
        IQueryProvider źródło = null;
        Expression wyrażenie = null;

        public Query(IQueryProvider źródło)
        {
            this.źródło = źródło;
            wyrażenie = Expression.Constant(this);
        }

        #region IEnumerable,IEnumerable<T>
        public IEnumerator<T> GetEnumerator()
        {                        
            //return ((IEnumerable<T>)Execute()).GetEnumerator();
            return ((IEnumerable<T>)źródło.Execute(wyrażenie)).GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            //return Execute().GetEnumerator();
            return ((IEnumerable)źródło.Execute(wyrażenie)).GetEnumerator();
        }
        #endregion
    }

    class GeneratorLiczbLosowych<T> : IQueryProvider
    {
        int size = 0;

        public GeneratorLiczbLosowych(int size)
        {
            if (size < 1) throw new Exception("Rozmiar musi być liczbą większą od zera");
            else this.size = size;
        }

        public IEnumerable<T> Execute()
        {
            if (typeof(T) == typeof(double)) return (IEnumerable<T>)ExecuteDouble();
            if (typeof(T) == typeof(int)) return (IEnumerable<T>)ExecuteInt();

            throw new Exception("Parametry typu innego niż double nie są obsługiwane");
        }

        private List<double> ExecuteDouble()
        {
            Random random = new Random();
            List<double> lista = new List<double>(size);
            for (int i = 0; i < size; i++)
            {
                double liczba = random.NextDouble();
                lista.Add(liczba);
            }
            return lista;
        }

        private List<int> ExecuteInt()
        {
            Random random = new Random();
            List<int> lista = new List<int>(size);
            for (int i = 0; i < size; i++)
            {
                int liczba = random.Next();
                lista.Add(liczba);
            }
            return lista;
        }

        #region IQueryProvider
        IQueryable IQueryProvider.CreateQuery(Expression expression)
        {
            return new Query<T>(this).AsQueryable();
        }

        IQueryable<S> IQueryProvider.CreateQuery<S>(Expression expression)
        {
            return (IQueryable<S>)(new Query<T>(this).AsQueryable());
        }

        S IQueryProvider.Execute<S>(Expression expression)
        {
            //return (S)this.Execute(expression);
            return (S)this.Execute();
        }

        object IQueryProvider.Execute(Expression expression)
        {
            //return this.Execute(expression);
            return this.Execute();
        }
        #endregion
    }
}
