using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls;
using System.Web;
using System.Globalization;
using System.ComponentModel;
using System.Drawing;
using System.Web.UI;

using System.Web.UI.Design;
using Westwind.Tools;

namespace Westwind.Web.Controls
{

    /// <summary>
    /// This control is an ASP.NET wrapper around &lt;&lt;a 
    /// href="http://marcgrabanski.com/code/ui-datepicker/"   
    /// target="top"&gt;&gt;Marc Garbanski's jQuery UI  
    /// Datepicker&lt;&lt;/a&gt;&gt; control.
    /// 
    /// Built against Version 3.4
    /// 
    /// The control provides a popup calendar or inline date selection with 
    /// Postback support added to the client control.
    /// 
    /// &lt;&lt;img src="images/wwDatePicker.png"&gt;&gt;
    /// 
    /// The control relies on the following resources:
    /// 
    /// &lt;&lt;ul&gt;&gt; &lt;&lt;li&gt;&gt; jQuery.js (jQuery 1.2 or later) 
    /// &lt;&lt;li&gt;&gt; ui.datepicker.js &lt;&lt;li&gt;&gt; ui.datepicker.css 
    /// &lt;&lt;/ul&gt;&gt;
    /// 
    /// These resources are embedded into the assembly so you don't need to 
    /// reference or distribute anything. You can however override each of these 
    /// resources with relative URL based resources or leave the scripts blank if 
    /// you like to manually load them.
    /// </summary>
    [ToolboxBitmap(typeof(System.Web.UI.WebControls.Calendar)), DefaultProperty("Text"),
    ToolboxData("<{0}:wwDatePicker runat=\"server\" />")]
    public class wwDatePicker : TextBox
    {
        public wwDatePicker()
        {
            this.Width = Unit.Pixel(80);
        }

        /// <summary>
        /// The currently selected date
        /// </summary>
        [DefaultValue(typeof(DateTime), ""),
        Category("Date Selection")]
        public DateTime? SelectedDate
        {
            get
            {
                DateTime defaultDate = DateTime.Parse("01/01/1900", CultureInfo.InstalledUICulture);

                if (this.Text == "")
                    return defaultDate;

                DateTime.TryParse(this.Text, out defaultDate);
                return defaultDate;
            }
            set
            {
                if (!value.HasValue)
                    this.Text = "";
                else
                {
                    string dateFormat = this.DateFormat;
                    if (dateFormat == "Auto")
                        dateFormat = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
                    this.Text = value.Value.ToString(dateFormat);
                }
            }
        }


        /// <summary>
        /// Determines how the datepicking option is activated
        /// </summary>
        [Description("Determines how the datepicking option is activated")]
        [Category("Date Selection"), DefaultValue(typeof(DatePickerDisplayModes), "ImageButton")]
        public DatePickerDisplayModes DisplayMode
        {
            get { return _DisplayMode; }
            set { _DisplayMode = value; }
        }
        private DatePickerDisplayModes _DisplayMode = DatePickerDisplayModes.ImageButton;



        /// <summary>
        /// Url to a Calendar Image or WebResource to use the default resource image.
        /// Applies only if the DisplayMode = ImageButton
        /// </summary>
        [Description("Url to a Calendar Image or WebResource to use the default resource image")]
        [Category("Resources"), DefaultValue("WebResource")]
        public string ButtonImage
        {
            get { return _ButtonImage; }
            set { _ButtonImage = value; }
        }
        private string _ButtonImage = "WebResource";

        /// <summary>
        /// The CSS that is used for the calendar
        /// </summary>
        [Category("Resources"), Description("The CSS that is used for the calendar or empty. WebResource loads from resources."),
         DefaultValue("WebResource")]
        public string CalendarCss
        {
            get { return _CalendarCss; }
            set { _CalendarCss = value; }
        }
        private string _CalendarCss = "WebResource";


        /// <summary>
        /// Location for the calendar JavaScript
        /// </summary>
        [Description("Location for the calendar JavaScript or empty for none. WebResource loads from resources")]
        [Category("Resources"), DefaultValue("WebResource")]
        public string CalendarJs
        {
            get { return _CalendarJs; }
            set { _CalendarJs = value; }
        }
        private string _CalendarJs = "WebResource";


        /// <summary>
        /// Location of jQuery library. Use WebResource for loading from resources
        /// </summary>
        [Description("Location of jQuery library or empty for none. Use WebResource for loading from resources")]
        [Category("Resources"), DefaultValue("WebResource")]
        public string jQueryJs
        {
            get { return _jQueryJs; }
            set { _jQueryJs = value; }
        }
        private string _jQueryJs = "WebResource";


        /// <summary>
        /// Determines the Date Format used. Auto uses CurrentCulture. Format: MDY/  month, date,year separator
        /// </summary>
        [Description("Determines the Date Format used. Auto uses CurrentCulture. Format: MDY/  month, date,year separator")]
        [Category("Date Selection"), DefaultValue("Auto")]
        public string DateFormat
        {
            get { return _DateFormat; }
            set { _DateFormat = value; }
        }
        private string _DateFormat = "Auto";

