using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Telerik.Web.UI;
using System.Web.UI;
using System.ComponentModel;
using System.Web.Script.Serialization;

namespace Telerik.Web.UI
{
    /// <summary>
    /// Extends RadGrid's Slider pager with a customizable tooltip 
    /// </summary>
    [TargetControlType(typeof(RadGrid))]
    public class GridSliderTooltipExtender : ExtenderControl
    {
        private static readonly object EventToolTipUpdate;

        static GridSliderTooltipExtender()
        {
            GridSliderTooltipExtender.EventToolTipUpdate = new object();
        }

        public GridSliderTooltipExtender()
        {
            _sliders = new List<RadSlider>();
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            if (!String.IsNullOrEmpty(this.TargetControlID))
            {
                _targetGrid = Page.FindControl(this.TargetControlID) as RadGrid;
                if (_targetGrid != null)
                {
                    _targetGrid.ItemCreated += new GridItemEventHandler(TargetGrid_ItemCreated);
                }
            }

            EnsureChildControls();
        }

        private void TargetGrid_ItemCreated(object sender, GridItemEventArgs e)
        {
            if (e.Item is GridPagerItem && e.Item.OwnerTableView.PagerStyle.Mode == GridPagerMode.Slider)
            {
                RadSlider slider = (RadSlider)e.Item.FindControl("GridSliderPager");
                if (slider != null)
                {
                    RegisterSlider(slider);
                }
            }
        }

        protected virtual void RegisterSlider(RadSlider slider)
        {
            foreach (RadSlider sld in _sliders)
            {
                if (sld == slider) return;

                if (sld.ID == slider.ID)
                {
                    _sliders.Remove(sld);

                    break;
                }
            }

            _sliders.Add(slider);
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            foreach (RadSlider slider in this._sliders)
            {
                _tooltipManager.TargetControls.Add(slider.ClientID, true);

                slider.OnClientSlideStart = String.Format(@"function(){{$find(""{0}"")._slide(""{1}"", true);}}", 
                    this.ClientID, 
                    slider.ClientID);

                slider.OnClientValueChanged = String.Format(@"function(){{$find(""{0}"")._slide(""{1}"", false);}}",
                    this.ClientID,
                    slider.ClientID);

                slider.OnClientSlideEnd = String.Format(@"function(){{$find(""{0}"")._slideEnd(""{1}"");}}",
                    this.ClientID,
                    slider.ClientID);
            }
        }

        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            this.Controls.Add(this.ToolTipManager);                        

            _tooltipManager.AjaxUpdate += new ToolTipUpdateEventHandler(ToolTipAjaxUpdate);
        }

        private void ToolTipAjaxUpdate(object sender, ToolTipUpdateEventArgs e)
        {
            if (!e.TargetControlID.Contains(this.TargetControlID))
            {
                return;
            }

            string[] toolTipArgs = e.Value.Split(':');
            int pageIndex = 0;
            int pageSize = 0;
            string value = String.Empty;

            try
            {
                pageIndex = Int32.Parse(toolTipArgs[0]);
                pageSize = Int32.Parse(toolTipArgs[1]);
                value = toolTipArgs[2];
            }
            catch (Exception)
            {                
                throw new ArgumentException("Pager ToolTip arguments are not valid.");
            }

            SliderToolTipUpdateEventArgs args = new SliderToolTipUpdateEventArgs(pageIndex, pageSize, value, e.UpdatePanel);
            OnToolTipUpdate(args);

            if (args.Cancel) return;


            if (this.ToolTipTemplate != null)
            {
                Control templateContainer = null;

                if (args.DataItem != null)
                {
                    templateContainer = new ToolTipTemplateContainer(args.DataItem);
                }
                else
                {
                    templateContainer = new Control();
                }

                this.ToolTipTemplate.InstantiateIn(templateContainer);
                e.UpdatePanel.ContentTemplateContainer.Controls.Add(templateContainer);
                e.UpdatePanel.ContentTemplateContainer.DataBind();
            }
        }

        class ToolTipTemplateContainer : Control, IDataItemContainer
        {
            public ToolTipTemplateContainer(object dataItem)
            {
                this._dataItem = dataItem;
            }

            private object _dataItem;

            #region IDataItemContainer Members

            public object DataItem
            {
                get { return this._dataItem; }
            }

            public int DataItemIndex
            {
                get { return -1; }
            }

            public int DisplayIndex
            {
                get { return -1; }
            }

            #endregion
        }

