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

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using MS.Internal;
using EGC = ExtendedGrid.Microsoft.Windows.Controls;

namespace ExtendedGrid.Microsoft.Windows.Controls
{
    /// <summary>
    ///     Internal class that holds the DataGrid's column collection.  Handles error-checking columns as they come in.
    /// </summary>
    internal class DataGridColumnCollection : ObservableCollection<EGC.DataGridColumn>
    {
        internal DataGridColumnCollection(EGC.DataGrid dataGridOwner)
        {
            Debug.Assert(dataGridOwner != null, "We should have a valid DataGrid");

            DisplayIndexMap = new List<int>(5);
            _dataGridOwner = dataGridOwner;

            RealizedColumnsBlockListForNonVirtualizedRows = null;
            RealizedColumnsDisplayIndexBlockListForNonVirtualizedRows = null;
            RebuildRealizedColumnsBlockListForNonVirtualizedRows = true;

            RealizedColumnsBlockListForVirtualizedRows = null;
            RealizedColumnsDisplayIndexBlockListForVirtualizedRows = null;
            RebuildRealizedColumnsBlockListForVirtualizedRows = true;
        }

        #region Protected Overrides

        protected override void InsertItem(int index, EGC.DataGridColumn item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item", SR.Get(SRID.DataGrid_NullColumn));
            }

            if (item.DataGridOwner != null)
            {
                throw new ArgumentException(SR.Get(SRID.DataGrid_InvalidColumnReuse, item.Header), "item");
            }

            if (DisplayIndexMapInitialized)
            {
                ValidateDisplayIndex(item, item.DisplayIndex, true);
            }

            base.InsertItem(index, item);
            item.CoerceValue(EGC.DataGridColumn.IsFrozenProperty);
        }

        protected override void SetItem(int index, EGC.DataGridColumn item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item", SR.Get(SRID.DataGrid_NullColumn));
            }

            if (index >= Count || index < 0)
            {
                throw new ArgumentOutOfRangeException("index", SR.Get(SRID.DataGrid_ColumnIndexOutOfRange, item.Header));
            }

            if (item.DataGridOwner != null && this[index] != item)
            {
                throw new ArgumentException(SR.Get(SRID.DataGrid_InvalidColumnReuse, item.Header), "item");
            }

            if (DisplayIndexMapInitialized)
            {
                ValidateDisplayIndex(item, item.DisplayIndex);
            }