        /// <summary>
        /// Minumum allowable date. Leave blank to allow any date
        /// </summary>
        [Description("Minumum allowable date")]
        [Category("Date Selection"), DefaultValue(typeof(DateTime?), null)]
        public DateTime? MinDate
        {
            get { return _MinDate; }
            set { _MinDate = value; }
        }
        private DateTime? _MinDate = null;

        /// <summary>
        /// Maximum allowable date. Leave blank to allow any date.
        /// </summary>
        [Description("Maximum allowable date. Leave blank to allow any date.")]
        [Category("Date Selection"), DefaultValue(typeof(DateTime?), null)]
        public DateTime? MaxDate
        {
            get { return _MaxDate; }
            set { _MaxDate = value; }
        }
        private DateTime? _MaxDate = null;


        /// <summary>
        /// Client event handler fired when a date is selected
        /// </summary>
        [Description("Client event handler fired when a date is selected")]
        [Category("Date Selection"), DefaultValue("")]
        public string OnClientSelect
        {
            get { return _OnClientSelect; }
            set { _OnClientSelect = value; }
        }
        private string _OnClientSelect = "";


        /// <summary>
        /// Determines where the Close icon is displayed. True = top, false = bottom.
        /// </summary>
        [Description("Determines where the Close icon is displayed. True = top, false = bottom.")]
        [Category("Date Selection"), DefaultValue(true)]
        public bool CloseAtTop
        {
            get { return _CloseAtTop; }
            set { _CloseAtTop = value; }
        }
        private bool _CloseAtTop = true;


