using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using System.Windows.Controls;
using EGC = ExtendedGrid.Microsoft.Windows.Controls;

namespace ExtendedGrid.Microsoft.Windows.Controls
{
    /// <summary>
    /// AutomationPeer for a cell item in a DataGridRow.
    /// Cell may not have a visual container if it is scrolled out of view.
    /// </summary>
    public sealed class DataGridCellItemAutomationPeer : AutomationPeer,
        IGridItemProvider, ITableItemProvider, IInvokeProvider, IScrollItemProvider, ISelectionItemProvider
    {
        #region Constructors

        /// <summary>
        /// AutomationPeer for an item in a DataGrid
        /// </summary>
        public DataGridCellItemAutomationPeer(object item, EGC.DataGridColumn dataGridColumn)
            : base()
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            if (dataGridColumn == null)
            {
                throw new ArgumentNullException("dataGridColumn");
            }

            _item = item;
            _column = dataGridColumn;
        }

        #endregion

        #region AutomationPeer Overrides
        
        ///
        protected override string GetAcceleratorKeyCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.GetAcceleratorKey() : string.Empty;
        }

        ///
        protected override string GetAccessKeyCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.GetAccessKey() : string.Empty;
        }

        ///
        protected override AutomationControlType GetAutomationControlTypeCore()
        {
            return AutomationControlType.Custom;
        }

        ///
        protected override string GetAutomationIdCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.GetAutomationId() : string.Empty;
        }

        ///
        protected override Rect GetBoundingRectangleCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.GetBoundingRectangle() : new Rect();
        }

        ///
        protected override List<AutomationPeer> GetChildrenCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.GetChildren() : null;
        }
        
        ///
        protected override string GetClassNameCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.GetClassName() : string.Empty;
        }

        ///
        protected override Point GetClickablePointCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.GetClickablePoint() : new Point(double.NaN, double.NaN);
        }

        ///
        protected override string GetHelpTextCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.GetHelpText() : string.Empty;
        }

        ///
        protected override string GetItemStatusCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.GetItemStatus() : string.Empty;
        }

        ///
        protected override string GetItemTypeCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.GetItemType() : string.Empty;
        }

        ///
        protected override AutomationPeer GetLabeledByCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.GetLabeledBy() : null;
        }

        ///
        protected override string GetLocalizedControlTypeCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.GetLocalizedControlType() : base.GetLocalizedControlTypeCore();
        }

        ///
        protected override string GetNameCore()
        {
            // It is better to get the clipboard content instead of wrapper peer Name which is empty in most cases
            object cellValue = _column.OnCopyingCellClipboardContent(_item);
            if (cellValue != null)
            {
                return cellValue.ToString();
            }

            return String.Empty;
        }

        ///
        protected override AutomationOrientation GetOrientationCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.GetOrientation() : AutomationOrientation.None;
        }

        ///
        public override object GetPattern(PatternInterface patternInterface)
        {
            switch (patternInterface)
            {
                case PatternInterface.Invoke:
                    if (!this.OwningDataGrid.IsReadOnly && !_column.IsReadOnly)
                    {
                        return this;
                    }

                    break;
                case PatternInterface.SelectionItem:
                case PatternInterface.ScrollItem:
                case PatternInterface.GridItem:
                case PatternInterface.TableItem:
                    return this;
            }

            return null;
        }
        
        ///
        protected override bool HasKeyboardFocusCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.HasKeyboardFocus() : false;
        }

        ///
        protected override bool IsContentElementCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.IsContentElement() : true;
        }

        ///
        protected override bool IsControlElementCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.IsControlElement() : true;
        }

        ///
        protected override bool IsEnabledCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.IsEnabled() : true;
        }

        ///
        protected override bool IsKeyboardFocusableCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.IsKeyboardFocusable() : false;
        }

        ///
        protected override bool IsOffscreenCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.IsOffscreen() : true;
        }

        ///
        protected override bool IsPasswordCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.IsPassword() : false;
        }

        ///
        protected override bool IsRequiredForFormCore()
        {
            return (this.OwningCellPeer != null) ? this.OwningCellPeer.IsRequiredForForm() : false;
        }

        ///
        protected override void SetFocusCore()
        {
            if (this.OwningCellPeer != null && this.OwningCellPeer.Owner.Focusable)
            {
                this.OwningCellPeer.SetFocus();
            }
        }
        
        #endregion

        #region IGridItemProvider

        int IGridItemProvider.Column
        {
            get
            {
                return this.OwningDataGrid.Columns.IndexOf(this._column);
            }
        }

        int IGridItemProvider.ColumnSpan
        {
            get
            {
                return 1;
            }
        }

        IRawElementProviderSimple IGridItemProvider.ContainingGrid
        {
            get
            { 
                return this.ContainingGrid;
            }
        }

        int IGridItemProvider.Row
        {
            get
            {
                return this.OwningDataGrid.Items.IndexOf(this._item);
            }
        }

        int IGridItemProvider.RowSpan
        {
            get
            {
                return 1;
            }
        }

        #endregion

        #region ITableItemProvider

        IRawElementProviderSimple[] ITableItemProvider.GetColumnHeaderItems()
        {
            if (this.OwningDataGrid != null &&
                (this.OwningDataGrid.HeadersVisibility & EGC.DataGridHeadersVisibility.Column) == EGC.DataGridHeadersVisibility.Column &&
                this.OwningDataGrid.ColumnHeadersPresenter != null)
            {
                EGC.DataGridColumnHeadersPresenter dataGridColumnHeadersPresenter = this.OwningDataGrid.ColumnHeadersPresenter;
                EGC.DataGridColumnHeader dataGridColumnHeader = dataGridColumnHeadersPresenter.ItemContainerGenerator.ContainerFromIndex(this.OwningDataGrid.Columns.IndexOf(_column)) as EGC.DataGridColumnHeader;
                if (dataGridColumnHeader != null)
                {
                    AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(dataGridColumnHeader);
                    if (peer != null)
                    {
                        List<IRawElementProviderSimple> providers = new List<IRawElementProviderSimple>(1);
                        providers.Add(ProviderFromPeer(peer));
                        return providers.ToArray();
                    }
                }
            }

            return null;
        }

        IRawElementProviderSimple[] ITableItemProvider.GetRowHeaderItems()
        {
            if (this.OwningDataGrid != null &&
                (this.OwningDataGrid.HeadersVisibility & EGC.DataGridHeadersVisibility.Row) == EGC.DataGridHeadersVisibility.Row)
            {
                EGC.DataGridAutomationPeer dataGridAutomationPeer = UIElementAutomationPeer.CreatePeerForElement(this.OwningDataGrid) as EGC.DataGridAutomationPeer;
                EGC.DataGridItemAutomationPeer dataGridItemAutomationPeer = dataGridAutomationPeer.GetOrCreateItemPeer(_item);
                if (dataGridItemAutomationPeer != null)
                {
                    AutomationPeer rowHeaderAutomationPeer = dataGridItemAutomationPeer.RowHeaderAutomationPeer;
                    if (rowHeaderAutomationPeer != null)
                    {
                        List<IRawElementProviderSimple> providers = new List<IRawElementProviderSimple>(1);
                        providers.Add(ProviderFromPeer(rowHeaderAutomationPeer));
                        return providers.ToArray();
                    }
                }
            }

            return null;
        }

        #endregion

        #region IInvokeProvider

        void IInvokeProvider.Invoke()
        {
            if (this.OwningDataGrid.IsReadOnly || _column.IsReadOnly)
            {
                return;
            }

            EnsureEnabled();

            bool success = false;

            // If the current cell is virtualized - scroll into view
            if (this.OwningCell == null)
            {
                this.OwningDataGrid.ScrollIntoView(_item, _column);
            }

            if (this.OwningCell != null)
            {
                IEditableCollectionView iecv = (IEditableCollectionView)this.OwningDataGrid.Items;
                if (iecv.CurrentEditItem == _item)
                {
                    success = this.OwningDataGrid.CommitEdit();
                }
                else
                {
                    this.OwningDataGrid.UnselectAll();
                    this.OwningCell.Focus();
                    success = this.OwningDataGrid.BeginEdit();
                }
            }

            if (!success)
            {
                throw new InvalidOperationException();
            }
        }

        #endregion

        #region IScrollItemProvider
        
        void IScrollItemProvider.ScrollIntoView()
        {
            this.OwningDataGrid.ScrollIntoView(_item, _column);
        }
        
        #endregion
        
        #region ISelectionItemProvider
        
        bool ISelectionItemProvider.IsSelected
        {
            get
            {
                return this.OwningDataGrid.SelectedCellsInternal.Contains(new EGC.DataGridCellInfo(_item, _column));
            }
        }

        IRawElementProviderSimple ISelectionItemProvider.SelectionContainer
        {
            get
            {
                return this.ContainingGrid;
            }
        }

        void ISelectionItemProvider.AddToSelection()
        {
            // If item is already selected - do nothing
            EGC.DataGridCellInfo currentCellInfo = new EGC.DataGridCellInfo(_item, _column);
            if (this.OwningDataGrid.SelectedCellsInternal.Contains(currentCellInfo))
            {
                return;
            }

            EnsureEnabled();

            if (this.OwningDataGrid.SelectionMode == EGC.DataGridSelectionMode.Single &&
                this.OwningDataGrid.SelectedCells.Count > 0)
            {
                throw new InvalidOperationException();
            }

            this.OwningDataGrid.SelectedCellsInternal.Add(currentCellInfo);
        }

        void ISelectionItemProvider.RemoveFromSelection()
        {
            EnsureEnabled();

            EGC.DataGridCellInfo currentCellInfo = new EGC.DataGridCellInfo(_item, _column);
            if (this.OwningDataGrid.SelectedCellsInternal.Contains(currentCellInfo))
            {
                this.OwningDataGrid.SelectedCellsInternal.Remove(currentCellInfo);
            }
        }

        void ISelectionItemProvider.Select()
        {
            EnsureEnabled();

            EGC.DataGridCellInfo currentCellInfo = new EGC.DataGridCellInfo(_item, _column);
            this.OwningDataGrid.SelectOnlyThisCell(currentCellInfo);
        }

        #endregion

        #region Private Methods

        private void EnsureEnabled()
        {
            if (!OwningDataGrid.IsEnabled)
            {
                throw new ElementNotEnabledException();
            }
        }
        
        #endregion

        #region Private Properties

        private EGC.DataGrid OwningDataGrid
        {
            get
            {
                return _column.DataGridOwner;
            }
        }

        // This may be null if the cell is virtualized
        private EGC.DataGridCell OwningCell
        {
            get
            {
                return this.OwningDataGrid.TryFindCell(_item, _column);
            }
        }

        internal EGC.DataGridCellAutomationPeer OwningCellPeer
        {
            get
            {
                EGC.DataGridCellAutomationPeer cellPeer = null;
                EGC.DataGridCell cell = this.OwningCell;
                if (cell != null)
                {
                    cellPeer = FrameworkElementAutomationPeer.CreatePeerForElement(cell) as EGC.DataGridCellAutomationPeer;
                    cellPeer.EventsSource = this;
                }

                return cellPeer;
            }
        }

        private IRawElementProviderSimple ContainingGrid
        {
            get
            {
                AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(OwningDataGrid);
                if (peer != null)
                {
                    return ProviderFromPeer(peer);
                }

                return null;
            }
        }

        #endregion

        #region Data

        private object _item;
        private EGC.DataGridColumn _column;

        #endregion
    }
}
