using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text.RegularExpressions;
using System.Web.UI;
using System.Web.UI.Design;
using System.Web.UI.WebControls;

namespace ynhtm.WebControls
{
    using Common;

    /// <summary>
    /// 分页方式。
    /// </summary>
    public enum PageMode
    {
        Post,
        Get
    }

    /// <summary>
    /// 分页项。
    /// </summary>
    public class PageItem
    {
        /// <summary>
        /// 初始化方法。
        /// </summary>
        /// <param name="index">页号。</param>
        /// <param name="url">分页地址。</param>
        public PageItem(int index, string url)
        {
            this.Index = index;
            this.Url = url;
        }

        /// <summary>
        /// 页号。
        /// </summary>
        public int Index { get; private set; }
        /// <summary>
        /// 分页地址。
        /// </summary>
        public string Url { get; private set; }
    }

    /// <summary>
    /// 分页回调方法。
    /// </summary>
    /// <param name="index">页号</param>
    /// <returns>返回分页地址。</returns>
    public delegate string PageUrlCallback(int index);

    /// <summary>
    /// 分页控件。
    /// </summary>
    [ToolboxData("<{0}:Pager runat=\"server\" PageSize=\"25\"></{0}:Pager>")]
    [Designer(typeof(Pager.Designer))]
    [DefaultEvent("PageIndexChanged")]
    [PersistChildren(false)]
    [ParseChildren(true)]
    public class Pager : WebControl, IPostBackEventHandler
    {
        class Designer : ControlDesigner
        {
            public override string GetDesignTimeHtml()
            {
                var component = (Pager)base.Component;
                var recordCount = component.RecordCount;
                if (recordCount <= 0) component.RecordCount = component.PageSize * 5;
                component.RecordCount = recordCount;
                return base.GetDesignTimeHtml();
            }
        }

        public Pager()
            : base(HtmlTextWriterTag.Ul) { }

        static readonly object eventPageIndexChanging;
        static readonly object eventPageIndexChanged;
        static Pager()
        {
            eventPageIndexChanging = new object();
            eventPageIndexChanged = new object();
        }

        public static List<PageItem> GetPageList(int buttonCount, int pageIndex, int pageCount, string firstUrl, PageUrlCallback callBack)
        {
            var end = buttonCount;
            if (pageIndex > buttonCount / 2)
            {
                var num = buttonCount / 2;
                end = pageIndex + num;
                if (end > pageCount) end = pageCount;
            }
            else if (end > pageCount) end = pageCount;

            var start = end - buttonCount + 1;
            if (start <= 0) start = 1;
            var result = new List<PageItem>();
            for (var index = start; index <= end; index++)
            {
                if (index == 1)
                    result.Add(new PageItem(index, firstUrl));
                else
                    result.Add(new PageItem(index, callBack.Invoke(index)));
            }
            return result;
        }

        #region Events

        public delegate void PageIndexChangingEventHandler(object sender, CancelEventArgs e);

        protected virtual void OnPageIndexChanging(CancelEventArgs e)
        {
            var handler = (PageIndexChangingEventHandler)base.Events[eventPageIndexChanging];
            if (handler != null) handler(this, e);
        }

        [WebCategory("Action")]
        public event PageIndexChangingEventHandler PageIndexChanging
        {
            add { base.Events.AddHandler(eventPageIndexChanging, value); }
            remove { base.Events.RemoveHandler(eventPageIndexChanging, value); }
        }

        protected virtual void OnPageIndexChanged(EventArgs e)
        {
            var handler = (EventHandler)base.Events[eventPageIndexChanged];
            if (handler != null) handler(this, e);
        }

        [WebSysDescription("GridView_OnPageIndexChanged")]
        [WebCategory("Action")]
        public event EventHandler PageIndexChanged
        {
            add { base.Events.AddHandler(eventPageIndexChanged, value); }
            remove { base.Events.RemoveHandler(eventPageIndexChanged, value); }
        }

        #endregion

        #region Propertys