        /// <summary>
        /// Code that embeds related resources (.js and css)
        /// </summary>
        /// <param name="scriptProxy"></param>
        protected void RegisterResources(ClientScriptProxy scriptProxy)
        {
            // *** Make sure jQuery is loaded
            if (this.jQueryJs == "WebResource")
                ControlResources.LoadjQuery(this.Page);
            else if (!string.IsNullOrEmpty(this.jQueryJs))
                scriptProxy.RegisterClientScriptInclude(this.Page, typeof(ControlResources),
                        ControlResources.JQUERY_SCRIPT_RESOURCE,
                        this.ResolveUrl(this.jQueryJs));

            // *** Load the calandar script
            string script = this.CalendarJs;

            // *** Load jQuery Calendar Scripts
            if (script == "WebResource")
                scriptProxy.RegisterClientScriptResource(this.Page, typeof(ControlResources),
                    ControlResources.JQUERY_CALENDAR_SCRIPT_RESOURCE);
            else if (!string.IsNullOrEmpty(script))
                scriptProxy.RegisterClientScriptInclude(this.Page, typeof(ControlResources),
                                    "__jqueryCalendar",
                                    this.ResolveUrl(script));

            // *** Load the related CSS reference into the page
            script = this.CalendarCss;
            if (script == "WebResource")
                script = scriptProxy.GetWebResourceUrl(this.Page, typeof(ControlResources),
                                             ControlResources.JQUERY_CALENDAR_CSS_RESOURCE);
            else if (!string.IsNullOrEmpty(script))
                script = this.ResolveUrl(this.CalendarCss);

            // *** Register Calendar CSS 'manually'
            string css = @"<link href=""" + script +
                         @""" type=""text/css"" rel=""stylesheet"" />";
            scriptProxy.RegisterClientScriptBlock(this.Page, typeof(ControlResources), "_calcss", css, false);
        }

        /// <summary>
        /// Most of the work happens here for generating the hook up script code
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            // *** MS AJAX aware script management
            ClientScriptProxy scriptProxy = ClientScriptProxy.Current;

            // *** Register resources
            this.RegisterResources(scriptProxy);

            string dateFormat = this.DateFormat;

            if (string.IsNullOrEmpty(dateFormat) || dateFormat == "Auto")
            {
                // *** Try to create a data format string from culture settings
                // *** this code will fail if culture can't be mapped on server hence the empty try/catch
                try
                {
                    dateFormat = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
                }
                catch { }
            }

            dateFormat = dateFormat.ToLower().Replace("yyyy", "yy");

            // *** Capture and map the various option parameters
            StringBuilder sbOptions = new StringBuilder(512);
            sbOptions.Append("{");

            string onSelect = this.OnClientSelect;

            if (this.DisplayMode == DatePickerDisplayModes.Button)
                sbOptions.Append("showOn: 'button',");
            else if (this.DisplayMode == DatePickerDisplayModes.ImageButton)
            {
                string img = this.ButtonImage;
                if (img == "WebResource")
                    img = scriptProxy.GetWebResourceUrl(this, typeof(ControlResources), ControlResources.CALENDAR_ICON_RESOURCE);
                else
                    img = this.ResolveUrl(this.ButtonImage);

                sbOptions.Append("showOn: 'button', buttonImageOnly: true, buttonImage: '" + img + "',buttonText: 'Select date',");
            }
            else if (this.DisplayMode == DatePickerDisplayModes.Inline)
            {
                scriptProxy.RegisterHiddenField(this, this.ClientID, this.Text);
                onSelect = this.ClientID + "OnSelect";
            }

            if (!string.IsNullOrEmpty(onSelect))
                sbOptions.Append("onSelect: " + onSelect + ",");

            if (this.MaxDate.HasValue)
                sbOptions.Append("maxDate: " + wwWebUtils.EncodeJsDate(MaxDate.Value) + ",");

            if (this.MinDate.HasValue)
                sbOptions.Append("minDate: " + wwWebUtils.EncodeJsDate(MinDate.Value) + ",");

            if (!this.CloseAtTop)
                sbOptions.Append("closeAtTop: false,");

            sbOptions.Append("dateFormat: '" + dateFormat + "'}");


            // *** Write out initilization code for calendar
            StringBuilder sbStartupScript = new StringBuilder(400);
            sbStartupScript.AppendLine("jQuery(document).ready( function() {");


            if (this.DisplayMode != DatePickerDisplayModes.Inline)
            {
                scriptProxy.RegisterClientScriptBlock(this.Page, 
                                                      typeof(ControlResources),
                                                      "__attachDatePickerInputKeys", 
                                                      this.AttachDatePickerKeysScript, true);

                sbStartupScript.AppendFormat("var cal = jQuery('#{0}').datepicker({1}).attachDatepickerInputKeys();\r\n", 
                                             this.ClientID, sbOptions);                
            }
            else
            {
                sbStartupScript.AppendLine("var cal = jQuery('#" + this.ClientID + "Div').datepicker(" + sbOptions.ToString() + ")" );

                if (this.SelectedDate.HasValue && this.SelectedDate.Value > new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc))
                {
                    JSONSerializer ser = new JSONSerializer();
                    ser.SerializeDateAsString = false;
                    string date = ser.Serialize(this.SelectedDate);
                    sbStartupScript.AppendLine(    "cal.datepicker('setDate'," + date + ");");
                }
                else
                    sbStartupScript.AppendLine("cal.datepicker('setDate',new Date());");

                // *** Assign value to hidden form var on selection
                scriptProxy.RegisterStartupScript(this, typeof(ControlResources), this.UniqueID + "OnSelect",
                    "function  " + this.ClientID + "OnSelect(dateStr)\r\n" +
                    "{\r\n" +
                    ((!string.IsNullOrEmpty(this.OnClientSelect)) ? this.OnClientSelect + "(dateStr);\r\n" : "") +
                    "jQuery('#" + this.ClientID + "')[0].value = dateStr;\r\n}\r\n", true);
            }

            sbStartupScript.AppendLine("} );");
            scriptProxy.RegisterStartupScript(this.Page, typeof(ControlResources), "_cal" + this.ID,
                 sbStartupScript.ToString(), true);
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="writer"></param>
        public override void RenderControl(HtmlTextWriter writer)
        {
            if (this.DisplayMode != DatePickerDisplayModes.Inline)
                base.RenderControl(writer);
            else
            {
                writer.Write("<div id='" + this.ClientID + "Div'></div>");
            }

            if (HttpContext.Current == null)
            {
                if (this.DisplayMode == DatePickerDisplayModes.Button)
                {
                    writer.Write(" <input type='button' value='...' style='width: 20px; height: 20px;' />");
                }
                else if ((this.DisplayMode == DatePickerDisplayModes.ImageButton))
                {
                    string img;
                    if (this.ButtonImage == "WebResource")
                        img = this.Page.ClientScript.GetWebResourceUrl(this.GetType(), ControlResources.CALENDAR_ICON_RESOURCE);
                    else
                        img = this.ResolveUrl(this.ButtonImage);

                    writer.AddAttribute(HtmlTextWriterAttribute.Src, img);
                    writer.AddAttribute("hspace", "2");
                    writer.RenderBeginTag(HtmlTextWriterTag.Img);
                    writer.RenderEndTag();
                }
            }
        }

        private string AttachDatePickerKeysScript =
@"
jQuery.fn.attachDatepickerInputKeys = function(callback) {                        
    if (this.length < 1) return this;
       
    this.keydown( function(e) {
        var j = jQuery(this);        
        var di = jQuery.datepicker._getInst(this._calId);         
        if (!di)
            return;                 

        di._setDateFromField(this);  // force update first            
        var d = di._getDate();
        if (!d)
           return true;
           
        var month = d.getMonth();
        var year = d.getFullYear();
        var day = d.getDate();
        
        switch(e.keyCode) {
            case 84: // [T]oday
                d = new Date(); break;                                        
            case 109: case 189: 
                d = new Date(year,month,day-1); break;
            case 107: case 187: 
                d = new Date(year,month,day+1); break;                
            case 77: //M
                 d = new Date(year,month-1,day); break;
            case 72: //H
                 d = new Date(year,month+1,day); break;
            default:
                return true;
        }
        j.val(di._formatDate(d));  
        di._setDate(d) ;
        if (callback)
            callback(this);
        return false;
    });    
    return this;  
}
";

    }


    public enum DatePickerDisplayModes
    {
        Button,
        ImageButton,
        AutoPopup,
        Inline
    }
}
