using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.IO.IsolatedStorage;
using System.Windows;
using System.Windows.Threading;
using System.Xml.Serialization;
using C1.Silverlight.DataGrid;
using System.Collections.Specialized;

namespace C1DataGrid_SaveState
{
    public class C1SaveStateBehavior : DependencyObject
    {
        #region ** fields

        private bool doneLoading;
        private DispatcherTimer _saveSettingTimer;

        #endregion

        #region ** initialization

        /// <summary>
        /// Initializes a new instance of the <see cref="C1SaveStateBehavior"/> class.
        /// </summary>
        public C1SaveStateBehavior()
        {
        }

        private C1DataGrid AssociatedObject { get; set; }

        private void OnAttached()
        {
            doneLoading = true;
            _saveSettingTimer = new DispatcherTimer();
            _saveSettingTimer.Interval = TimeSpan.FromSeconds(0.5);
            _saveSettingTimer.Tick += SaveDataGridSettings;

            AssociatedObject.ColumnReordered += OnColumnReordered;
            AssociatedObject.SortChanged += OnSortChanged;
            AssociatedObject.GroupChanged += OnGroupChanged;
            AssociatedObject.FilterChanged += OnFilterChanged;
            AssociatedObject.FrozenColumnCountChanged += OnFrozenColumnCountChanged;
            AssociatedObject.AutoGeneratedColumns += OnAutoGeneratedColumns;
            AssociatedObject.Columns.CollectionChanged += OnColumnsCollectionChanged;
        }

        private void OnDetaching()
        {
            _saveSettingTimer.Tick -= SaveDataGridSettings;
            AssociatedObject.ColumnReordered -= OnColumnReordered;
            AssociatedObject.SortChanged -= OnSortChanged;
            AssociatedObject.GroupChanged -= OnGroupChanged;
            AssociatedObject.FilterChanged -= OnFilterChanged;
            AssociatedObject.FrozenColumnCountChanged -= OnFrozenColumnCountChanged;
            AssociatedObject.AutoGeneratedColumns -= OnAutoGeneratedColumns;
            AssociatedObject.Columns.CollectionChanged -= OnColumnsCollectionChanged;
        }

        #endregion

        #region ** object model

        /// <summary>
        /// Gets the save state behavior.
        /// </summary>
        /// <param name="obj">The data grid to which the saved state is applied.</param>
        /// <returns></returns>
        public static C1SaveStateBehavior GetSaveStateBehavior(C1DataGrid obj)
        {
            return (C1SaveStateBehavior)obj.GetValue(SaveStateBehaviorProperty);
        }

        /// <summary>
        /// Sets the save state behavior.
        /// </summary>
        /// <param name="obj">The data grid to which the saved state will be applied.</param>
        /// <param name="value">The behavior.</param>
        public static void SetSaveStateBehavior(C1DataGrid obj, C1SaveStateBehavior value)
        {
            obj.SetValue(SaveStateBehaviorProperty, value);
        }

        /// <summary>
        /// Attached advanced filters behavior.
        /// </summary>
        public static readonly DependencyProperty SaveStateBehaviorProperty =
            DependencyProperty.RegisterAttached("SaveStateBehavior", typeof(C1SaveStateBehavior), typeof(C1SaveStateBehavior), new PropertyMetadata(null, new PropertyChangedCallback((d, a) =>
            {
                if (a.OldValue != null)
                {
                    var behavior = (C1SaveStateBehavior)a.OldValue;
                    behavior.OnDetaching();
                    behavior.AssociatedObject = d as C1DataGrid;
                }
                if (a.NewValue != null)
                {
                    var behavior = (C1SaveStateBehavior)a.NewValue;
                    behavior.AssociatedObject = d as C1DataGrid;
                    behavior.OnAttached();
                }
            })));

        string fileName = "datagridsample";

        #endregion

        #region ** private stuff

        private void OnColumnReordered(object sender, C1.Silverlight.DataGrid.DataGridColumnEventArgs e)
        {
            SaveDataGridSettingsAsync();
        }

        private void OnSortChanged(object sender, DataGridSortChangedEventArgs e)
        {
            SaveDataGridSettingsAsync();
        }

        private void OnGroupChanged(object sender, DataGridGroupChangedEventArgs e)
        {
            SaveDataGridSettingsAsync();
        }

        private void OnFilterChanged(object sender, DataGridFilterChangedEventArgs e)
        {
            SaveDataGridSettingsAsync();
        }

        private void OnFrozenColumnCountChanged(object sender, C1.Silverlight.PropertyChangedEventArgs<int> e)
        {
            SaveDataGridSettingsAsync();
        }