            base.SetItem(index, item);
            item.CoerceValue(EGC.DataGridColumn.IsFrozenProperty);
        }

        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    if (DisplayIndexMapInitialized)
                    {
                        UpdateDisplayIndexForNewColumns(e.NewItems, e.NewStartingIndex);
                    }

                    InvalidateHasVisibleStarColumns();
                    break;

                case NotifyCollectionChangedAction.Move:
                    if (DisplayIndexMapInitialized)
                    {
                        UpdateDisplayIndexForMovedColumn(e.OldStartingIndex, e.NewStartingIndex);
                    }

                    break;

                case NotifyCollectionChangedAction.Remove:
                    if (DisplayIndexMapInitialized)
                    {
                        UpdateDisplayIndexForRemovedColumns(e.OldItems, e.OldStartingIndex);
                    }

                    ClearDisplayIndex(e.OldItems, e.NewItems);
                    InvalidateHasVisibleStarColumns();
                    break;

                case NotifyCollectionChangedAction.Replace:
                    if (DisplayIndexMapInitialized)
                    {
                        UpdateDisplayIndexForReplacedColumn(e.OldItems, e.NewItems);
                    }

                    ClearDisplayIndex(e.OldItems, e.NewItems);
                    InvalidateHasVisibleStarColumns();
                    break;

                case NotifyCollectionChangedAction.Reset:
                    // We dont ClearDisplayIndex here because we no longer have access to the old items.
                    // Instead this is handled in ClearItems.
                    if (DisplayIndexMapInitialized)
                    {
                        DisplayIndexMap.Clear();
                        DataGridOwner.UpdateColumnsOnVirtualizedCellInfoCollections(NotifyCollectionChangedAction.Reset, -1, null, -1);
                    }

                    HasVisibleStarColumns = false;
                    break;
            }

            base.OnCollectionChanged(e);
        }

        /// <summary>
        ///     Clear's all the columns from this collection and resets DisplayIndex to its default value.
        /// </summary>
        protected override void ClearItems()
        {
            ClearDisplayIndex(this, null);

            // Clear DataGrid reference is on all columns.
            // Doing it here since CollectionChanged notification wouldn't
            // propagate the cleared columns list.
            DataGridOwner.UpdateDataGridReference(this, true);
            base.ClearItems();
        }

        #endregion 

        #region Notification Propagation

        internal void NotifyPropertyChanged(DependencyObject d, string propertyName, DependencyPropertyChangedEventArgs e, NotificationTarget target)
        {
            if (EGC.DataGridHelper.ShouldNotifyColumnCollection(target))
            {
                if (e.Property == EGC.DataGridColumn.DisplayIndexProperty)
                {
                    OnColumnDisplayIndexChanged((EGC.DataGridColumn)d, (int)e.OldValue, (int)e.NewValue);
                    if (((EGC.DataGridColumn)d).IsVisible)
                    {
                        InvalidateColumnRealization(true);
                    }
                }
                else if (e.Property == EGC.DataGridColumn.WidthProperty)
                {
                    if (((EGC.DataGridColumn)d).IsVisible)
                    {
                        InvalidateColumnRealization(false);
                    }
                }
                else if (e.Property == EGC.DataGrid.FrozenColumnCountProperty)
                {
                    InvalidateColumnRealization(false);
                    OnDataGridFrozenColumnCountChanged((int)e.OldValue, (int)e.NewValue);
                }
                else if (e.Property == EGC.DataGridColumn.VisibilityProperty)
                {
                    InvalidateHasVisibleStarColumns();
                    InvalidateColumnWidthsComputation();
                    InvalidateColumnRealization(true);
                }
                else if (e.Property == EGC.DataGrid.EnableColumnVirtualizationProperty)
                {
                    InvalidateColumnRealization(true);
                }
                else if (e.Property == EGC.DataGrid.CellsPanelHorizontalOffsetProperty)
                {
                    OnCellsPanelHorizontalOffsetChanged(e);
                }
                else if (e.Property == EGC.DataGrid.HorizontalScrollOffsetProperty ||
                         string.Compare(propertyName, "ViewportWidth", StringComparison.Ordinal) == 0)
                {
                    InvalidateColumnRealization(false);
                }
            }

            if (EGC.DataGridHelper.ShouldNotifyColumns(target))
            {
                int count = this.Count;
                for (int i = 0; i < count; i++)
                {
                    // Passing in NotificationTarget.Columns directly to ensure the notification doesn't
                    // bounce back to the collection.
                    this[i].NotifyPropertyChanged(d, e, NotificationTarget.Columns);
                }
            }
        }

        #endregion

        #region Display Index

        /// <summary>
        ///     Returns the DataGridColumn with the given DisplayIndex
        /// </summary>
        internal EGC.DataGridColumn ColumnFromDisplayIndex(int displayIndex)
        {
            Debug.Assert(displayIndex >= 0 && displayIndex < DisplayIndexMap.Count, "displayIndex should have already been validated");
            return this[DisplayIndexMap[displayIndex]];
        }

        /// <summary>
        ///     A map of display index (key) to index in the column collection (value).  Used to quickly find a column from its display index.
        /// </summary>
        internal List<int> DisplayIndexMap
        {
            get
            {
                if (!DisplayIndexMapInitialized)
                {
                    InitializeDisplayIndexMap();
                }

                return _displayIndexMap;
            }

            private set 
            { 
                _displayIndexMap = value; 
            }
        }

        /// <summary>
        ///     Used to guard against re-entrancy when changing the DisplayIndex of a column.
        /// </summary>
        private bool IsUpdatingDisplayIndex
        {
            get { return _isUpdatingDisplayIndex; }
            set { _isUpdatingDisplayIndex = value; }
        }

        private int CoerceDefaultDisplayIndex(EGC.DataGridColumn column)
        {
            return CoerceDefaultDisplayIndex(column, IndexOf(column));
        }

        /// <summary>
        ///     This takes a column and checks that if its DisplayIndex is the default value.  If so, it coerces
        ///     the DisplayIndex to be its location in the columns collection.
        ///     We can't do this in CoerceValue because the callback isn't called for default values.  Instead we call this
        ///     whenever a column is added or replaced in the collection or when the DisplayIndex of an existing column has changed.
        /// </summary>
        /// <param name="column">The column</param>
        /// <param name="newDisplayIndex">The DisplayIndex the column should have</param>
        /// <returns>The DisplayIndex of the column</returns>
        private int CoerceDefaultDisplayIndex(EGC.DataGridColumn column, int newDisplayIndex)
        {
            if (EGC.DataGridHelper.IsDefaultValue(column, EGC.DataGridColumn.DisplayIndexProperty))
            {
                bool isUpdating = IsUpdatingDisplayIndex;
                try
                {
                    IsUpdatingDisplayIndex = true;
                    column.DisplayIndex = newDisplayIndex;
                }
                finally
                {
                    IsUpdatingDisplayIndex = isUpdating;
                }

                return newDisplayIndex;
            }

            return column.DisplayIndex;
        }

        /// <summary>
        ///     Called when a column's display index has changed.  
        /// <param name="oldDisplayIndex">the old display index of the column</param>
        /// <param name="newDisplayIndex">the new display index of the column</param>
        private void OnColumnDisplayIndexChanged(EGC.DataGridColumn column, int oldDisplayIndex, int newDisplayIndex)
        {
            // Handle ClearValue.  -1 is the default value and really means 'DisplayIndex should be the index of the column in the column collection'.
            // We immediately replace the display index without notifying anyone.
            if (oldDisplayIndex == -1 || _isClearingDisplayIndex)
            {
                // change from -1 to the new value; the OnColumnDisplayIndexChanged further down the stack (from old value to -1) will handle
                // notifying the user and updating columns.
                return;
            }

            // The DisplayIndex may have changed to the default value.  
            newDisplayIndex = CoerceDefaultDisplayIndex(column);

            if (newDisplayIndex == oldDisplayIndex)
            {
                return;
            }

            // Our coerce value callback should have validated the DisplayIndex.  Fire the virtual.
            Debug.Assert(newDisplayIndex >= 0 && newDisplayIndex < Count, "The new DisplayIndex should have already been validated");
            DataGridOwner.OnColumnDisplayIndexChanged(new EGC.DataGridColumnEventArgs(column));

            // Call our helper to walk through all other columns and adjust their display indices.
            UpdateDisplayIndexForChangedColumn(oldDisplayIndex, newDisplayIndex);
        }

        /// <summary>
        ///     Called when the DisplayIndex for a single column has changed.  The other columns may have conflicting display indices, so
        ///     we walk through them and adjust.  This method does nothing if we're already updating display index as part of a larger
        ///     operation (such as add or remove).  This is both for re-entrancy and to avoid modifying the display index map as we walk over
        ///     the columns.
        /// </summary>
        private void UpdateDisplayIndexForChangedColumn(int oldDisplayIndex, int newDisplayIndex)
        {
            // The code below adjusts the DisplayIndex of other columns and shouldn't happen if this column's display index is changed
            // to account for the change in another.
            if (IsUpdatingDisplayIndex)
            {
                // Avoid re-entrancy; setting DisplayIndex on columns causes their OnDisplayIndexChanged to fire.
                return;
            }

            try
            {
                IsUpdatingDisplayIndex = true;

                Debug.Assert(oldDisplayIndex != newDisplayIndex, "A column's display index must have changed for us to call OnColumnDisplayIndexChanged");
                Debug.Assert(oldDisplayIndex >= 0 && oldDisplayIndex < Count, "The old DisplayIndex should be valid");

                // Update the display index mapping for all affected columns.
                int columnIndex = DisplayIndexMap[oldDisplayIndex];
                DisplayIndexMap.RemoveAt(oldDisplayIndex);
                DisplayIndexMap.Insert(newDisplayIndex, columnIndex);

                // Update the display index of other columns.
                if (newDisplayIndex < oldDisplayIndex)
                {
                    // DisplayIndex decreased. All columns with DisplayIndex >= newDisplayIndex and < oldDisplayIndex
                    // get their DisplayIndex incremented.
                    for (int i = newDisplayIndex + 1; i <= oldDisplayIndex; i++)
                    {
                        ColumnFromDisplayIndex(i).DisplayIndex++;
                    }
                }
                else
                {
                    // DisplayIndex increased. All columns with DisplayIndex <= newDisplayIndex and > oldDisplayIndex get their DisplayIndex decremented.
                    for (int i = oldDisplayIndex; i < newDisplayIndex; i++)
                    {
                        ColumnFromDisplayIndex(i).DisplayIndex--;
                    }
                }

                Debug_VerifyDisplayIndexMap();

                DataGridOwner.UpdateColumnsOnVirtualizedCellInfoCollections(NotifyCollectionChangedAction.Move, oldDisplayIndex, null, newDisplayIndex);
            }
            finally
            {
                IsUpdatingDisplayIndex = false;
            }
        }

        private void UpdateDisplayIndexForMovedColumn(int oldColumnIndex, int newColumnIndex)
        {
            int displayIndex = RemoveFromDisplayIndexMap(oldColumnIndex);
            InsertInDisplayIndexMap(displayIndex, newColumnIndex);
            DataGridOwner.UpdateColumnsOnVirtualizedCellInfoCollections(NotifyCollectionChangedAction.Move, oldColumnIndex, null, newColumnIndex);
        }

        /// <summary>
        ///     Sets the DisplayIndex on all newly inserted or added columns and updates the existing columns as necessary.  
        /// </summary>
        private void UpdateDisplayIndexForNewColumns(IList newColumns, int startingIndex)
        {
            EGC.DataGridColumn column;
            int newDisplayIndex, columnIndex;

            Debug.Assert(
                newColumns.Count == 1,
                "This derives from ObservableCollection; it is impossible to add multiple columns at once");
            Debug.Assert(IsUpdatingDisplayIndex == false, "We don't add new columns as part of a display index update operation");

            try
            {
                IsUpdatingDisplayIndex = true;

                // Set the display index of the new columns and add them to the DisplayIndexMap
                column = (EGC.DataGridColumn)newColumns[0];
                columnIndex = startingIndex;

                newDisplayIndex = CoerceDefaultDisplayIndex(column, columnIndex);

                // Inserting the column in the map means that all columns with display index >= the new column's display index
                // were given a higher display index.  This is perfect, except that the column indices have changed due to the insert
                // in the column collection.  We need to iterate over the column indices and increment them appropriately.  We also
                // need to give each changed column a new display index.
                InsertInDisplayIndexMap(newDisplayIndex, columnIndex);

                for (int i = 0; i < DisplayIndexMap.Count; i++)
                {
                    if (i > newDisplayIndex)
                    {
                        // All columns with DisplayIndex higher than the newly inserted columns
                        // need to have their DisplayIndex adiusted.
                        column = ColumnFromDisplayIndex(i);
                        column.DisplayIndex++;
                    }
                }

                Debug_VerifyDisplayIndexMap();

                DataGridOwner.UpdateColumnsOnVirtualizedCellInfoCollections(NotifyCollectionChangedAction.Add, -1, null, newDisplayIndex);
            }
            finally
            {
                IsUpdatingDisplayIndex = false;
            }
        }

        // This method is called in first DataGrid measure call
        // It needs to populate DisplayIndexMap and validate the DisplayIndex of all columns
        internal void InitializeDisplayIndexMap()
        {
            if (_displayIndexMapInitialized)
            {
                return;
            }

            _displayIndexMapInitialized = true;

            Debug.Assert(DisplayIndexMap.Count == 0, "DisplayIndexMap should be empty until first measure call.");
            int columnCount = Count;
            Dictionary<int, int> assignedDisplayIndexMap = new Dictionary<int, int>(); // <DisplayIndex, ColumnIndex>

            // First loop:
            // 1. Validate all columns DisplayIndex
            // 2. Add columns with DisplayIndex!=default to the assignedDisplayIndexMap
            for (int columnIndex = 0; columnIndex < columnCount; columnIndex++)
            {
                EGC.DataGridColumn currentColumn = this[columnIndex];
                int currentColumnDisplayIndex = currentColumn.DisplayIndex;
                
                ValidateDisplayIndex(currentColumn, currentColumnDisplayIndex);
                if (currentColumnDisplayIndex >= 0)
                {
                    if (assignedDisplayIndexMap.ContainsKey(currentColumnDisplayIndex))
                    {
                        throw new ArgumentException(SR.Get(SRID.DataGrid_DuplicateDisplayIndex));
                    }

                    assignedDisplayIndexMap.Add(currentColumnDisplayIndex, columnIndex);
                }
            }

            // Second loop:
            // Assign DisplayIndex to the columns with default values
            int nextAvailableColumnIndex = 0;
            for (int columnIndex = 0; columnIndex < columnCount; columnIndex++)
            {
                EGC.DataGridColumn currentColumn = this[columnIndex];
                int currentColumnDisplayIndex = currentColumn.DisplayIndex;

                bool hasDefaultDisplayIndex = EGC.DataGridHelper.IsDefaultValue(currentColumn, EGC.DataGridColumn.DisplayIndexProperty);
                if (hasDefaultDisplayIndex)
                {
                    while (assignedDisplayIndexMap.ContainsKey(nextAvailableColumnIndex))
                    {
                        nextAvailableColumnIndex++;
                    }

                    CoerceDefaultDisplayIndex(currentColumn, nextAvailableColumnIndex);
                    assignedDisplayIndexMap.Add(nextAvailableColumnIndex, columnIndex);
                    nextAvailableColumnIndex++;
                }
            }

            // Third loop:
            // Copy generated assignedDisplayIndexMap into DisplayIndexMap
            for (int displayIndex = 0; displayIndex < columnCount; displayIndex++)
            {
                Debug.Assert(assignedDisplayIndexMap.ContainsKey(displayIndex));
                DisplayIndexMap.Add(assignedDisplayIndexMap[displayIndex]);
            }
        }

        /// <summary>
        ///     Updates the display index for all columns affected by the removal of a set of columns.  
        /// </summary>
        private void UpdateDisplayIndexForRemovedColumns(IList oldColumns, int startingIndex)
        {
            EGC.DataGridColumn column;
            Debug.Assert(
                oldColumns.Count == 1, 
                "This derives from ObservableCollection; it is impossible to remove multiple columns at once");
            Debug.Assert(IsUpdatingDisplayIndex == false, "We don't remove columns as part of a display index update operation");

            try
            {
                IsUpdatingDisplayIndex = true;
                Debug.Assert(DisplayIndexMap.Count > Count, "Columns were just removed: the display index map shouldn't have yet been updated");

                int removedDisplayIndex = RemoveFromDisplayIndexMap(startingIndex);

                // Removing the column in the map means that all columns with display index >= the new column's display index
                // were given a lower display index.  This is perfect, except that the column indices have changed due to the insert
                // in the column collection.  We need to iterate over the column indices and decrement them appropriately.  We also
                // need to give each changed column a new display index.
                for (int i = 0; i < DisplayIndexMap.Count; i++)
                {
                    if (i >= removedDisplayIndex)
                    {
                        // All columns with DisplayIndex higher than the newly deleted columns need to have their DisplayIndex adiusted
                        // (we use >= because a column will have been decremented to have the same display index as the deleted column).
                        column = ColumnFromDisplayIndex(i);
                        column.DisplayIndex--;
                    }
                }

                Debug_VerifyDisplayIndexMap();

                DataGridOwner.UpdateColumnsOnVirtualizedCellInfoCollections(NotifyCollectionChangedAction.Remove, removedDisplayIndex, (EGC.DataGridColumn)oldColumns[0], -1);
            }
            finally
            {
                IsUpdatingDisplayIndex = false;
            }
        }

        /// <summary>
        ///     Updates the display index for the column that was just replaced and adjusts the other columns if necessary
        /// </summary>
        private void UpdateDisplayIndexForReplacedColumn(IList oldColumns, IList newColumns)
        { 
            if (oldColumns != null && oldColumns.Count > 0 && newColumns != null && newColumns.Count > 0)
            {
                Debug.Assert(oldColumns.Count == 1 && newColumns.Count == 1, "Multi replace isn't possible with ObservableCollection");
                EGC.DataGridColumn oldColumn = (EGC.DataGridColumn)oldColumns[0];
                EGC.DataGridColumn newColumn = (EGC.DataGridColumn)newColumns[0];

                if (oldColumn != null && newColumn != null)
                {
                    int newDisplayIndex = CoerceDefaultDisplayIndex(newColumn);

                    if (oldColumn.DisplayIndex != newDisplayIndex)
                    {
                        // Update the display index of other columns to adjust for that of the new one.
                        UpdateDisplayIndexForChangedColumn(oldColumn.DisplayIndex, newDisplayIndex);
                    }

                    DataGridOwner.UpdateColumnsOnVirtualizedCellInfoCollections(NotifyCollectionChangedAction.Replace, newDisplayIndex, oldColumn, newDisplayIndex);
                }
            }
        }

        /// <summary>
        ///     Clears the DisplayIndexProperty on each of the columns.
        /// </summary>
        private void ClearDisplayIndex(IList oldColumns, IList newColumns)
        {
            if (oldColumns != null)
            {
                try
                {
                    _isClearingDisplayIndex = true;
                    var count = oldColumns.Count;
                    for (int i = 0; i < count; i++)
                    {
                        var column = (EGC.DataGridColumn)oldColumns[i];
                        
                        // Only clear the old column's index if its not in newColumns
                        if (newColumns != null && newColumns.Contains(column))
                        {
                            continue;
                        }

                        column.ClearValue(EGC.DataGridColumn.DisplayIndexProperty);
                    }
                }
                finally
                {
                    _isClearingDisplayIndex = false;
                }
            }
        }

        /// <summary>
        ///     Returns true if the display index is valid for the given column
        /// </summary>
        private bool IsDisplayIndexValid(EGC.DataGridColumn column, int displayIndex, bool isAdding)
        {
            // -1 is legal only as a default value
            if (displayIndex == -1 && EGC.DataGridHelper.IsDefaultValue(column, EGC.DataGridColumn.DisplayIndexProperty))
            {
                return true;
            }
             
            // If we're adding a column the count will soon be increased by one -- so a DisplayIndex == Count is ok.
            return displayIndex >= 0 && (isAdding ? displayIndex <= Count : displayIndex < Count);
        }

        /// <summary>
        ///     Inserts the given columnIndex in the DisplayIndexMap at the given display index.
        /// </summary>
        private void InsertInDisplayIndexMap(int newDisplayIndex, int columnIndex)
        {
            DisplayIndexMap.Insert(newDisplayIndex, columnIndex);

            for (int i = 0; i < DisplayIndexMap.Count; i++)
            {
                if (DisplayIndexMap[i] >= columnIndex && i != newDisplayIndex)
                {
                    // These are columns that are after the inserted item in the column collection; we have to adiust
                    // to account for the shifted column index.
                    DisplayIndexMap[i]++;
                }
            }
        }

        /// <summary>
        ///     Removes the given column index from the DisplayIndexMap
        /// </summary>
        private int RemoveFromDisplayIndexMap(int columnIndex)
        {
            int removedDisplayIndex = DisplayIndexMap.IndexOf(columnIndex);
            Debug.Assert(removedDisplayIndex >= 0);

            DisplayIndexMap.RemoveAt(removedDisplayIndex);

            for (int i = 0; i < DisplayIndexMap.Count; i++)
            {
                if (DisplayIndexMap[i] >= columnIndex)
                {
                    // These are columns that are after the removed item in the column collection; we have to adiust
                    // to account for the shifted column index.
                    DisplayIndexMap[i]--;
                }
            }

            return removedDisplayIndex;
        }

        /// <summary>
        ///     Throws an ArgumentOutOfRangeException if the given displayIndex is invalid for the given column.
        /// </summary>
        internal void ValidateDisplayIndex(EGC.DataGridColumn column, int displayIndex)
        {
            ValidateDisplayIndex(column, displayIndex, false);
        }

        /// <summary>
        ///     Throws an ArgumentOutOfRangeException if the given displayIndex is invalid for the given column.
        /// </summary>
        internal void ValidateDisplayIndex(EGC.DataGridColumn column, int displayIndex, bool isAdding)
        {
            if (!IsDisplayIndexValid(column, displayIndex, isAdding))
            {
                throw new ArgumentOutOfRangeException("displayIndex", displayIndex, SR.Get(SRID.DataGrid_ColumnDisplayIndexOutOfRange, column.Header));
            }
        }

        [Conditional("DEBUG")]
        private void Debug_VerifyDisplayIndexMap()
        {
            Debug.Assert(Count == DisplayIndexMap.Count, "Display Index map is of the wrong size");
            for (int i = 0; i < DisplayIndexMap.Count; i++)
            {
                Debug.Assert(DisplayIndexMap[i] >= 0 && DisplayIndexMap[i] < Count, "DisplayIndex map entry doesn't point to a valid column");
                Debug.Assert(ColumnFromDisplayIndex(i).DisplayIndex == i, "DisplayIndex map doesn't match column indices");
            }
        }

        #endregion 

        #region Frozen Columns

        /// <summary>
        ///     Method which sets / resets the IsFrozen property of columns based on DataGrid's FrozenColumnCount.
        ///     It is possible that the FrozenColumnCount change could be a result of column count itself, in
        ///     which case only the columns which are in the collection at the moment are to be considered.
        /// </summary>
        /// <param name="oldFrozenCount"></param>
        /// <param name="newFrozenCount"></param>
        private void OnDataGridFrozenColumnCountChanged(int oldFrozenCount, int newFrozenCount)
        {
            if (newFrozenCount > oldFrozenCount)
            {
                int columnCount = Math.Min(newFrozenCount, Count);
                for (int i = oldFrozenCount; i < columnCount; i++)
                {
                    ColumnFromDisplayIndex(i).IsFrozen = true;
                }
            }
            else
            {
                int columnCount = Math.Min(oldFrozenCount, Count);
                for (int i = newFrozenCount; i < columnCount; i++)
                {
                    ColumnFromDisplayIndex(i).IsFrozen = false;
                }
            }
        }

        #endregion

        #region Helpers

        private EGC.DataGrid DataGridOwner
        {
            get { return _dataGridOwner; }
        }

        // Used by DataGridColumnCollection to delay the validation of DisplayIndex
        // Validation should be delayed because we in the process of adding columns we may have DisplayIndex less that current columns number
        // After all columns are generated or added in xaml we can do the validation
        internal bool DisplayIndexMapInitialized 
        { 
            get 
            { 
                return _displayIndexMapInitialized; 
            } 
        }

        #endregion

        #region Star Column Helper

        /// <summary>
        ///     Method which determines if there are any
        ///     star columns in datagrid except the given column and also returns perStarWidth
        /// </summary>
        private bool HasVisibleStarColumnsInternal(EGC.DataGridColumn ignoredColumn, out double perStarWidth)
        {
            bool hasStarColumns = false;
            perStarWidth = 0.0;
            foreach (EGC.DataGridColumn column in this)
            {
                if (column == ignoredColumn || 
                    !column.IsVisible)
                {
                    continue;
                }

                EGC.DataGridLength width = column.Width;
                if (width.IsStar)
                {
                    hasStarColumns = true;
                    if (!DoubleUtil.AreClose(width.Value, 0.0) &&
                        !DoubleUtil.AreClose(width.DesiredValue, 0.0))
                    {
                        perStarWidth = width.DesiredValue / width.Value;
                        break;
                    }
                }
            }

            return hasStarColumns;
        }

        /// <summary>
        ///     Method which determines if there are any
        ///     star columns in datagrid and also returns perStarWidth
        /// </summary>
        private bool HasVisibleStarColumnsInternal(out double perStarWidth)
        {
            return HasVisibleStarColumnsInternal(null, out perStarWidth);
        }

        /// <summary>
        ///     Method which determines if there are any
        ///     star columns in datagrid except the given column
        /// </summary>
        private bool HasVisibleStarColumnsInternal(EGC.DataGridColumn ignoredColumn)
        {
            double perStarWidth;
            return HasVisibleStarColumnsInternal(ignoredColumn, out perStarWidth);
        }

        /// <summary>
        ///     Property which determines if there are any star columns
        ///     in the datagrid.
        /// </summary>
        internal bool HasVisibleStarColumns 
        { 
            get; private set; 
        }        

        /// <summary>
        ///     Method which redetermines if the collection has any star columns are not.
        /// </summary>
        internal void InvalidateHasVisibleStarColumns()
        {
            HasVisibleStarColumns = HasVisibleStarColumnsInternal(null);
        }

        /// <summary>
        ///     Method which redistributes the width of star columns among them selves
        /// </summary>
        private void RecomputeStarColumnWidths()
        {
            double totalDisplaySpace = DataGridOwner.GetViewportWidthForColumns();
            double nonStarSpace = 0.0;
            foreach (EGC.DataGridColumn column in this)
            {
                EGC.DataGridLength width = column.Width;
                if (column.IsVisible && !width.IsStar)
                {
                    nonStarSpace += width.DisplayValue;
                }
            }

            if (DoubleUtil.IsNaN(nonStarSpace))
            {
                return;
            }

            ComputeStarColumnWidths(totalDisplaySpace - nonStarSpace);
        }

        /// <summary>
        ///     Helper method which computes the widths of all the star columns
        /// </summary>
        private double ComputeStarColumnWidths(double availableStarSpace)
        {
            Debug.Assert(
                !DoubleUtil.IsNaN(availableStarSpace) && !Double.IsNegativeInfinity(availableStarSpace) && !Double.IsPositiveInfinity(availableStarSpace),
                "availableStarSpace is not valid");

            List<EGC.DataGridColumn> unResolvedColumns = new List<EGC.DataGridColumn>();
            List<EGC.DataGridColumn> partialResolvedColumns = new List<EGC.DataGridColumn>();
            double totalFactors = 0.0;
            double totalMinWidths = 0.0;
            double totalMaxWidths = 0.0;
            double utilizedStarSpace = 0.0;

            // Accumulate all the star columns into unResolvedColumns in the beginning
            foreach (EGC.DataGridColumn column in this)
            {
                EGC.DataGridLength width = column.Width;
                if (column.IsVisible && width.IsStar)
                {
                    unResolvedColumns.Add(column);
                    totalFactors += width.Value;
                    totalMinWidths += column.MinWidth;
                    totalMaxWidths += column.MaxWidth;
                }
            }

            if (DoubleUtil.LessThan(availableStarSpace, totalMinWidths))
            {
                availableStarSpace = totalMinWidths;
            }

            if (DoubleUtil.GreaterThan(availableStarSpace, totalMaxWidths))
            {
                availableStarSpace = totalMaxWidths;
            }

            while (unResolvedColumns.Count > 0)
            {
                double starValue = availableStarSpace / totalFactors;

                // Find all the columns whose star share is less than thier min width and move such columns
                // into partialResolvedColumns giving them atleast the minwidth and there by reducing the availableSpace and totalFactors
                for (int i = 0, count = unResolvedColumns.Count; i < count; i++)
                {
                    EGC.DataGridColumn column = unResolvedColumns[i];
                    EGC.DataGridLength width = column.Width;

                    double columnMinWidth = column.MinWidth;
                    double starColumnWidth = availableStarSpace * width.Value / totalFactors;

                    if (DoubleUtil.GreaterThan(columnMinWidth, starColumnWidth))
                    {
                        availableStarSpace = Math.Max(0.0, availableStarSpace - columnMinWidth);
                        totalFactors -= width.Value;
                        unResolvedColumns.RemoveAt(i);
                        i--;
                        count--;
                        partialResolvedColumns.Add(column);
                    }
                }

                // With the remaining space determine in any columns star share is more than maxwidth.
                // If such columns are found give them their max width and remove them from unResolvedColumns
                // there by reducing the availablespace and totalfactors. If such column is found, the remaining columns are to be recomputed
                bool iterationRequired = false;
                for (int i = 0, count = unResolvedColumns.Count; i < count; i++)
                {
                    EGC.DataGridColumn column = unResolvedColumns[i];
                    EGC.DataGridLength width = column.Width;

                    double columnMaxWidth = column.MaxWidth;
                    double starColumnWidth = availableStarSpace * width.Value / totalFactors;

                    if (DoubleUtil.LessThan(columnMaxWidth, starColumnWidth))
                    {
                        iterationRequired = true;
                        unResolvedColumns.RemoveAt(i);
                        availableStarSpace -= columnMaxWidth;
                        utilizedStarSpace += columnMaxWidth;
                        totalFactors -= width.Value;
                        column.UpdateWidthForStarColumn(columnMaxWidth, starValue * width.Value, width.Value);
                        break;
                    }
                }

                // If it was determined by the previous step that another iteration is needed
                // then move all the partialResolvedColumns back to unResolvedColumns and there by
                // restoring availablespace and totalfactors.
                // If another iteration is not needed then allocate min widths to all columns in 
                // partial resolved columns and star share to all unresolved columns there by
                // ending the loop
                if (iterationRequired)
                {
                    for (int i = 0, count = partialResolvedColumns.Count; i < count; i++)
                    {
                        EGC.DataGridColumn column = partialResolvedColumns[i];

                        unResolvedColumns.Add(column);
                        availableStarSpace += column.MinWidth;
                        totalFactors += column.Width.Value;
                    }

                    partialResolvedColumns.Clear();
                }
                else
                {
                    for (int i = 0, count = partialResolvedColumns.Count; i < count; i++)
                    {
                        EGC.DataGridColumn column = partialResolvedColumns[i];
                        EGC.DataGridLength width = column.Width;
                        double columnMinWidth = column.MinWidth;
                        column.UpdateWidthForStarColumn(columnMinWidth, width.Value * starValue, width.Value);
                        utilizedStarSpace += columnMinWidth;
                    }

                    partialResolvedColumns.Clear();
                    for (int i = 0, count = unResolvedColumns.Count; i < count; i++)
                    {
                        EGC.DataGridColumn column = unResolvedColumns[i];
                        EGC.DataGridLength width = column.Width;
                        double starColumnWidth = availableStarSpace * width.Value / totalFactors;
                        column.UpdateWidthForStarColumn(starColumnWidth, width.Value * starValue, width.Value);
                        utilizedStarSpace += starColumnWidth;
                    }

                    unResolvedColumns.Clear();
                }
            }

            return utilizedStarSpace;
        }

        #endregion

        #region Column Width Computation Helper

        /// <summary>
        ///     Method which handles the column widths computation for CellsPanelHorizontalOffset change
        /// </summary>
        private void OnCellsPanelHorizontalOffsetChanged(DependencyPropertyChangedEventArgs e)
        {
            InvalidateColumnRealization(false);

            // Change in CellsPanelOffset width has an opposite effect on Column
            // width distribution. Hence widthChange is (oldvalue - newvalue)
            double totalAvailableWidth = DataGridOwner.GetViewportWidthForColumns();
            RedistributeColumnWidthsOnAvailableSpaceChange((double)e.OldValue - (double)e.NewValue, totalAvailableWidth);
        }

        /// <summary>
        ///     Helper method to invalidate the average width computation
        /// </summary>
        internal void InvalidateAverageColumnWidth()
        {
            _averageColumnWidth = null;
        }

        /// <summary>
        ///     Property holding the average widht of columns
        /// </summary>
        internal double AverageColumnWidth
        {
            get
            {
                if (!_averageColumnWidth.HasValue)
                {
                    _averageColumnWidth = ComputeAverageColumnWidth();
                }

                return _averageColumnWidth.Value;
            }
        }

        /// <summary>
        ///     Helper method which determines the average with of all the columns
        /// </summary>
        private double ComputeAverageColumnWidth()
        {
            double eligibleDisplayValue = 0.0;
            int totalFactors = 0;
            foreach (EGC.DataGridColumn column in this)
            {
                EGC.DataGridLength width = column.Width;
                if (column.IsVisible && !DoubleUtil.IsNaN(width.DisplayValue))
                {
                    eligibleDisplayValue += width.DisplayValue;
                    totalFactors++;
                }
            }

            if (totalFactors != 0)
            {
                return eligibleDisplayValue / totalFactors;
            }

            return 0.0;
        }

        /// <summary>
        ///     Property indicating whether the column width computation opertaion is pending
        /// </summary>
        internal bool ColumnWidthsComputationPending
        {
            get
            {
                return _columnWidthsComputationPending;
            }
        }

        /// <summary>
        ///     Helper method to invalidate the column width computation
        /// </summary>
        internal void InvalidateColumnWidthsComputation()
        {
            if (_columnWidthsComputationPending)
            {
                return;
            }

            DataGridOwner.Dispatcher.BeginInvoke(new DispatcherOperationCallback(ComputeColumnWidths), DispatcherPriority.Render, this);
            _columnWidthsComputationPending = true;
        }

        /// <summary>
        ///     Helper method which computes the widths of the columns. Used as a callback
        ///     to dispatcher operation
        /// </summary>
        private object ComputeColumnWidths(object arg)
        {
            ComputeColumnWidths();
            DataGridOwner.NotifyPropertyChanged(
                DataGridOwner, 
                "DelayedColumnWidthComputation", 
                new DependencyPropertyChangedEventArgs(), 
                NotificationTarget.CellsPresenter | NotificationTarget.ColumnHeadersPresenter);
            return null;
        }

        /// <summary>
        ///     Method which computes the widths of the columns
        /// </summary>
        private void ComputeColumnWidths()
        {
            if (HasVisibleStarColumns)
            {
                InitializeColumnDisplayValues();
                DistributeSpaceAmongColumns(DataGridOwner.GetViewportWidthForColumns());
            }
            else
            {
                ExpandAllColumnWidthsToDesiredValue();
            }

            _columnWidthsComputationPending = false;
        }

        /// <summary>
        ///     Method which initializes the column width's diplay value to its desired value
        /// </summary>
        private void InitializeColumnDisplayValues()
        {
            foreach (EGC.DataGridColumn column in this)
            {
                if (!column.IsVisible)
                {
                    continue;
                }

                EGC.DataGridLength width = column.Width;
                if (!width.IsStar)
                {
                    double minWidth = column.MinWidth;
                    double displayValue = EGC.DataGridHelper.CoerceToMinMax(DoubleUtil.IsNaN(width.DesiredValue) ? minWidth : width.DesiredValue, minWidth, column.MaxWidth);
                    if (!DoubleUtil.AreClose(width.DisplayValue, displayValue))
                    {
                        column.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, displayValue));
                    }
                }
            }
        }

        /// <summary>
        ///     Method which redistributes the column widths based on change in MinWidth of a column
        /// </summary>
        internal void RedistributeColumnWidthsOnMinWidthChangeOfColumn(EGC.DataGridColumn changedColumn, double oldMinWidth)
        {
            EGC.DataGridLength width = changedColumn.Width;
            double minWidth = changedColumn.MinWidth;
            if (DoubleUtil.GreaterThan(minWidth, width.DisplayValue))
            {
                if (HasVisibleStarColumns)
                {
                    TakeAwayWidthFromColumns(changedColumn, minWidth - width.DisplayValue, false);
                }

                changedColumn.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, minWidth));
            }
            else if (DoubleUtil.LessThan(minWidth, oldMinWidth))
            {
                if (width.IsStar)
                {
                    if (DoubleUtil.AreClose(width.DisplayValue, oldMinWidth))
                    {
                        GiveAwayWidthToColumns(changedColumn, oldMinWidth - minWidth, true);
                    }
                }
                else if (DoubleUtil.GreaterThan(oldMinWidth, width.DesiredValue))
                {
                    double displayValue = Math.Max(width.DesiredValue, minWidth);
                    if (HasVisibleStarColumns)
                    {
                        GiveAwayWidthToColumns(changedColumn, oldMinWidth - displayValue);
                    }

                    changedColumn.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, displayValue));
                }
            }
        }

        /// <summary>
        ///     Method which redistributes the column widths based on change in MaxWidth of a column
        /// </summary>
        internal void RedistributeColumnWidthsOnMaxWidthChangeOfColumn(EGC.DataGridColumn changedColumn, double oldMaxWidth)
        {
            EGC.DataGridLength width = changedColumn.Width;
            double maxWidth = changedColumn.MaxWidth;
            if (DoubleUtil.LessThan(maxWidth, width.DisplayValue))
            {
                if (HasVisibleStarColumns)
                {
                    GiveAwayWidthToColumns(changedColumn, width.DisplayValue - maxWidth);
                }

                changedColumn.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, maxWidth));
            }
            else if (DoubleUtil.GreaterThan(maxWidth, oldMaxWidth))
            {
                if (width.IsStar)
                {
                    RecomputeStarColumnWidths();
                }
                else if (DoubleUtil.LessThan(oldMaxWidth, width.DesiredValue))
                {
                    double displayValue = Math.Min(width.DesiredValue, maxWidth);
                    if (HasVisibleStarColumns)
                    {
                        double leftOverSpace = TakeAwayWidthFromUnusedSpace(false, displayValue - oldMaxWidth);
                        leftOverSpace = TakeAwayWidthFromStarColumns(changedColumn, leftOverSpace);
                        displayValue -= leftOverSpace;
                    }

                    changedColumn.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, displayValue));
                }
            }
        }

        /// <summary>
        ///     Method which redistributes the column widths based on change in Width of a column
        /// </summary>
        internal void RedistributeColumnWidthsOnWidthChangeOfColumn(EGC.DataGridColumn changedColumn, EGC.DataGridLength oldWidth)
        {
            EGC.DataGridLength width = changedColumn.Width;
            bool hasStarColumns = HasVisibleStarColumns;
            if (oldWidth.IsStar && !width.IsStar && !hasStarColumns)
            {
                ExpandAllColumnWidthsToDesiredValue();
            }
            else if (width.IsStar && !oldWidth.IsStar)
            {
                if (!HasVisibleStarColumnsInternal(changedColumn))
                {
                    ComputeColumnWidths();
                }
                else
                {
                    double minWidth = changedColumn.MinWidth;
                    double leftOverSpace = GiveAwayWidthToNonStarColumns(null, oldWidth.DisplayValue - minWidth);
                    changedColumn.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, minWidth + leftOverSpace));
                    RecomputeStarColumnWidths();
                }
            }
            else if (width.IsStar && oldWidth.IsStar)
            {
                RecomputeStarColumnWidths();
            }
            else if (hasStarColumns)
            {
                RedistributeColumnWidthsOnNonStarWidthChange(
                    changedColumn,
                    oldWidth);
            }
        }

        /// <summary>
        ///     Method which redistributes the column widths based on change in available space of a column
        /// </summary>
        internal void RedistributeColumnWidthsOnAvailableSpaceChange(double availableSpaceChange, double newTotalAvailableSpace)
        {
            if (!ColumnWidthsComputationPending && HasVisibleStarColumns)
            {
                if (DoubleUtil.GreaterThan(availableSpaceChange, 0.0))
                {
                    GiveAwayWidthToColumns(null, availableSpaceChange);
                }
                else if (DoubleUtil.LessThan(availableSpaceChange, 0.0))
                {
                    TakeAwayWidthFromColumns(null, Math.Abs(availableSpaceChange), false, newTotalAvailableSpace);
                }
            }
        }

        /// <summary>
        ///     Method which expands the display values of widths of all columns to
        ///     their desired values. Usually used when the last star column's width
        ///     is changed to non-star
        /// </summary>
        private void ExpandAllColumnWidthsToDesiredValue()
        {
            foreach (EGC.DataGridColumn column in this)
            {
                if (!column.IsVisible)
                {
                    continue;
                }

                EGC.DataGridLength width = column.Width;
                double maxWidth = column.MaxWidth;
                if (DoubleUtil.GreaterThan(width.DesiredValue, width.DisplayValue) &&
                    !DoubleUtil.AreClose(width.DisplayValue, maxWidth))
                {
                    column.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, Math.Min(width.DesiredValue, maxWidth)));
                }
            }
        }

        /// <summary>
        ///     Method which redistributes widths of columns on change of a column's width
        ///     when datagrid itself has star columns, but neither the oldwidth or the newwidth
        ///     of changed column is star.
        /// </summary>
        private void RedistributeColumnWidthsOnNonStarWidthChange(EGC.DataGridColumn changedColumn, EGC.DataGridLength oldWidth)
        {
            EGC.DataGridLength width = changedColumn.Width;
            if (DoubleUtil.GreaterThan(width.DesiredValue, oldWidth.DisplayValue))
            {
                double nonRetrievableSpace = TakeAwayWidthFromColumns(changedColumn, width.DesiredValue - oldWidth.DisplayValue, changedColumn != null);
                if (DoubleUtil.GreaterThan(nonRetrievableSpace, 0.0))
                {
                    changedColumn.SetWidthInternal(new EGC.DataGridLength(
                        width.Value,
                        width.UnitType,
                        width.DesiredValue,
                        Math.Max(width.DisplayValue - nonRetrievableSpace, changedColumn.MinWidth)));
                }
            }
            else if (DoubleUtil.LessThan(width.DesiredValue, oldWidth.DisplayValue))
            {
                double newDesiredValue = EGC.DataGridHelper.CoerceToMinMax(width.DesiredValue, changedColumn.MinWidth, changedColumn.MaxWidth);
                GiveAwayWidthToColumns(changedColumn, oldWidth.DisplayValue - newDesiredValue);
            }
        }

        /// <summary>
        ///     Method which distributes a given amount of width among all the columns
        /// </summary>
        private void DistributeSpaceAmongColumns(double availableSpace)
        {
            double sumOfMinWidths = 0.0;
            double sumOfMaxWidths = 0.0;
            double sumOfStarMinWidths = 0.0;
            foreach (EGC.DataGridColumn column in this)
            {
                if (!column.IsVisible)
                {
                    continue;
                }

                sumOfMinWidths += column.MinWidth;
                sumOfMaxWidths += column.MaxWidth;
                if (column.Width.IsStar)
                {
                    sumOfStarMinWidths += column.MinWidth;
                }
            }

            if (DoubleUtil.LessThan(availableSpace, sumOfMinWidths))
            {
                availableSpace = sumOfMinWidths;
            }

            if (DoubleUtil.GreaterThan(availableSpace, sumOfMaxWidths))
            {
                availableSpace = sumOfMaxWidths;
            }

            double nonStarSpaceLeftOver = DistributeSpaceAmongNonStarColumns(availableSpace - sumOfStarMinWidths);

            ComputeStarColumnWidths(sumOfStarMinWidths + nonStarSpaceLeftOver);
        }

        /// <summary>
        ///     Helper method which distributes a given amount of width among all non star columns
        /// </summary>
        private double DistributeSpaceAmongNonStarColumns(double availableSpace)
        {
            double requiredSpace = 0.0;
            foreach (EGC.DataGridColumn column in this)
            {
                EGC.DataGridLength width = column.Width;
                if (!column.IsVisible ||
                    width.IsStar)
                {
                    continue;
                }

                requiredSpace += width.DisplayValue;
            }

            if (DoubleUtil.LessThan(availableSpace, requiredSpace))
            {
                double spaceDeficit = requiredSpace - availableSpace;
                TakeAwayWidthFromNonStarColumns(null, spaceDeficit);
            }

            return Math.Max(availableSpace - requiredSpace, 0.0);
        }

        #endregion

        #region Column Resizing Helper

        /// <summary>
        ///     Method which is called when user resize of column starts
        /// </summary>
        internal void OnColumnResizeStarted()
        {
            _originalWidthsForResize = new Dictionary<EGC.DataGridColumn, EGC.DataGridLength>();
            foreach (EGC.DataGridColumn column in this)
            {
                _originalWidthsForResize[column] = column.Width;
            }
        }

        /// <summary>
        ///     Method which is called when user resize of column ends
        /// </summary>
        internal void OnColumnResizeCompleted(bool cancel)
        {
            if (cancel && _originalWidthsForResize != null)
            {
                foreach (EGC.DataGridColumn column in this)
                {
                    if (_originalWidthsForResize.ContainsKey(column))
                    {
                        column.Width = _originalWidthsForResize[column];
                    }
                }
            }

            _originalWidthsForResize = null;
        }

        /// <summary>
        ///     Method which recomputes the widths of columns on resize of column
        /// </summary>
        internal void RecomputeColumnWidthsOnColumnResize(EGC.DataGridColumn resizingColumn, double horizontalChange, bool retainAuto)
        {
            EGC.DataGridLength resizingColumnWidth = resizingColumn.Width;
            double expectedRezingColumnWidth = resizingColumnWidth.DisplayValue + horizontalChange;

            if (DoubleUtil.LessThan(expectedRezingColumnWidth, resizingColumn.MinWidth))
            {
                horizontalChange = resizingColumn.MinWidth - resizingColumnWidth.DisplayValue;
            }
            else if (DoubleUtil.GreaterThan(expectedRezingColumnWidth, resizingColumn.MaxWidth))
            {
                horizontalChange = resizingColumn.MaxWidth - resizingColumnWidth.DisplayValue;
            }

            int resizingColumnIndex = resizingColumn.DisplayIndex;

            if (DoubleUtil.GreaterThan(horizontalChange, 0.0))
            {
                RecomputeColumnWidthsOnColumnPositiveResize(horizontalChange, resizingColumnIndex, retainAuto);
            }
            else if (DoubleUtil.LessThan(horizontalChange, 0.0))
            {
                RecomputeColumnWidthsOnColumnNegativeResize(-horizontalChange, resizingColumnIndex, retainAuto);
            }
        }

        /// <summary>
        ///     Method which computes widths of columns on positive resize of a column
        /// </summary>
        private void RecomputeColumnWidthsOnColumnPositiveResize(
            double horizontalChange,
            int resizingColumnIndex,
            bool retainAuto)
        {
            double perStarWidth = 0.0;
            if (HasVisibleStarColumnsInternal(out perStarWidth))
            {
                // reuse unused space
                horizontalChange = TakeAwayUnusedSpaceOnColumnPositiveResize(horizontalChange, resizingColumnIndex, retainAuto);

                // reducing star columns to right
                horizontalChange = RecomputeStarColumnWidthsOnColumnPositiveResize(horizontalChange, resizingColumnIndex, perStarWidth, retainAuto);

                // reducing columns to the right which are greater than the min size
                horizontalChange = RecomputeNonStarColumnWidthsOnColumnPositiveResize(horizontalChange, resizingColumnIndex, retainAuto);
            }
            else
            {
                EGC.DataGridColumn column = ColumnFromDisplayIndex(resizingColumnIndex);
                SetResizedColumnWidth(column, horizontalChange, retainAuto);
            }
        }

        /// <summary>
        ///     Method which resizes the widths of star columns on positive resize of a column
        /// </summary>
        private double RecomputeStarColumnWidthsOnColumnPositiveResize(
            double horizontalChange,
            int resizingColumnIndex,
            double perStarWidth,
            bool retainAuto)
        {
            while (DoubleUtil.GreaterThan(horizontalChange, 0.0))
            {
                double minPerStarExcessRatio = Double.PositiveInfinity;
                double rightStarFactors = GetStarFactorsForPositiveResize(resizingColumnIndex + 1, out minPerStarExcessRatio);

                if (DoubleUtil.GreaterThan(rightStarFactors, 0.0))
                {
                    horizontalChange = ReallocateStarValuesForPositiveResize(
                        resizingColumnIndex,
                        horizontalChange,
                        minPerStarExcessRatio,
                        rightStarFactors,
                        perStarWidth,
                        retainAuto);

                    if (DoubleUtil.AreClose(horizontalChange, 0.0))
                    {
                        break;
                    }
                }
                else
                {
                    break;
                }
            }

            return horizontalChange;
        }

        private static bool CanColumnParticipateInResize(EGC.DataGridColumn column)
        {
            return column.IsVisible && column.CanUserResize;
        }

        /// <summary>
        ///     Method which returns the total of star factors of the columns which could be resized on positive resize of a column
        /// </summary>
        private double GetStarFactorsForPositiveResize(int startIndex, out double minPerStarExcessRatio)
        {
            minPerStarExcessRatio = Double.PositiveInfinity;
            double rightStarFactors = 0.0;
            for (int i = startIndex, count = Count; i < count; i++)
            {
                EGC.DataGridColumn column = ColumnFromDisplayIndex(i);
                if (!CanColumnParticipateInResize(column))
                {
                    continue;
                }

                EGC.DataGridLength width = column.Width;
                if (width.IsStar && !DoubleUtil.AreClose(width.Value, 0.0))
                {
                    if (DoubleUtil.GreaterThan(width.DisplayValue, column.MinWidth))
                    {
                        rightStarFactors += width.Value;
                        double excessRatio = (width.DisplayValue - column.MinWidth) / width.Value;
                        if (DoubleUtil.LessThan(excessRatio, minPerStarExcessRatio))
                        {
                            minPerStarExcessRatio = excessRatio;
                        }
                    }
                }
            }

            return rightStarFactors;
        }

        /// <summary>
        ///     Method which reallocated the star factors of star columns on 
        ///     positive resize of a column
        /// </summary>
        private double ReallocateStarValuesForPositiveResize(
            int startIndex,
            double horizontalChange,
            double perStarExcessRatio,
            double totalStarFactors,
            double perStarWidth,
            bool retainAuto)
        {
            double changePerStar = 0.0;
            double horizontalChangeForIteration = 0.0;
            if (DoubleUtil.LessThan(horizontalChange, perStarExcessRatio * totalStarFactors))
            {
                changePerStar = horizontalChange / totalStarFactors;
                horizontalChangeForIteration = horizontalChange;
                horizontalChange = 0.0;
            }
            else
            {
                changePerStar = perStarExcessRatio;
                horizontalChangeForIteration = changePerStar * totalStarFactors;
                horizontalChange -= horizontalChangeForIteration;
            }

            for (int i = startIndex, count = Count; i < count; i++)
            {
                EGC.DataGridColumn column = ColumnFromDisplayIndex(i);
                EGC.DataGridLength width = column.Width;
                if (i == startIndex)
                {
                    SetResizedColumnWidth(column, horizontalChangeForIteration, retainAuto);
                }
                else if (column.Width.IsStar && CanColumnParticipateInResize(column) && DoubleUtil.GreaterThan(width.DisplayValue, column.MinWidth))
                {
                    double columnDesiredWidth = width.DisplayValue - (width.Value * changePerStar);
                    column.UpdateWidthForStarColumn(Math.Max(columnDesiredWidth, column.MinWidth), columnDesiredWidth, columnDesiredWidth / perStarWidth);
                }
            }

            return horizontalChange;
        }

        /// <summary>
        ///     Method which recomputes widths of non star columns on positive resize of a column
        /// </summary>
        private double RecomputeNonStarColumnWidthsOnColumnPositiveResize(
            double horizontalChange,
            int resizingColumnIndex,
            bool retainAuto)
        {
            if (DoubleUtil.GreaterThan(horizontalChange, 0.0))
            {
                double totalExcessWidth = 0.0;
                bool iterationNeeded = true;
                for (int i = Count - 1; iterationNeeded && i > resizingColumnIndex; i--)
                {
                    EGC.DataGridColumn column = ColumnFromDisplayIndex(i);
                    if (!CanColumnParticipateInResize(column))
                    {
                        continue;
                    }

                    EGC.DataGridLength width = column.Width;
                    double minWidth = column.MinWidth;
                    if (!width.IsStar &&
                        DoubleUtil.GreaterThan(width.DisplayValue, minWidth))
                    {
                        double columnExcessWidth = width.DisplayValue - minWidth;
                        if (DoubleUtil.GreaterThanOrClose(totalExcessWidth + columnExcessWidth, horizontalChange))
                        {
                            columnExcessWidth = horizontalChange - totalExcessWidth;
                            iterationNeeded = false;
                        }

                        column.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, width.DisplayValue - columnExcessWidth));
                        totalExcessWidth += columnExcessWidth;
                    }
                }

                if (DoubleUtil.GreaterThan(totalExcessWidth, 0.0))
                {
                    EGC.DataGridColumn column = ColumnFromDisplayIndex(resizingColumnIndex);
                    SetResizedColumnWidth(column, totalExcessWidth, retainAuto);
                    horizontalChange -= totalExcessWidth;
                }
            }

            return horizontalChange;
        }

        /// <summary>
        ///     Method which recomputes the widths of columns on negative resize of a column
        /// </summary>
        private void RecomputeColumnWidthsOnColumnNegativeResize(
            double horizontalChange,
            int resizingColumnIndex,
            bool retainAuto)
        {
            double perStarWidth = 0.0;
            if (HasVisibleStarColumnsInternal(out perStarWidth))
            {
                // increasing columns to the right which are less than the desired size
                horizontalChange = RecomputeNonStarColumnWidthsOnColumnNegativeResize(horizontalChange, resizingColumnIndex, retainAuto);

                // increasing star columns to the right
                horizontalChange = RecomputeStarColumnWidthsOnColumnNegativeResize(horizontalChange, resizingColumnIndex, perStarWidth, retainAuto);

                if (DoubleUtil.GreaterThan(horizontalChange, 0.0))
                {
                    EGC.DataGridColumn resizingColumn = ColumnFromDisplayIndex(resizingColumnIndex);
                    if (!resizingColumn.Width.IsStar)
                    {
                        SetResizedColumnWidth(resizingColumn, -horizontalChange, retainAuto);
                    }
                }
            }
            else
            {
                EGC.DataGridColumn column = ColumnFromDisplayIndex(resizingColumnIndex);
                SetResizedColumnWidth(column, -horizontalChange, retainAuto);
            }
        }

        /// <summary>
        ///     Method which recomputes widths of non star columns on negative resize of a column
        /// </summary>
        private double RecomputeNonStarColumnWidthsOnColumnNegativeResize(
            double horizontalChange,
            int resizingColumnIndex,
            bool retainAuto)
        {
            if (DoubleUtil.GreaterThan(horizontalChange, 0.0))
            {
                double totalLagWidth = 0.0;
                bool iterationNeeded = true;
                for (int i = resizingColumnIndex + 1, count = Count; iterationNeeded && i < count; i++)
                {
                    EGC.DataGridColumn column = ColumnFromDisplayIndex(i);
                    if (!CanColumnParticipateInResize(column))
                    {
                        continue;
                    }

                    EGC.DataGridLength width = column.Width;
                    if (!width.IsStar &&
                        DoubleUtil.LessThan(width.DisplayValue, width.DesiredValue) &&
                        !DoubleUtil.AreClose(width.DisplayValue, column.MaxWidth))
                    {
                        double columnLagWidth = width.DesiredValue - width.DisplayValue;
                        if (DoubleUtil.GreaterThanOrClose(totalLagWidth + columnLagWidth, horizontalChange))
                        {
                            columnLagWidth = horizontalChange - totalLagWidth;
                            iterationNeeded = false;
                        }

                        column.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, width.DisplayValue + columnLagWidth));
                        totalLagWidth += columnLagWidth;
                    }
                }

                if (DoubleUtil.GreaterThan(totalLagWidth, 0.0))
                {
                    EGC.DataGridColumn column = ColumnFromDisplayIndex(resizingColumnIndex);
                    SetResizedColumnWidth(column, -totalLagWidth, retainAuto);
                    horizontalChange -= totalLagWidth;
                }
            }

            return horizontalChange;
        }

        /// <summary>
        ///     Method which recomputes widths on star columns on negative resize of a column
        /// </summary>
        private double RecomputeStarColumnWidthsOnColumnNegativeResize(
            double horizontalChange,
            int resizingColumnIndex,
            double perStarWidth,
            bool retainAuto)
        {
            while (DoubleUtil.GreaterThan(horizontalChange, 0.0))
            {
                double minPerStarLagRatio = Double.PositiveInfinity;
                double rightStarFactors = GetStarFactorsForNegativeResize(resizingColumnIndex + 1, out minPerStarLagRatio);

                if (DoubleUtil.GreaterThan(rightStarFactors, 0.0))
                {
                    horizontalChange = ReallocateStarValuesForNegativeResize(
                        resizingColumnIndex,
                        horizontalChange,
                        minPerStarLagRatio,
                        rightStarFactors,
                        perStarWidth,
                        retainAuto);

                    if (DoubleUtil.AreClose(horizontalChange, 0.0))
                    {
                        break;
                    }
                }
                else
                {
                    break;
                }
            }

            return horizontalChange;
        }

        /// <summary>
        ///     Method which returns the total star factors of columns which resize of negative resize of a column
        /// </summary>
        private double GetStarFactorsForNegativeResize(int startIndex, out double minPerStarLagRatio)
        {
            minPerStarLagRatio = Double.PositiveInfinity;
            double rightStarFactors = 0.0;
            for (int i = startIndex, count = Count; i < count; i++)
            {
                EGC.DataGridColumn column = ColumnFromDisplayIndex(i);
                if (!CanColumnParticipateInResize(column))
                {
                    continue;
                }

                EGC.DataGridLength width = column.Width;
                if (width.IsStar && !DoubleUtil.AreClose(width.Value, 0.0))
                {
                    if (DoubleUtil.LessThan(width.DisplayValue, column.MaxWidth))
                    {
                        rightStarFactors += width.Value;
                        double lagRatio = (column.MaxWidth - width.DisplayValue) / width.Value;
                        if (DoubleUtil.LessThan(lagRatio, minPerStarLagRatio))
                        {
                            minPerStarLagRatio = lagRatio;
                        }
                    }
                }
            }

            return rightStarFactors;
        }

        /// <summary>
        ///     Method which reallocates star factors of columns on negative resize of a column
        /// </summary>
        private double ReallocateStarValuesForNegativeResize(
            int startIndex,
            double horizontalChange,
            double perStarLagRatio,
            double totalStarFactors,
            double perStarWidth,
            bool retainAuto)
        {
            double changePerStar = 0.0;
            double horizontalChangeForIteration = 0.0;
            if (DoubleUtil.LessThan(horizontalChange, perStarLagRatio * totalStarFactors))
            {
                changePerStar = horizontalChange / totalStarFactors;
                horizontalChangeForIteration = horizontalChange;
                horizontalChange = 0.0;
            }
            else
            {
                changePerStar = perStarLagRatio;
                horizontalChangeForIteration = changePerStar * totalStarFactors;
                horizontalChange -= horizontalChangeForIteration;
            }

            for (int i = startIndex, count = Count; i < count; i++)
            {
                EGC.DataGridColumn column = ColumnFromDisplayIndex(i);
                EGC.DataGridLength width = column.Width;
                if (i == startIndex)
                {
                    SetResizedColumnWidth(column, -horizontalChangeForIteration, retainAuto);
                }
                else if (column.Width.IsStar && CanColumnParticipateInResize(column) && DoubleUtil.LessThan(width.DisplayValue, column.MaxWidth))
                {
                    double columnDesiredWidth = width.DisplayValue + (width.Value * changePerStar);
                    column.UpdateWidthForStarColumn(Math.Min(columnDesiredWidth, column.MaxWidth), columnDesiredWidth, columnDesiredWidth / perStarWidth);
                }
            }

            return horizontalChange;
        }

        /// <summary>
        ///     Helper method which sets the width of the column which is currently getting resized
        /// </summary>
        private static void SetResizedColumnWidth(EGC.DataGridColumn column, double widthDelta, bool retainAuto)
        {
            EGC.DataGridLength width = column.Width;
            double columnDisplayWidth = EGC.DataGridHelper.CoerceToMinMax(width.DisplayValue + widthDelta, column.MinWidth, column.MaxWidth);

            if (width.IsStar)
            {
                double starValue = width.DesiredValue / width.Value;
                column.UpdateWidthForStarColumn(columnDisplayWidth, columnDisplayWidth, columnDisplayWidth / starValue);
            }
            else if (!width.IsAbsolute && retainAuto)
            {
                column.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, columnDisplayWidth));
            }
            else
            {
                column.SetWidthInternal(new EGC.DataGridLength(columnDisplayWidth, EGC.DataGridLengthUnitType.Pixel, columnDisplayWidth, columnDisplayWidth));
            }
        }

        #endregion

        #region Width Give Away Methods

        /// <summary>
        ///     Method which tries to give away the given amount of width 
        ///     among all the columns except the ignored column
        /// </summary>
        private double GiveAwayWidthToColumns(EGC.DataGridColumn ignoredColumn, double giveAwayWidth)
        {
            return GiveAwayWidthToColumns(ignoredColumn, giveAwayWidth, false);
        }

        /// <summary>
        ///     Method which tries to give away the given amount of width 
        ///     among all the columns except the ignored column
        /// </summary>
        private double GiveAwayWidthToColumns(EGC.DataGridColumn ignoredColumn, double giveAwayWidth, bool recomputeStars)
        {
            double originalGiveAwayWidth = giveAwayWidth;
            giveAwayWidth = GiveAwayWidthToScrollViewerExcess(giveAwayWidth);
            giveAwayWidth = GiveAwayWidthToNonStarColumns(ignoredColumn, giveAwayWidth);

            if (DoubleUtil.GreaterThan(giveAwayWidth, 0.0) || recomputeStars)
            {
                double sumOfStarDisplayWidths = 0.0;
                double sumOfStarMaxWidths = 0.0;
                bool giveAwayWidthIncluded = false;
                foreach (EGC.DataGridColumn column in this)
                {
                    EGC.DataGridLength width = column.Width;
                    if (width.IsStar && column.IsVisible)
                    {
                        if (column == ignoredColumn)
                        {
                            giveAwayWidthIncluded = true;
                        }

                        sumOfStarDisplayWidths += width.DisplayValue;
                        sumOfStarMaxWidths += column.MaxWidth;
                    }
                }

                double expectedStarSpace = sumOfStarDisplayWidths;
                if (!giveAwayWidthIncluded)
                {
                    expectedStarSpace += giveAwayWidth;
                }
                else if (!DoubleUtil.AreClose(originalGiveAwayWidth, giveAwayWidth))
                {
                    expectedStarSpace -= (originalGiveAwayWidth - giveAwayWidth);
                }

                double usedStarSpace = ComputeStarColumnWidths(Math.Min(expectedStarSpace, sumOfStarMaxWidths));
                giveAwayWidth = Math.Max(usedStarSpace - expectedStarSpace, 0.0);
            }

            return giveAwayWidth;
        }

        /// <summary>
        ///     Method which tries to give away the given amount of width
        ///     among all non star columns except the ignored column
        /// </summary>
        private double GiveAwayWidthToNonStarColumns(EGC.DataGridColumn ignoredColumn, double giveAwayWidth)
        {
            while (DoubleUtil.GreaterThan(giveAwayWidth, 0.0))
            {
                int countOfParticipatingColumns = 0;
                double minLagWidth = FindMinimumLaggingWidthOfNonStarColumns(
                    ignoredColumn,
                    out countOfParticipatingColumns);

                if (countOfParticipatingColumns == 0)
                {
                    break;
                }

                double minTotalLagWidth = minLagWidth * countOfParticipatingColumns;
                if (DoubleUtil.GreaterThanOrClose(minTotalLagWidth, giveAwayWidth))
                {
                    minLagWidth = giveAwayWidth / countOfParticipatingColumns;
                    giveAwayWidth = 0.0;
                }
                else
                {
                    giveAwayWidth -= minTotalLagWidth;
                }

                GiveAwayWidthToEveryNonStarColumn(ignoredColumn, minLagWidth);
            }

            return giveAwayWidth;
        }

        /// <summary>
        ///     Helper method which finds the minimum non-zero difference between displayvalue and desiredvalue
        ///     among all non star columns
        /// </summary>
        private double FindMinimumLaggingWidthOfNonStarColumns(
            EGC.DataGridColumn ignoredColumn,
            out int countOfParticipatingColumns)
        {
            double minLagWidth = Double.PositiveInfinity;
            countOfParticipatingColumns = 0;
            foreach (EGC.DataGridColumn column in this)
            {
                if (ignoredColumn == column ||
                    !column.IsVisible)
                {
                    continue;
                }

                EGC.DataGridLength width = column.Width;
                if (width.IsStar)
                {
                    continue;
                }

                double columnMaxWidth = column.MaxWidth;
                if (DoubleUtil.LessThan(width.DisplayValue, width.DesiredValue) &&
                    !DoubleUtil.AreClose(width.DisplayValue, columnMaxWidth))
                {
                    countOfParticipatingColumns++;
                    double lagWidth = Math.Min(width.DesiredValue, columnMaxWidth) - width.DisplayValue;
                    if (DoubleUtil.LessThan(lagWidth, minLagWidth))
                    {
                        minLagWidth = lagWidth;
                    }
                }
            }

            return minLagWidth;
        }

        /// <summary>
        ///     Helper method which gives away the given amount of width to
        ///     every non star column whose display value is less than its desired value
        /// </summary>
        private void GiveAwayWidthToEveryNonStarColumn(EGC.DataGridColumn ignoredColumn, double perColumnGiveAwayWidth)
        {
            foreach (EGC.DataGridColumn column in this)
            {
                if (ignoredColumn == column ||
                    !column.IsVisible)
                {
                    continue;
                }

                EGC.DataGridLength width = column.Width;
                if (width.IsStar)
                {
                    continue;
                }

                if (DoubleUtil.LessThan(width.DisplayValue, Math.Min(width.DesiredValue, column.MaxWidth)))
                {
                    column.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, width.DisplayValue + perColumnGiveAwayWidth));
                }
            }
        }

        /// <summary>
        ///     Helper method which gives away width to scroll viewer
        ///     if its extent width is greater than viewport width
        /// </summary>
        private double GiveAwayWidthToScrollViewerExcess(double giveAwayWidth)
        {
            double totalSpace = DataGridOwner.GetViewportWidthForColumns();
            double usedSpace = 0.0;
            foreach (EGC.DataGridColumn column in this)
            {
                if (column.IsVisible)
                {
                    usedSpace += column.Width.DisplayValue;
                }
            }

            if (DoubleUtil.GreaterThan(usedSpace, totalSpace))
            {
                double contributingSpace = usedSpace - totalSpace;
                giveAwayWidth -= Math.Min(contributingSpace, giveAwayWidth);
            }

            return giveAwayWidth;
        }

        #endregion

        #region Width Take Away Methods

        /// <summary>
        ///     Method which tries to get the unused column space when another column tries to positive resize
        /// </summary>
        private double TakeAwayUnusedSpaceOnColumnPositiveResize(double horizontalChange, int resizingColumnIndex, bool retainAuto)
        {
            double spaceNeeded = TakeAwayWidthFromUnusedSpace(false, horizontalChange);
            if (DoubleUtil.LessThan(spaceNeeded, horizontalChange))
            {
                EGC.DataGridColumn resizingColumn = ColumnFromDisplayIndex(resizingColumnIndex);
                SetResizedColumnWidth(resizingColumn, horizontalChange - spaceNeeded, retainAuto);
            }

            return spaceNeeded;
        }

        /// <summary>
        ///     Helper method which tries to take away width from unused space
        /// </summary>
        private double TakeAwayWidthFromUnusedSpace(bool spaceAlreadyUtilized, double takeAwayWidth, double totalAvailableWidth)
        {
            double usedSpace = 0.0;
            foreach (EGC.DataGridColumn column in this)
            {
                if (column.IsVisible)
                {
                    usedSpace += column.Width.DisplayValue;
                }
            }

            if (spaceAlreadyUtilized)
            {
                if (DoubleUtil.GreaterThanOrClose(totalAvailableWidth, usedSpace))
                {
                    return 0.0;
                }
                else
                {
                    return Math.Min(usedSpace - totalAvailableWidth, takeAwayWidth);
                }
            }
            else
            {
                double unusedSpace = totalAvailableWidth - usedSpace;
                if (DoubleUtil.GreaterThan(unusedSpace, 0.0))
                {
                    takeAwayWidth = Math.Max(0.0, takeAwayWidth - unusedSpace);
                }

                return takeAwayWidth;
            }
        }

        /// <summary>
        ///     Helper method which tries to take away width from unused space
        /// </summary>
        private double TakeAwayWidthFromUnusedSpace(bool spaceAlreadyUtilized, double takeAwayWidth)
        {
            double totalAvailableWidth = DataGridOwner.GetViewportWidthForColumns();
            if (DoubleUtil.GreaterThan(totalAvailableWidth, 0.0))
            {
                return TakeAwayWidthFromUnusedSpace(spaceAlreadyUtilized, takeAwayWidth, totalAvailableWidth);
            }

            return takeAwayWidth;
        }

        /// <summary>
        ///     Method which tries to take away the given amount of width from columns
        ///     except the ignored column
        /// </summary>
        private double TakeAwayWidthFromColumns(EGC.DataGridColumn ignoredColumn, double takeAwayWidth, bool widthAlreadyUtilized)
        {
            double totalAvailableWidth = DataGridOwner.GetViewportWidthForColumns();
            return TakeAwayWidthFromColumns(ignoredColumn, takeAwayWidth, widthAlreadyUtilized, totalAvailableWidth);
        }

        /// <summary>
        ///     Method which tries to take away the given amount of width from columns
        ///     except the ignored column
        /// </summary>
        private double TakeAwayWidthFromColumns(EGC.DataGridColumn ignoredColumn, double takeAwayWidth, bool widthAlreadyUtilized, double totalAvailableWidth)
        {
            takeAwayWidth = TakeAwayWidthFromUnusedSpace(widthAlreadyUtilized, takeAwayWidth, totalAvailableWidth);

            takeAwayWidth = TakeAwayWidthFromStarColumns(ignoredColumn, takeAwayWidth);

            takeAwayWidth = TakeAwayWidthFromNonStarColumns(ignoredColumn, takeAwayWidth);
            return takeAwayWidth;
        }

        /// <summary>
        ///     Method which tries to take away the given amount of width form
        ///     the star columns
        /// </summary>
        private double TakeAwayWidthFromStarColumns(EGC.DataGridColumn ignoredColumn, double takeAwayWidth)
        {
            if (DoubleUtil.GreaterThan(takeAwayWidth, 0.0))
            {
                double sumOfStarDisplayWidths = 0.0;
                double sumOfStarMinWidths = 0.0;
                foreach (EGC.DataGridColumn column in this)
                {
                    EGC.DataGridLength width = column.Width;
                    if (width.IsStar && column.IsVisible)
                    {
                        if (column == ignoredColumn)
                        {
                            sumOfStarDisplayWidths += takeAwayWidth;
                        }

                        sumOfStarDisplayWidths += width.DisplayValue;
                        sumOfStarMinWidths += column.MinWidth;
                    }
                }

                double expectedStarSpace = sumOfStarDisplayWidths - takeAwayWidth;
                double usedStarSpace = ComputeStarColumnWidths(Math.Max(expectedStarSpace, sumOfStarMinWidths));
                takeAwayWidth = Math.Max(usedStarSpace - expectedStarSpace, 0.0);
            }

            return takeAwayWidth;
        }

        /// <summary>
        ///     Method which tries to take away the given amount of width
        ///     among all non star columns except the ignored column
        /// </summary>
        private double TakeAwayWidthFromNonStarColumns(EGC.DataGridColumn ignoredColumn, double takeAwayWidth)
        {
            while (DoubleUtil.GreaterThan(takeAwayWidth, 0.0))
            {
                int countOfParticipatingColumns = 0;
                double minExcessWidth = FindMinimumExcessWidthOfNonStarColumns(
                    ignoredColumn,
                    out countOfParticipatingColumns);

                if (countOfParticipatingColumns == 0)
                {
                    break;
                }

                double minTotalExcessWidth = minExcessWidth * countOfParticipatingColumns;
                if (DoubleUtil.GreaterThanOrClose(minTotalExcessWidth, takeAwayWidth))
                {
                    minExcessWidth = takeAwayWidth / countOfParticipatingColumns;
                    takeAwayWidth = 0.0;
                }
                else
                {
                    takeAwayWidth -= minTotalExcessWidth;
                }

                TakeAwayWidthFromEveryNonStarColumn(ignoredColumn, minExcessWidth);
            }

            return takeAwayWidth;
        }

        /// <summary>
        ///     Helper method which finds the minimum non-zero difference between displayvalue and minwidth
        ///     among all non star columns
        /// </summary>
        private double FindMinimumExcessWidthOfNonStarColumns(
            EGC.DataGridColumn ignoredColumn,
            out int countOfParticipatingColumns)
        {
            double minExcessWidth = Double.PositiveInfinity;
            countOfParticipatingColumns = 0;
            foreach (EGC.DataGridColumn column in this)
            {
                if (ignoredColumn == column ||
                    !column.IsVisible)
                {
                    continue;
                }

                EGC.DataGridLength width = column.Width;
                if (width.IsStar)
                {
                    continue;
                }

                double minWidth = column.MinWidth;
                if (DoubleUtil.GreaterThan(width.DisplayValue, minWidth))
                {
                    countOfParticipatingColumns++;
                    double excessWidth = width.DisplayValue - minWidth;
                    if (DoubleUtil.LessThan(excessWidth, minExcessWidth))
                    {
                        minExcessWidth = excessWidth;
                    }
                }
            }

            return minExcessWidth;
        }

        /// <summary>
        ///     Helper method which takes away the given amount of width from
        ///     every non star column whose display value is greater than its minwidth
        /// </summary>
        private void TakeAwayWidthFromEveryNonStarColumn(
            EGC.DataGridColumn ignoredColumn,
            double perColumnTakeAwayWidth)
        {
            foreach (EGC.DataGridColumn column in this)
            {
                if (ignoredColumn == column ||
                    !column.IsVisible)
                {
                    continue;
                }

                EGC.DataGridLength width = column.Width;
                if (width.IsStar)
                {
                    continue;
                }

                if (DoubleUtil.GreaterThan(width.DisplayValue, column.MinWidth))
                {
                    column.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, width.DisplayValue - perColumnTakeAwayWidth));
                }
            }
        }

        #endregion

        #region Column Virtualization

        /// <summary>
        ///     Property which indicates that the RealizedColumnsBlockList
        ///     is dirty and needs to be rebuilt for non-column virtualized rows
        /// </summary>
        internal bool RebuildRealizedColumnsBlockListForNonVirtualizedRows 
        { 
            get; set; 
        }

        /// <summary>
        ///     List of realized column index blocks for non-column virtualized rows
        /// </summary>
        internal List<RealizedColumnsBlock> RealizedColumnsBlockListForNonVirtualizedRows
        {
            get
            {
                return _realizedColumnsBlockListForNonVirtualizedRows;
            }

            set
            {
                _realizedColumnsBlockListForNonVirtualizedRows = value;

                // Notify other rows and column header row to
                // remeasure their child panel's in order to be
                // in sync with latest column realization computations
                EGC.DataGrid dataGrid = DataGridOwner;
                dataGrid.NotifyPropertyChanged(
                    dataGrid,
                    "RealizedColumnsBlockListForNonVirtualizedRows",
                    new DependencyPropertyChangedEventArgs(),
                    NotificationTarget.CellsPresenter | NotificationTarget.ColumnHeadersPresenter);
            }
        }

        /// <summary>
        ///     List of realized column display index blocks for non-column virtualized rows
        /// </summary>
        internal List<RealizedColumnsBlock> RealizedColumnsDisplayIndexBlockListForNonVirtualizedRows 
        { 
            get; set; 
        }

        /// <summary>
        ///     Property which indicates that the RealizedColumnsBlockList
        ///     is dirty and needs to be rebuilt for column virtualized rows
        /// </summary>
        internal bool RebuildRealizedColumnsBlockListForVirtualizedRows 
        { 
            get; set; 
        }

        /// <summary>
        ///     List of realized column index blocks for column virtualized rows
        /// </summary>
        internal List<RealizedColumnsBlock> RealizedColumnsBlockListForVirtualizedRows
        {
            get
            {
                return _realizedColumnsBlockListForVirtualizedRows;
            }

            set
            {
                _realizedColumnsBlockListForVirtualizedRows = value;
                
                // Notify other rows and column header row to
                // remeasure their child panel's in order to be
                // in sync with latest column realization computations
                EGC.DataGrid dataGrid = DataGridOwner;
                dataGrid.NotifyPropertyChanged(
                    dataGrid,
                    "RealizedColumnsBlockListForVirtualizedRows", 
                    new DependencyPropertyChangedEventArgs(),
                    NotificationTarget.CellsPresenter | NotificationTarget.ColumnHeadersPresenter);
            }
        }

        /// <summary>
        ///     List of realized column display index blocks for column virtualized rows
        /// </summary>
        internal List<RealizedColumnsBlock> RealizedColumnsDisplayIndexBlockListForVirtualizedRows 
        { 
            get; set; 
        }

        /// <summary>
        ///     Called when properties which affect the realized columns namely
        ///     Column Width, FrozenColumnCount, DisplayIndex etc. are changed.
        /// </summary>
        internal void InvalidateColumnRealization(bool invalidateForNonVirtualizedRows)
        {
            RebuildRealizedColumnsBlockListForVirtualizedRows = true;
            if (invalidateForNonVirtualizedRows)
            {
                RebuildRealizedColumnsBlockListForNonVirtualizedRows = true;
            }
        }

        #endregion

        #region Hidden Columns

        /// <summary>
        ///     Helper property to return the display index of first visible column
        /// </summary>
        internal int FirstVisibleDisplayIndex
        {
            get
            {
                for (int i = 0, count = this.Count; i < count; i++)
                {
                    EGC.DataGridColumn column = ColumnFromDisplayIndex(i);
                    if (column.IsVisible)
                    {
                        return i;
                    }
                }

                return -1;
            }
        }

        /// <summary>
        ///     Helper property to return the display index of last visible column
        /// </summary>
        internal int LastVisibleDisplayIndex
        {
            get
            {
                for (int i = this.Count - 1; i >= 0; i--)
                {
                    EGC.DataGridColumn column = ColumnFromDisplayIndex(i);
                    if (column.IsVisible)
                    {
                        return i;
                    }
                }

                return -1;
            }
        }

        #endregion

        #region Data

        private EGC.DataGrid _dataGridOwner;
        private bool _isUpdatingDisplayIndex;     // true if we're in the middle of updating the display index of each column.
        private List<int> _displayIndexMap;            // maps a DisplayIndex to an index in the _columns collection.
        private bool _displayIndexMapInitialized; // Flag is used to delay the validation of DisplayIndex until the first measure
        private bool _isClearingDisplayIndex; // Flag indicating that we're currently clearing the display index.  We should not coerce default display index's during this time. 
        private bool _columnWidthsComputationPending; // Flag indicating whether the columns width computaion operation is pending
        private Dictionary<EGC.DataGridColumn, EGC.DataGridLength> _originalWidthsForResize; // Dictionary to hold the original widths of columns for resize operation
        private double? _averageColumnWidth = null;       // average width of all visible columns
        private List<RealizedColumnsBlock> _realizedColumnsBlockListForNonVirtualizedRows = null; // Realized columns for non-virtualized rows
        private List<RealizedColumnsBlock> _realizedColumnsBlockListForVirtualizedRows = null; // Realized columns for virtualized rows

        #endregion
    }
}