//---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
//---------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;

namespace ExtendedGrid.Microsoft.Windows.Controls
{
    /// <summary>
    ///     Collection used as the ItemsSource of The DataGridColumnHeadersPresenter.  This is a wrapper around the Column collection; each item
    ///     returns the corresponding Column.Header.
    /// </summary>
    internal class ColumnHeaderCollection : IEnumerable, INotifyCollectionChanged, IDisposable
    {
        public ColumnHeaderCollection(ObservableCollection<DataGridColumn> columns)
        {
            _columns = columns;

            if (_columns != null)
            {
                _columns.CollectionChanged += OnColumnsChanged;
            }
        }

        public DataGridColumn ColumnFromIndex(int index)
        {
            if (index >= 0 && index < _columns.Count)
            {
                return _columns[index];
            }

            return null;
        }

        #region Notification Propagation

        /// <summary>
        /// Called when the Header property on a Column changes.  Causes us to fire a CollectionChanged event to specify that an item has been replaced.
        /// </summary>
        /// <param name="column"></param>
        internal void NotifyHeaderPropertyChanged(DataGridColumn column, DependencyPropertyChangedEventArgs e)
        {
            Debug.Assert(e.Property == DataGridColumn.HeaderProperty, "We only want to know about the header property changing");
            Debug.Assert(_columns.Contains(column));

            NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(
                NotifyCollectionChangedAction.Replace,
                e.NewValue,
                e.OldValue,
                _columns.IndexOf(column));

            FireCollectionChanged(args);
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            if (_columns != null)
            {
                _columns.CollectionChanged -= OnColumnsChanged;
            }
        }

        #endregion 

        #region IEnumerable Members

        public IEnumerator GetEnumerator()
        {
            return new ColumnHeaderCollectionEnumerator(_columns);
        }

        private class ColumnHeaderCollectionEnumerator : IEnumerator, IDisposable
        {
            public ColumnHeaderCollectionEnumerator(ObservableCollection<DataGridColumn> columns)
            {
                if (columns != null)
                {
                    _columns = columns;
                    _columns.CollectionChanged += OnColumnsChanged;
                }

                _current = -1;
            }

            #region IEnumerator Members

            public object Current
            {
                get
                {
                    if (IsValid)
                    {
                        DataGridColumn column = _columns[_current];

                        if (column != null)
                        {
                            return column.Header;
                        }

                        return null;
                    }

                    throw new InvalidOperationException();
                }
            }

            public bool MoveNext()
            {
                if (HasChanged)
                {
                    throw new InvalidOperationException();
                }

                if (_columns != null && _current < _columns.Count - 1)
                {
                    _current++;
                    return true;
                }

                return false;
            }

            public void Reset()
            {
                if (HasChanged)
                {
                    throw new InvalidOperationException();
                }

                _current = -1;
            }

            #endregion

            #region IDisposable Members

            public void Dispose()
            {
                if (_columns != null)
                {
                    _columns.CollectionChanged -= OnColumnsChanged;
                }
            }

            #endregion 

            #region Helpers

            private bool HasChanged
            {
                get
                {
                    return _columnsChanged;
                }
            }

            private bool IsValid
            {
                get
                {
                    return _columns != null && _current >= 0 && _current < _columns.Count && !HasChanged;
                }
            }

            private void OnColumnsChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                _columnsChanged = true;
            }

            #endregion 

            #region Data

            private int _current;
            private bool _columnsChanged;
            private ObservableCollection<DataGridColumn> _columns;

            #endregion
        }

        #endregion

        #region INotifyCollectionChanged Members

        /// <summary>
        /// INotifyCollectionChanged CollectionChanged event
        /// </summary>
        public event NotifyCollectionChangedEventHandler CollectionChanged;

        /// <summary>
        ///     Helper to raise a CollectionChanged event when the columns collection has changed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnColumnsChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            NotifyCollectionChangedEventArgs newArgs;

            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    newArgs = new NotifyCollectionChangedEventArgs(e.Action, HeadersFromColumns(e.NewItems), e.NewStartingIndex);
                    break;

                case NotifyCollectionChangedAction.Remove:
                    newArgs = new NotifyCollectionChangedEventArgs(e.Action, HeadersFromColumns(e.OldItems), e.OldStartingIndex);
                    break;

                case NotifyCollectionChangedAction.Move:
                    newArgs = new NotifyCollectionChangedEventArgs(e.Action, HeadersFromColumns(e.OldItems), e.NewStartingIndex, e.OldStartingIndex);
                    break;

                case NotifyCollectionChangedAction.Replace:
                    newArgs = new NotifyCollectionChangedEventArgs(e.Action, HeadersFromColumns(e.NewItems), HeadersFromColumns(e.OldItems), e.OldStartingIndex);
                    break;

                default:
                    newArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
                    break;
            }

            FireCollectionChanged(newArgs);
        }

        private void FireCollectionChanged(NotifyCollectionChangedEventArgs args)
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, args);
            }
        }
        
        private static object[] HeadersFromColumns(IList columns)
        {
            object[] headers = new object[columns.Count];

            for (int i = 0; i < columns.Count; i++)
            {
                DataGridColumn column = columns[i] as DataGridColumn;

                if (column != null)
                {
                    headers[i] = column.Header;
                }
                else
                {
                    headers[i] = null;
                }
            }

            return headers;
        }

        #endregion

        private ObservableCollection<DataGridColumn> _columns;
    }
}