        [WebCategory("Paging")]
        [DefaultValue(PageMode.Post)]
        public PageMode PageMode
        {
            get
            {
                object o = this.ViewState["PageMode"];
                if (o != null) return (PageMode)o;
                return PageMode.Post;
            }
            set { this.ViewState["PageMode"] = value; }
        }

        [WebCategory("Paging")]
        [DefaultValue("page")]
        public string ArgName
        {
            get
            {
                object o = this.ViewState["ArgName"];
                if (o != null) return (string)o;
                return "page";
            }
            set { this.ViewState["ArgName"] = value; }
        }

        [WebSysDescription("PagerSettings_FirstPageText")]
        [WebCategory("Paging")]
        [DefaultValue("")]
        [Themeable(true)]
        public string FirstPageText
        {
            get
            {
                object o = this.ViewState["FirstPageText"];
                if (o != null) return (string)o;
                return "";
            }
            set { this.ViewState["FirstPageText"] = value; }
        }

        [WebSysDescription("PagerSettings_LastPageText")]
        [WebCategory("Paging")]
        [DefaultValue("")]
        [Themeable(true)]
        public string LastPageText
        {
            get
            {
                object o = this.ViewState["LastPageText"];
                if (o != null) return (string)o;
                return "";
            }
            set { this.ViewState["LastPageText"] = value; }
        }

        [WebSysDescription("PagerSettings_NextPageText")]
        [WebCategory("Paging")]
        [DefaultValue("")]
        [Themeable(true)]
        public string NextPageText
        {
            get
            {
                object o = this.ViewState["NextPageText"];
                if (o != null) return (string)o;
                return "";
            }
            set { this.ViewState["NextPageText"] = value; }
        }

        [WebSysDescription("PagerSettings_PreviousPageText")]
        [WebCategory("Paging")]
        [DefaultValue("")]
        [Themeable(true)]
        public string PreviousPageText
        {
            get
            {
                object o = this.ViewState["PreviousPageText"];
                if (o != null) return (string)o;
                return "";
            }
            set { this.ViewState["PreviousPageText"] = value; }
        }

        [WebCategory("Paging")]
        [DefaultValue("")]
        [Themeable(true)]
        public string ButtonFormat
        {
            get
            {
                object o = this.ViewState["ButtonFormat"];
                if (o != null) return (string)o;
                return "";
            }
            set { this.ViewState["ButtonFormat"] = value; }
        }

        [WebCategory("Paging")]
        [DefaultValue("")]
        [Themeable(true)]
        public string StartText
        {
            get
            {
                object o = this.ViewState["StartText"];
                if (o != null) return (string)o;
                return "";
            }
            set { this.ViewState["StartText"] = value; }
        }

        [WebCategory("Paging")]
        [DefaultValue("")]
        [Themeable(true)]
        public string EndText
        {
            get
            {
                object o = this.ViewState["EndText"];
                if (o != null) return (string)o;
                return "";
            }
            set { this.ViewState["EndText"] = value; }
        }

        [WebSysDescription("PagerSettings_PageButtonCount")]
        [WebCategory("Paging")]
        [DefaultValue(10)]
        [Themeable(true)]
        public int PageButtonCount
        {
            get
            {
                object o = this.ViewState["PageButtonCount"];
                if (o != null) return (int)o;
                return 10;
            }
            set
            {
                if (value < 0) throw new ArgumentOutOfRangeException("PageButtonCount");
                this.ViewState["PageButtonCount"] = value;
            }
        }