        protected virtual void OnToolTipUpdate(SliderToolTipUpdateEventArgs e)
        {
            SliderToolTipUpdateEventHandler handler = (SliderToolTipUpdateEventHandler)base.Events[GridSliderTooltipExtender.EventToolTipUpdate];
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Gets or sets the tooltip template rendered in the RadGrid slider.
        /// </summary>
        [Browsable(false), DefaultValue(null), PersistenceMode(PersistenceMode.InnerProperty),
        Description("Gets or sets the tooltip template rendered in the RadGrid slider.")]
        public ITemplate ToolTipTemplate
        {
            get { return this._toolTipTemplate; }
            set { this._toolTipTemplate = value; }
        }

        /// <summary>
        /// Gets the RadToolTipManager instance associated with this extender control.
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        PersistenceMode(PersistenceMode.InnerProperty),
        Bindable(false),
        MergableProperty(false),
        Description("Gets the RadToolTipManager instance associated with this extender control")]
        public virtual RadToolTipManager ToolTipManager
        {
            get
            {
                if (this._tooltipManager == null)
                {
                    this._tooltipManager = new RadToolTipManager();
                    this._tooltipManager.ID = this.ID + "_ToolTipManager";
                    this._tooltipManager.ShowEvent = ToolTipShowEvent.FromCode;
                    this._tooltipManager.ShowDelay = 100;
                    this._tooltipManager.HideEvent = ToolTipHideEvent.FromCode;
                    this._tooltipManager.HideDelay = 0;
                    this._tooltipManager.ShowCallout = false;
                    this._tooltipManager.Position = ToolTipPosition.TopCenter;
                    this._tooltipManager.RelativeTo = ToolTipRelativeDisplay.Element;
                }

                return this._tooltipManager;
            }
        }

        /// <summary>
        /// Gets or sets the client BeforeShow event handler that is fired 
        /// before the slider tooltip is displayed
        /// </summary>
        public string OnClientBeforeShow
        {
            get { return (string)this.ViewState["OnClientBeforeShow"] ?? String.Empty; }
            set { this.ViewState["OnClientBeforeShow"] = value; }
        }

        /// <summary>
        /// Fires when the tooltip is updated using AJAX callback
        /// </summary>
        public event SliderToolTipUpdateEventHandler TooltipUpdate
        {
            add { base.Events.AddHandler(GridSliderTooltipExtender.EventToolTipUpdate, value); }
            remove { base.Events.RemoveHandler(GridSliderTooltipExtender.EventToolTipUpdate, value); }
        }

        private RadGrid _targetGrid;
        private RadToolTipManager _tooltipManager;
        private List<RadSlider> _sliders;
        private ITemplate _toolTipTemplate;

        protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors(Control targetControl)
        {
            ScriptBehaviorDescriptor descriptor = new ScriptBehaviorDescriptor("Telerik.Web.UI.GridSliderTooltipExtender", targetControl.ClientID);

            descriptor.AddProperty("_id", this.ClientID);
            descriptor.AddComponentProperty("toolTipManager", _tooltipManager.ID);

            if (!String.IsNullOrEmpty(this.OnClientBeforeShow))
            {
                descriptor.AddEvent("beforeShow", this.OnClientBeforeShow);
            }

            yield return descriptor;
        }

        protected override IEnumerable<ScriptReference> GetScriptReferences()
        {
            yield return new ScriptReference(this.ResolveClientUrl("GridSliderTooltipExtender.js"));
        }
    }

    public delegate void SliderToolTipUpdateEventHandler(object sender, SliderToolTipUpdateEventArgs args);

    public class SliderToolTipUpdateEventArgs : CancelEventArgs
    {
        internal SliderToolTipUpdateEventArgs(int gridPageIndex, int gridPageSize, string value, UpdatePanel updatePanel)
        {
            this._gridPageIndex = gridPageIndex;
            this._gridPageSize = gridPageSize;
            this._value = value;
            this._updatePanel = updatePanel;
        }

        /// <summary>
        /// Gets the requested page index
        /// </summary>
        public int GridPageIndex
        {
            get { return this._gridPageIndex; }
        }

        /// <summary>
        /// Gets the current grid page size
        /// </summary>
        public int GridPageSize
        {
            get { return this._gridPageSize; }
        }

        /// <summary>
        /// Gets the UpdatePanel that is making the tooltip update request
        /// </summary>
        public UpdatePanel UpdatePanel
        {
            get { return this._updatePanel; }
        }

        /// <summary>
        /// Gets the user-defined value sent from the client
        /// </summary>
        public string Value
        {
            get { return this._value; }
        }

        /// <summary>
        /// Gets or sets the data item that will be bound to the tooltip
        /// template. Set this property to a data item from the requested
        /// grid page
        /// </summary>
        public object DataItem
        {
            get { return this._dataItem; }
            set { this._dataItem = value; }
        }

        private int _gridPageIndex;
        private int _gridPageSize;
        private UpdatePanel _updatePanel;
        private string _value;
        private object _dataItem;
    }
}