using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data.Services.Client;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using C1.Silverlight.DataGrid;

namespace C1DataGrid_WCF_DS
{
    public abstract class AstoriaDataSourceView<T> : IDataSourceView
        where T : class
    {
        private DataGridSortManager _sortManager;
        private DataGridFilterManager _filterManager;
        private IEnumerable<T> _source;

        // --------------------------------------------------------------------
        // Basic datasource methods/properties
        // --------------------------------------------------------------------
        public bool CanAdd { get { return true; } }

        public bool CanRemove { get { return true; } }

        public bool CanEdit { get { return true; } }

        public bool CanFilter { get { return true; } }

        public bool CanSort { get { return true; } }

        public bool Contains(object item) { return (_source != null) ? _source.Contains(item as T) : false; }

        //public int Count { get { return (_source != null) ? _source.Count() : 0; } }
        public int GetCount(object[] groupKeys)
        {
            return (_source != null) ? _source.Count() : 0;
        }


        public object GetDataItem(object[] groupKeys, int index) { return _source.ElementAt(index); }

        public int IndexOf(object[] groupKeys, object dataItem) { return (_source != null) ? _source.ToList().IndexOf(dataItem as T) : -1; }

        public bool IsEmpty { get { return GetCount(null) == 0; } }

        public IEnumerable SourceCollection { get { return _source; } }

        public IEnumerator GetEnumerator() { return _source.GetEnumerator(); }

        public event C1CollectionChangedEventHandler CollectionChanged;

        // --------------------------------------------------------------------
        // Refresh
        // --------------------------------------------------------------------
        public abstract void Refresh();

        protected IQueryable<T> Filter(IQueryable<T> initialQuery)
        {
            IQueryable<T> filteredQuery = initialQuery;
            foreach (FilterDescription filter in _filterManager.FilterDescriptions)
            {
                filteredQuery = filteredQuery.Where(filter.GetExpression<T>());
            }
            return filteredQuery;
        }

        protected IQueryable<T> Sort(IQueryable<T> initialQuery)
        {
            IQueryable<T> sortedQuery = initialQuery;
            foreach (SortDescription sortDescription in _sortManager.SortDescriptions)
            {
                sortedQuery = InvokeSort(sortDescription.PropertyName, new object[] { sortedQuery, sortDescription });
            }
            return sortedQuery;
        }

        public DataServiceQuery<T> SingleSort<PropertyType>(DataServiceQuery<T> sortedQuery, SortDescription sortDescription)
        {
            if (sortDescription.State.Direction == DataGridSortDirection.Ascending)
            {
                if (!IsSortedQuery(sortedQuery))
                {
                    sortedQuery = sortedQuery.OrderBy(GetKeySelector<PropertyType>(sortDescription.PropertyName)) as DataServiceQuery<T>;
                }
                else
                {
                    sortedQuery = ((IOrderedQueryable<T>)sortedQuery).ThenBy(GetKeySelector<PropertyType>(sortDescription.PropertyName)) as DataServiceQuery<T>; ;
                }
            }
            else
            {
                if (!IsSortedQuery(sortedQuery))
                {
                    sortedQuery = sortedQuery.OrderByDescending(GetKeySelector<PropertyType>(sortDescription.PropertyName)) as DataServiceQuery<T>;
                }
                else
                {
                    sortedQuery = ((IOrderedQueryable<T>)sortedQuery).ThenByDescending(GetKeySelector<PropertyType>(sortDescription.PropertyName)) as DataServiceQuery<T>; ;
                }
            }
            return sortedQuery;
        }

        // --------------------------------------------------------------------
        // CRUD methods
        // --------------------------------------------------------------------
        public object CreateDataItem()
        {
            Type type = typeof(T);
            object newInstance = null;
            if (type != null)
            {
                ConstructorInfo info = type.GetConstructor(new Type[] { });
                if (info != null)
                {
                    newInstance = info.Invoke(new object[] { });
                }
            }
            return newInstance;
        }

        public abstract void AddDataItems(KeyValuePair<int, Object>[] newItems);

        public abstract void EditDataItems(KeyValuePair<int, object>[] editedItems);

        public abstract void RemoveDataItems(KeyValuePair<int, object>[] removedItems);

        // -------------------------------------------------------------------
        // Helper Methods
        // --------------------------------------------------------------------

        protected void InvokeService(IQueryable<T> query)
        {
            DataServiceQuery<T> dataServiceQuery = (DataServiceQuery<T>)query;
            dataServiceQuery.BeginExecute(EndInvokeService, query);
        }

        private void EndInvokeService(IAsyncResult result)
        {
            DataServiceQuery<T> dataServiceQuery = result.AsyncState as DataServiceQuery<T>;
            IEnumerable<T> data = dataServiceQuery.EndExecute(result).ToList();
            SetSource(data);
        }

        private bool IsSortedQuery(IQueryable<T> query)
        {
            string sortMethod = "ORDERBY";
            return ((DataServiceQuery)query).RequestUri.ToString().ToUpper().Contains(sortMethod);
        }

        private DataServiceQuery<T> InvokeSort(string propertyPath, object[] parameters)
        {
            string methodName = "SingleSort";
            Type propertyType = GetPropertyType<T>(propertyPath);
            MethodInfo info = this.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance).MakeGenericMethod(new Type[] { propertyType });
            return info.Invoke(this, parameters) as DataServiceQuery<T>;
        }

        private Expression<Func<T, TResult>> GetKeySelector<TResult>(string propertyPath)
        {
            ParameterExpression param = Expression.Parameter(typeof(T), "e");
            string[] properties = propertyPath.Split('.');
            Expression propertyAccess = param;
            for (int i = 0; i < properties.Length; i++)
            {
                propertyAccess = Expression.Property(propertyAccess, properties[i]);
            }
            return Expression.Lambda<Func<T, TResult>>(propertyAccess, param);
        }

        private Type GetPropertyType<T>(string propertyPath)
        {
            string[] properties = propertyPath.Split('.');
            Type propertyType = typeof(T);
            for (int i = 0; i < properties.Length; i++)
            {
                propertyType = propertyType.GetProperty(properties[i]).PropertyType;
            }
            return propertyType;
        }

        // --------------------------------------------------------------------
        // Initialization
        // --------------------------------------------------------------------
        public AstoriaDataSourceView(IEnumerable<T> source)
        {
            SetSource(source);
        }

        public void Initialize(DataGridFilterManager filterManager, DataGridSortManager sortManager, DataGridGroupManager groupManager)
        {
            _filterManager = filterManager;
            _sortManager = sortManager;
        }

        private void SetSource(IEnumerable<T> source)
        {
            _source = source;
            if (CollectionChanged != null)
            {
                CollectionChanged(this, new C1CollectionChangedEventArgs());
            }
        }

        public object[] GetGroups(object[] groupKeys)
        {
            return new object[0];
        }

    }
}