        [WebCategory("Paging")]
        [DefaultValue(0)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public int PageCount
        {
            get { return (int)Math.Ceiling(this.RecordCount / (double)this.PageSize); }
        }

        [WebSysDescription("GridView_PageIndex")]
        [WebCategory("Paging")]
        [DefaultValue(1)]
        [Themeable(false)]
        public int PageIndex
        {
            get
            {
                if (this.PageMode == PageMode.Get)
                {
                    var pageIndex = 1;
                    int.TryParse(this.Page.Request.QueryString[this.ArgName], out pageIndex);
                    if (pageIndex <= 0) pageIndex = 1;
                    return pageIndex;
                }
                object o = this.ViewState["PageIndex"];
                if (o != null) return (int)o;
                return 1;
            }
            set
            {
                if (value < 1) throw new ArgumentOutOfRangeException("PageIndex");
                if (value != this.PageIndex)
                {
                    CancelEventArgs e = new CancelEventArgs(false);
                    this.OnPageIndexChanging(e);
                    if (!e.Cancel)
                    {
                        this.ViewState["PageIndex"] = value;
                        this.OnPageIndexChanged(EventArgs.Empty);
                    }
                }
            }
        }

        [WebSysDescription("GridView_PageSize")]
        [WebCategory("Paging")]
        [DefaultValue(25)]
        [Themeable(true)]
        public int PageSize
        {
            get
            {
                object o = this.ViewState["PageSize"];
                if (o != null) return (int)o;
                return 10;
            }
            set
            {
                if (value < 1) throw new ArgumentOutOfRangeException("PageSize");
                this.ViewState["PageSize"] = value;
            }
        }

        [WebCategory("Paging")]
        [DefaultValue(0)]
        [Browsable(false)]
        [Themeable(false)]
        public int RecordCount
        {
            get
            {
                object o = this.ViewState["RecordCount"];
                if (o != null) return (int)o;
                return 0;
            }
            set
            {
                if (value < 0) throw new ArgumentOutOfRangeException("RecordCount");
                this.ViewState["RecordCount"] = value;
            }
        }

        Style pageStyle;
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [DefaultValue("")]
        [NotifyParentProperty(true)]
        [WebCategory("Styles")]
        [WebSysDescription("WebControl_PagerStyle")]
        public Style PageStyle
        {
            get
            {
                if (this.pageStyle == null)
                {
                    this.pageStyle = new Style();
                    if (base.IsTrackingViewState)
                        ((IStateManager)this.pageStyle).TrackViewState();
                }
                return this.pageStyle;
            }
        }

        Style pageSelectedStyle;
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [DefaultValue("")]
        [NotifyParentProperty(true)]
        [WebCategory("Styles")]
        [WebSysDescription("WebControl_PagerStyle")]
        public Style PageSelectedStyle
        {
            get
            {
                if (this.pageSelectedStyle == null)
                {
                    this.pageSelectedStyle = new Style();
                    if (base.IsTrackingViewState)
                        ((IStateManager)this.pageSelectedStyle).TrackViewState();
                }
                return this.pageSelectedStyle;
            }
        }

        Style startTextStyle;
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [DefaultValue("")]
        [NotifyParentProperty(true)]
        [WebCategory("Styles")]
        [WebSysDescription("WebControl_PagerStyle")]
        public Style StartTextStyle
        {
            get
            {
                if (this.startTextStyle == null)
                {
                    this.startTextStyle = new Style();
                    if (base.IsTrackingViewState)
                        ((IStateManager)this.startTextStyle).TrackViewState();
                }
                return this.startTextStyle;
            }
        }

        Style endTextStyle;
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [DefaultValue("")]
        [NotifyParentProperty(true)]
        [WebCategory("Styles")]
        [WebSysDescription("WebControl_PagerStyle")]
        public Style EndTextStyle
        {
            get
            {
                if (this.endTextStyle == null)
                {
                    this.endTextStyle = new Style();
                    if (base.IsTrackingViewState)
                        ((IStateManager)this.endTextStyle).TrackViewState();
                }
                return this.endTextStyle;
            }
        }

        #endregion

        #region Methods

        void IPostBackEventHandler.RaisePostBackEvent(string eventArgument)
        {
            this.PageIndex = int.Parse(eventArgument);
        }

        protected override void Render(HtmlTextWriter writer)
        {
            if (this.RecordCount / this.PageSize > 0) base.Render(writer);
        }

        string GetPageUrl(int index)
        {
            if (this.PageMode == PageMode.Get)
            {
                var arg = this.ArgName;
                var url = Regex.Replace(this.Page.Request.RawUrl, @"\?.*?$", "", RegexOptions.Compiled);
                var query = this.Page.ClientQueryString.Trim('&');
                if (!query.IsNullOrWhiteSpace())
                {
                    var flag = false;
                    query = Regex.Replace(query, "(^|&)" + Regex.Escape(arg) + "=.*?(&|$)", delegate(Match match)
                    {
                        flag = true;
                        return match.Result("$1") + arg + "=" + index.ToString() + match.Result("$2");
                    }, RegexOptions.IgnoreCase | RegexOptions.Compiled);
                    if (flag) return url + "?" + query;
                    return url + "?" + query + "&" + arg + "=" + index.ToString();
                }
                else
                    return url + "?" + arg + "=" + index.ToString();
            }
            return this.Page.ClientScript.GetPostBackClientHyperlink(this, index.ToString());
        }

        public override void RenderBeginTag(HtmlTextWriter writer)
        {
            base.RenderBeginTag(writer);
            var s = this.StartText;
            if (!s.IsNullOrWhiteSpace())
            {
                if (this.startTextStyle != null) this.startTextStyle.AddAttributesToRender(writer);
                writer.RenderBeginTag(HtmlTextWriterTag.Li);
                writer.Write(s, this.RecordCount, this.PageIndex, this.PageCount);
                writer.RenderEndTag();
            }
        }

        protected override void RenderContents(HtmlTextWriter writer)
        {
            var clientScript = this.Page.ClientScript;
            var pageIndex = this.PageIndex;
            var pageCount = this.PageCount;
            var pageButtonCount = this.PageButtonCount;

            if (pageIndex > pageCount) pageIndex = pageCount;

            var list = GetPageList(pageButtonCount, pageIndex, pageCount, this.GetPageUrl(1), this.GetPageUrl);

            var buttonFormat = this.ButtonFormat;
            if (buttonFormat.Trim().Length == 0) buttonFormat = "{0}";

            if (pageIndex > 1)
            {
                var firstPageText = this.FirstPageText;
                if (!firstPageText.IsNullOrWhiteSpace())
                {
                    writer.RenderBeginTag(HtmlTextWriterTag.Li);
                    if (this.pageStyle != null) this.pageStyle.AddAttributesToRender(writer);
                    writer.AddAttribute(HtmlTextWriterAttribute.Href, this.GetPageUrl(1));
                    writer.RenderBeginTag(HtmlTextWriterTag.A);
                    writer.Write(firstPageText);
                    writer.RenderEndTag();
                    writer.RenderEndTag();
                }

                var previousPageText = this.PreviousPageText;
                if (!previousPageText.IsNullOrWhiteSpace())
                {
                    int previousPage = pageIndex - 1;
                    writer.RenderBeginTag(HtmlTextWriterTag.Li);
                    if (this.pageStyle != null) this.pageStyle.AddAttributesToRender(writer);
                    writer.AddAttribute(HtmlTextWriterAttribute.Href, this.GetPageUrl(previousPage));
                    writer.RenderBeginTag(HtmlTextWriterTag.A);
                    writer.Write(previousPageText, previousPage);
                    writer.RenderEndTag();
                    writer.RenderEndTag();
                }
            }

            foreach (var i in list)
            {
                if (i.Index != pageIndex)
                {
                    writer.RenderBeginTag(HtmlTextWriterTag.Li);
                    if (this.pageStyle != null) this.pageStyle.AddAttributesToRender(writer);
                    writer.AddAttribute(HtmlTextWriterAttribute.Href, i.Url);
                    writer.RenderBeginTag(HtmlTextWriterTag.A);
                    writer.Write(buttonFormat, i.Index);
                    writer.RenderEndTag();
                    writer.RenderEndTag();
                }
                else
                {
                    writer.RenderBeginTag(HtmlTextWriterTag.Li);
                    if (this.pageSelectedStyle != null)
                        this.pageSelectedStyle.AddAttributesToRender(writer);
                    else if (this.pageStyle != null)
                        this.pageStyle.AddAttributesToRender(writer);
                    writer.RenderBeginTag(HtmlTextWriterTag.Span);
                    writer.Write(buttonFormat, i.Index);
                    writer.RenderEndTag();
                    writer.RenderEndTag();
                }
            }


            if (pageIndex < pageCount)
            {
                var nextPageText = this.NextPageText;
                if (!nextPageText.IsNullOrWhiteSpace())
                {
                    var nextPage = pageIndex + 1;
                    writer.RenderBeginTag(HtmlTextWriterTag.Li);
                    if (this.pageStyle != null) this.pageStyle.AddAttributesToRender(writer);
                    writer.AddAttribute(HtmlTextWriterAttribute.Href, this.GetPageUrl(nextPage));
                    writer.RenderBeginTag(HtmlTextWriterTag.A);
                    writer.Write(nextPageText, nextPage);
                    writer.RenderEndTag();
                    writer.RenderEndTag();
                }

                var lastPageText = this.LastPageText;
                if (!lastPageText.IsNullOrWhiteSpace())
                {
                    writer.RenderBeginTag(HtmlTextWriterTag.Li);
                    if (this.pageStyle != null) this.pageStyle.AddAttributesToRender(writer);
                    writer.AddAttribute(HtmlTextWriterAttribute.Href, this.GetPageUrl(pageCount));
                    writer.RenderBeginTag(HtmlTextWriterTag.A);
                    writer.Write(lastPageText, pageCount);
                    writer.RenderEndTag();
                    writer.RenderEndTag();
                }
            }

            base.RenderContents(writer);
        }

        public override void RenderEndTag(HtmlTextWriter writer)
        {
            var s = this.EndText;
            if (!s.IsNullOrWhiteSpace())
            {
                if (this.endTextStyle != null) this.endTextStyle.AddAttributesToRender(writer);
                writer.RenderBeginTag(HtmlTextWriterTag.Li);
                writer.Write(s, this.RecordCount, this.PageIndex, this.PageCount);
                writer.RenderEndTag();
            }
            base.RenderEndTag(writer);
        }

        #endregion

        #region ViewState

        protected override void LoadViewState(object savedState)
        {
            if (savedState != null)
            {
                var objArray = (object[])savedState;
                if (objArray[0] != null) base.LoadViewState(objArray[0]);
                if (objArray[1] != null) ((IStateManager)this.PageStyle).LoadViewState(objArray[1]);
                if (objArray[2] != null) ((IStateManager)this.PageSelectedStyle).LoadViewState(objArray[2]);
                if (objArray[3] != null) ((IStateManager)this.StartTextStyle).LoadViewState(objArray[3]);
                if (objArray[4] != null) ((IStateManager)this.EndTextStyle).LoadViewState(objArray[4]);
            }
        }

        protected override void TrackViewState()
        {
            base.TrackViewState();
            if (this.pageStyle != null) ((IStateManager)this.pageStyle).TrackViewState();
            if (this.pageSelectedStyle != null) ((IStateManager)this.pageSelectedStyle).TrackViewState();
            if (this.startTextStyle != null) ((IStateManager)this.startTextStyle).TrackViewState();
            if (this.endTextStyle != null) ((IStateManager)this.endTextStyle).TrackViewState();
        }

        protected override object SaveViewState()
        {
            var savedState = new object[5];
            savedState[0] = base.SaveViewState();
            savedState[1] = this.pageStyle != null ? ((IStateManager)this.pageStyle).SaveViewState() : null;
            savedState[2] = this.pageSelectedStyle != null ? ((IStateManager)this.pageSelectedStyle).SaveViewState() : null;
            savedState[3] = this.startTextStyle != null ? ((IStateManager)this.startTextStyle).SaveViewState() : null;
            savedState[4] = this.endTextStyle != null ? ((IStateManager)this.endTextStyle).SaveViewState() : null;
            return savedState;
        }

        #endregion
    }
}