using System;
using System.Windows;
using System.Windows.Media;
using System.Reflection;
using System.Collections.Generic;
using System.Globalization;
using C1.Silverlight.FlexGrid;

namespace C1.Silverlight.FlexGridExcel
{
    /// <summary>
    /// Undoable editing action for bound grids.
    /// </summary>
    public class EditAction : IUndoableAction
    {
        //-------------------------------------------------------------------------
        #region ** fields

        C1FlexGrid _flex;
        object _item, _oldValue, _newValue;
        PropertyInfo _prop;

        #endregion

        //-------------------------------------------------------------------------
        #region ** ctor

        /// <summary>
        /// Initializes a new instance of an <see cref="EditAction"/>.
        /// </summary>
        /// <param name="sender"><see cref="C1FlexGrid"/> that owns the action.</param>
        /// <param name="e"><see cref="CellRangeEventArgs"/> that contains action parameters.</param>
        /// <param name="oldValue">Object that contains the cell value before the action.</param>
        public EditAction(object sender, CellEditEventArgs e, object oldValue)
        {
            // get property being edited
            _flex = sender as C1FlexGrid;
            var col = _flex.Columns[e.Column];
            var propName = col.BoundPropertyName;
            if (propName != null)
            {
                // save item and property being edited
                _item = _flex.Rows[e.Row].DataItem;
                _prop = _item.GetType().GetProperty(propName);
                if (_prop != null)
                {
                    // save the original value
                    _oldValue = oldValue;

                    // save the new value
                    _newValue = _prop.GetValue(_item, null);
                }
            }
        }

        #endregion

        //-------------------------------------------------------------------------
        #region ** IUndoableAction

        /// <summary>
        /// Undoes the lat edit action.
        /// </summary>
        public void Undo()
        {
            _prop.SetValue(_item, _oldValue, null);
            RestoreSelection();
        }
        /// <summary>
        /// Repeats the last edit action.
        /// </summary>
        public void Redo()
        {
            _prop.SetValue(_item, _newValue, null);
            RestoreSelection();
        }

        #endregion

        //-------------------------------------------------------------------------
        #region ** implementation

        void RestoreSelection()
        {
            foreach (var row in _flex.Rows)
            {
                if (row.DataItem == _item)
                {
                    foreach (var col in _flex.Columns)
                    {
                        if (_prop.Name == col.BoundPropertyName)
                        {
                            _flex.Select(row.Index, col.Index);
                            break;
                        }
                    }
                    break;
                }
            }
        }
        static object GetValueFromBindings(FrameworkElement e)
        {
            foreach (FrameworkElement child in GetChildrenOfType<FrameworkElement>(e))
            {
                foreach (var fi in child.GetType().GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy))
                {
                    var dp = fi.GetValue(null) as DependencyProperty;
                    if (dp != null && child.GetBindingExpression(dp) != null)
                    {
                        return child.GetValue(dp);
                    }
                }
            }
            return null;
        }
        static IEnumerable<T> GetChildrenOfType<T>(DependencyObject e) where T : DependencyObject
        {
            if (e != null)
            {
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(e); i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(e, i);
                    if (child != null && child is T)
                    {
                        yield return (T)child;
                    }
                    foreach (T childOfChild in GetChildrenOfType<T>(child))
                    {
                        yield return childOfChild;
                    }
                }
            }
        }
        #endregion
    }
}