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

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

namespace LinqToTxt
{
    public class Query<T> : IOrderedQueryable<T>
    {
        protected IQueryProvider źródło = null;
        protected Expression wyrażenie = null;

        public Query(IQueryProvider źródło)
        {
            if (źródło == null) throw new ArgumentNullException("źródło");
            this.źródło = źródło;
            wyrażenie = Expression.Constant(this);
        }

        public Query(IQueryProvider źródło, Expression wyrażenie)
        {
            if (źródło == null) throw new ArgumentNullException("źródło");
            this.źródło = źródło;
            if (wyrażenie == null) throw new ArgumentNullException("wyrażenie");
            if (!typeof(IQueryable<T>).IsAssignableFrom(wyrażenie.Type)) 
                throw new ArgumentOutOfRangeException("wyrażenie");
            this.wyrażenie = wyrażenie;
        }

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

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

        Expression IQueryable.Expression
        {
            get { return this.wyrażenie; }
        }

        Type IQueryable.ElementType
        {
            get { return typeof(T); }
        }

        IQueryProvider IQueryable.Provider
        {
            get { return this.źródło; }
        }
    }

    class LinqToTxtQuery<T> : Query<T>
    {
        public LinqToTxtQuery(IQueryProvider źródłoWejściowe)
            :base(źródłoWejściowe)
        {            
        }

        public LinqToTxtQuery(IQueryProvider źródłoWejściowe, Expression wyrażenie)
            :base(źródłoWejściowe,wyrażenie)
        {
        }

        public void Insert(T dane)
        {
            ((ZrodloLinqToTxt<T>)źródło).Insert(dane);
        }

        public int Delete(IOrderedQueryable<T> dane, bool odrazu)
        {
            if (!((ZrodloLinqToTxt<T>)źródło).NumeryWybranychWierszy.ContainsKey(dane.Expression))
                dane.GetEnumerator();
            List<int> numeryWierszyDoUsunięcia = ((ZrodloLinqToTxt<T>)źródło).NumeryWybranychWierszy[dane.Expression];
            return ((ZrodloLinqToTxt<T>)źródło).Delete(numeryWierszyDoUsunięcia, odrazu);
        }
        
        public int Delete(IQueryable<T> dane, bool odrazu)
        {
            if (!((ZrodloLinqToTxt<T>)źródło).NumeryWybranychWierszy.ContainsKey(dane.Expression))
                dane.GetEnumerator();
            List<int> numeryWierszyDoUsunięcia = ((ZrodloLinqToTxt<T>)źródło).NumeryWybranychWierszy[dane.Expression];
            return ((ZrodloLinqToTxt<T>)źródło).Delete(numeryWierszyDoUsunięcia, odrazu);
        }

        public int Delete(T dane, bool odrazu)
        {
            List<T> doUsunięcia = new List<T>(1);
            doUsunięcia.Add(dane);
            return this.Delete(doUsunięcia, odrazu);
        }

        public int Delete(List<T> dane, bool odrazu)
        {
            if (dane == null) throw new NullReferenceException("Dane do usunięcia nie mogą być NULL.");
            return ((ZrodloLinqToTxt<T>)źródło).Delete(dane, odrazu);
        }

        public int Update(IOrderedQueryable<T> dane, Dictionary<string, string> noweWartości, bool odrazu)
        {
            if (!((ZrodloLinqToTxt<T>)źródło).NumeryWybranychWierszy.ContainsKey(dane.Expression))
                dane.GetEnumerator();
            List<int> numeryWierszyDoAktualizacji = ((ZrodloLinqToTxt<T>)źródło).NumeryWybranychWierszy[dane.Expression];

            return ((ZrodloLinqToTxt<T>)źródło).Update(numeryWierszyDoAktualizacji, noweWartości, odrazu);
        }

        public int Update(IQueryable<T> dane, Dictionary<string, string> noweWartości, bool odrazu)
        {
            if (!((ZrodloLinqToTxt<T>)źródło).NumeryWybranychWierszy.ContainsKey(dane.Expression))
                dane.GetEnumerator();
            List<int> numeryWierszyDoAktualizacji = ((ZrodloLinqToTxt<T>)źródło).NumeryWybranychWierszy[dane.Expression];

            return ((ZrodloLinqToTxt<T>)źródło).Update(numeryWierszyDoAktualizacji, noweWartości, odrazu);
        }

        public int Update(IOrderedQueryable<T> dane, List<string> noweWartości, bool odrazu)
        {
            if (!((ZrodloLinqToTxt<T>)źródło).NumeryWybranychWierszy.ContainsKey(dane.Expression))
                dane.GetEnumerator();
            List<int> numeryWierszyDoAktualizacji = ((ZrodloLinqToTxt<T>)źródło).NumeryWybranychWierszy[dane.Expression];

            return ((ZrodloLinqToTxt<T>)źródło).Update(numeryWierszyDoAktualizacji, noweWartości, odrazu);
        }

        public int Update(IQueryable<T> dane, List<string> noweWartości, bool odrazu)
        {
            if (!((ZrodloLinqToTxt<T>)źródło).NumeryWybranychWierszy.ContainsKey(dane.Expression))
                dane.GetEnumerator();
            List<int> numeryWierszyDoAktualizacji = ((ZrodloLinqToTxt<T>)źródło).NumeryWybranychWierszy[dane.Expression];

            return ((ZrodloLinqToTxt<T>)źródło).Update(numeryWierszyDoAktualizacji, noweWartości, odrazu);
        }

        public void Update()
        {
            ((ZrodloLinqToTxt<T>)źródło).Update();
        }
    }
}