        private void OnAutoGeneratedColumns(object sender, EventArgs e)
        {
            AssociatedObject.Columns["ImageUrl"].DisplayIndex = 0;
            if (HasDataGridSettings())
            {
                var dataGridSettings = GetDataGridSettings();
                AssociatedObject.FrozenColumnCount = dataGridSettings.FrozenColumnsCount;
                var sortDict = new Dictionary<DataGridColumn, UserColumnSettings.SortGroupState>();
                var groupDict = new Dictionary<DataGridColumn, UserColumnSettings.SortGroupState>();
                var filterList = new List<KeyValuePair<DataGridColumn, DataGridFilterState>>();
                foreach (var columnSetting in dataGridSettings.ColumnSettings)
                {
                    var column = AssociatedObject.Columns[columnSetting.ColumnName];
                    if (column != null)
                    {
                        column.DisplayIndex = columnSetting.DisplayIndex;
                        sortDict[column] = columnSetting.Sort;
                        if (columnSetting.Group.Direction != DataGridSortDirection.None)
                            groupDict[column] = columnSetting.Group;
                        filterList.Add(new KeyValuePair<DataGridColumn, DataGridFilterState>(column, columnSetting.Filter));
                        if (columnSetting.Width > 0)
                        {
                            column.Width = new DataGridLength(columnSetting.Width);
                        }
                    }
                }
                if (filterList.Count > 0)
                {
                    AssociatedObject.FilterBy(filterList.ToArray());
                }
                if (sortDict.Count > 0)
                {
                    var sortparam = sortDict.OrderBy(pair => pair.Value.Index).Select(pair => new KeyValuePair<DataGridColumn, DataGridSortDirection>(pair.Key, pair.Value.Direction)).ToArray();
                    AssociatedObject.SortBy(sortDict.OrderBy(pair => pair.Value.Index).Select(pair => new KeyValuePair<DataGridColumn, DataGridSortDirection>(pair.Key, pair.Value.Direction)).ToArray());
                }
                if (groupDict.Count > 0)
                {
                    var groupparam = groupDict.OrderBy(pair => pair.Value.Index).Select(pair => new KeyValuePair<DataGridColumn, DataGridSortDirection>(pair.Key, pair.Value.Direction)).ToArray();
                    AssociatedObject.GroupBy(groupDict.OrderBy(pair => pair.Value.Index).Select(pair => new KeyValuePair<DataGridColumn, DataGridSortDirection>(pair.Key, pair.Value.Direction)).ToArray());
                }
            }
            else
            {
                SaveDataGridSettingsAsync();
            }
        }

        private void OnColumnsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                var newColumns = e.NewItems.Cast<DataGridColumn>();
                foreach (var newColumn in newColumns)
                {
                    newColumn.WidthChanged += OnColumnWidthChanged;
                }
            }
            if (e.OldItems != null)
            {
                var oldColumns = e.OldItems.Cast<DataGridColumn>();
                foreach (var oldColumn in oldColumns)
                {
                    oldColumn.WidthChanged -= OnColumnWidthChanged;
                }
            }
        }

        private void OnColumnWidthChanged(object sender, C1.Silverlight.PropertyChangedEventArgs<DataGridLength> e)
        {
            SaveDataGridSettingsAsync();
        }

        private bool HasDataGridSettings()
        {
            IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication();
            return isoFile.FileExists(fileName);
        }

        private void SaveDataGridSettingsAsync()
        {
            if (!doneLoading)
                return;
            _saveSettingTimer.Start();
        }

        void SaveDataGridSettings(object sender, EventArgs e)
        {
            _saveSettingTimer.Stop();
            DataGridSettings settings = new DataGridSettings();
            settings.FrozenColumnsCount = AssociatedObject.FrozenColumnCount;
            foreach (var column in AssociatedObject.Columns)
            {
                UserColumnSettings columnSetting = new UserColumnSettings();
                columnSetting.ColumnName = column.Name;
                columnSetting.DisplayIndex = column.DisplayIndex;
                columnSetting.Sort = new UserColumnSettings.SortGroupState() { Direction = column.SortState.Direction, Index = column.SortState.Index };
                columnSetting.Group = new UserColumnSettings.SortGroupState() { Direction = column.GroupState.Direction, Index = column.GroupState.Index };
                columnSetting.Filter = column.FilterState;
                columnSetting.Width = column.Width.IsAbsolute ? column.Width.Value : -1;

                settings.ColumnSettings.Add(columnSetting);
            }

            IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication();
            using (IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream(fileName, FileMode.Create, isoFile))
            {
                XmlSerializer ser = CreateSerializer();
                ser.Serialize(isoStream, settings);
            }
        }

        private DataGridSettings GetDataGridSettings()
        {
            IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication();
            using (IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream(fileName, FileMode.Open, isoFile))
            {
                try
                {
                    XmlSerializer ser = CreateSerializer();
                    return (DataGridSettings)ser.Deserialize(isoStream);
                }
                catch
                {
                }
            }
            return new DataGridSettings();
        }

        private static XmlSerializer CreateSerializer()
        {
            // DataGridComboBoxFilter needs List<object>
            return new XmlSerializer(typeof(DataGridSettings), new[] { typeof(List<object>) });
        }

        #endregion

        #region ** settings classes

        public class UserColumnSettings
        {
            public class SortGroupState
            {
                public DataGridSortDirection Direction { get; set; }
                public int Index { get; set; }
            }
            public string ColumnName { get; set; }
            public int DisplayIndex { get; set; }
            public SortGroupState Sort { get; set; }
            public SortGroupState Group { get; set; }
            public DataGridFilterState Filter { get; set; }
            public double Width { get; set; }
        }

        public class DataGridSettings
        {
            public DataGridSettings()
            {
                ColumnSettings = new List<UserColumnSettings>();
            }
            public List<UserColumnSettings> ColumnSettings { get; set; }
            public int FrozenColumnsCount { get; set; }
        }

        #endregion
    }
}
