#region License

/*
 * Copyright © 2002-2005 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#endregion

#region Imports

using System;
using System.Collections.Specialized;
using System.IO;
using System.Threading;
using System.Web.UI;
using System.Web.UI.WebControls;
using Spring.Util;

#endregion

namespace Spring.Web.UI.Controls
{
    /// <summary>
    /// Displays a pop-up DHTML calendar.
    /// </summary>
    /// <remarks>
    /// <p>
	/// Credit: this control uses a slightly modified version of the 
	/// <a href="http://www.dynarch.com/projects/calendar/">Dynarch.com DHTML Calendar</a>,
	/// written by Mihai Bazon.
    /// </p>
    /// </remarks>
    /// <author>Aleksandar Seovic</author>
    [ValidationProperty("SelectedDate")]
    public class Calendar : WebControl, IPostBackDataHandler
    {
    	private const string AllowEditingViewStateKey = "AllowEditing";
    	private const string DateFormatViewStateKey = "Format";
    	private const string SelectedDateViewStateKey = "SelectedDate";
    	private static readonly object EventDateChanged = new object();

        private string skin;

        /// <summary>
        /// Registers necessary scripts and stylesheet.
        /// </summary>
        /// <param name="e">
        /// An <see cref="System.EventArgs"/> object that contains the event data.
        /// </param>
        protected override void OnPreRender(EventArgs e)
        {
            if (skin != null)
            {
                Page.RegisterStyleFile("CalendarStyle", WebUtils.CreateAbsolutePath(Page.ScriptsRoot, "Calendar/calendar-" + skin + ".css"));
            }
            Page.RegisterHeadScriptFile("Calendar", WebUtils.CreateAbsolutePath(Page.ScriptsRoot,"Calendar/calendar.js"));
            Page.RegisterHeadScriptFile("CalendarLanguage", WebUtils.CreateAbsolutePath(Page.ScriptsRoot, "Calendar/lang/calendar-" + Page.UserCulture.Name + ".js"));
            Page.RegisterHeadScriptFile("CalendarSetup", WebUtils.CreateAbsolutePath(Page.ScriptsRoot,"Calendar/calendar-setup.js"));
        }

        /// <summary>
        /// Gets a reference to the <see cref="Spring.Web.UI.Page"/> instance that contains the
        /// server control.
        /// </summary>
        /// <value>
		/// A reference to the <see cref="Spring.Web.UI.Page"/> instance that contains the
		/// server control.
        /// </value>
        private new Page Page
        {
            get { return base.Page as Page; }
        }

        /// <summary>
        /// The selected date.
        /// </summary>
        /// <value>The selected date.</value>
        public DateTime SelectedDate
        {
            get
            {
                if (this.ViewState[SelectedDateViewStateKey] == null)
                {
                    return DateTime.Now;
                }
                return (DateTime) this.ViewState[SelectedDateViewStateKey];
            }
            set { this.ViewState[SelectedDateViewStateKey] = value; }
        }

        /// <summary>
        /// The date format that is to be used.
        /// </summary>
        /// <value>A valid date format string.</value>
        public string Format
        {
            get
            {
                if (this.ViewState[DateFormatViewStateKey] == null)
                {
                    return Thread.CurrentThread.CurrentCulture.DateTimeFormat.ShortDatePattern;
                }
                return (string) this.ViewState[DateFormatViewStateKey];
            }
            set { this.ViewState[DateFormatViewStateKey] = value; }
        }

        /// <summary>
        /// Is direct editing of the date allowed?
        /// </summary>
        /// <value>
        /// <see lang="true"/> if direct editing of the date is allowed?</value>
        public bool AllowEditing
        {
        	get
        	{
        		if (this.ViewState[AllowEditingViewStateKey] == null)
        		{
        			return true;
        		}
        		return (bool) this.ViewState[AllowEditingViewStateKey];
        	}
        	set { this.ViewState[AllowEditingViewStateKey] = value; }
        }

    	/// <summary>
    	/// The (CSS) style.
    	/// </summary>
    	/// <value>The style for the calendar.</value>
    	public string Skin
    	{
    		get { return skin; }
    		set { skin = value; }
    	}

    	/// <summary>
    	/// Renders a hidden input field that stores the value for the radio button group.
    	/// </summary>
    	/// <param name="writer">
    	/// <see cref="System.Web.UI.HtmlTextWriter"/> to use for rendering.
    	/// </param>
    	protected override void Render(HtmlTextWriter writer)
    	{
           writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0");
           writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "0");

           writer.RenderBeginTag(HtmlTextWriterTag.Table);
           writer.RenderBeginTag(HtmlTextWriterTag.Tr);                   
           writer.RenderBeginTag(HtmlTextWriterTag.Td);
           
    	   RenderTextBox(writer);
           
    	   writer.RenderEndTag();            
           writer.RenderBeginTag(HtmlTextWriterTag.Td);
           
    	   RenderButton(writer);
           
    	   writer.RenderEndTag();            
           writer.RenderEndTag();
           writer.RenderEndTag();

           RenderSetupScript(writer);
    	}

    	private void RenderTextBox(HtmlTextWriter writer)
    	{
    	    writer.AddAttribute(HtmlTextWriterAttribute.Id, ClientID);    		
    	    writer.AddAttribute(HtmlTextWriterAttribute.Type, "text");    	                
    	    writer.AddAttribute(HtmlTextWriterAttribute.Name, UniqueID);    	    
    	    writer.AddAttribute(HtmlTextWriterAttribute.Value, (SelectedDate != DateTime.MinValue ? SelectedDate.ToString(Format) : ""));
    		    		
    	    if (!AllowEditing)
    		{
                writer.AddAttribute(HtmlTextWriterAttribute.ReadOnly, "readonly");    			
    		}
    		base.AddAttributesToRender(writer);
            
            writer.RenderBeginTag(HtmlTextWriterTag.Input);    	    
    		writer.RenderEndTag();
    	}

    	private void RenderButton(HtmlTextWriter writer)
    	{
    	    writer.AddAttribute(HtmlTextWriterAttribute.Id, ClientID + "_button");
    	    writer.AddAttribute(HtmlTextWriterAttribute.Src, WebUtils.CreateAbsolutePath(Page.ScriptsRoot, "Calendar/img.gif"));
            writer.AddAttribute(HtmlTextWriterAttribute.Border, "0");

    	    writer.RenderBeginTag(HtmlTextWriterTag.Img);    	        		
    	    writer.RenderEndTag();
    	}

    	private void RenderSetupScript(HtmlTextWriter writer)
    	{    	    
    		writer.AddAttribute("language", "javascript");    	
    	    
            writer.RenderBeginTag(HtmlTextWriterTag.Script);
    		writer.WriteLine("Calendar.setup({");
    		writer.WriteLine("    inputField : \"" + ClientID + "\",");
    		writer.WriteLine("    button     : \"" + ClientID + "_button\"");
    		writer.WriteLine("});");
    		writer.RenderEndTag();
    	}

    	#region IPostBackDataHandler Members

    	/// <summary>
    	/// Raises the <c>SelectionChanged</c> event.
    	/// </summary>
    	public void RaisePostDataChangedEvent()
    	{
    		OnDateChanged(EventArgs.Empty);
    	}

    	/// <summary>
    	/// Loads postback data into the control.
    	/// </summary>
    	/// <param name="postDataKey">
    	/// The key that should be used to retrieve data.
    	/// </param>
    	/// <param name="postCollection">The postback data collection.</param>
    	/// <returns><see lang="true"/> if data has changed.</returns>
    	public bool LoadPostData(string postDataKey, NameValueCollection postCollection)
    	{
    		DateTime dateValue;
    		string dateString = postCollection[postDataKey];
    		if (StringUtils.HasText(dateString))
    		{
    			try
    			{
    				dateValue = DateTime.Parse(dateString);
    			}
    			catch (FormatException)
    			{
    				dateValue = DateTime.MinValue;
    			}
    		}
    		else
    		{
    			dateValue = DateTime.MinValue;
    		}
    		bool changed = dateValue != this.SelectedDate;
    		if (changed)
    		{
    			this.ViewState[SelectedDateViewStateKey] = dateValue;
    		}
    		return changed;
    	}

    	#endregion

    	/// <summary>
    	/// The method that is called on postback if the date has changed.
    	/// </summary>
    	/// <param name="e">
    	/// The event argument (empty and unused).
    	/// </param>
    	protected virtual void OnDateChanged(EventArgs e)
    	{
    		EventHandler handler = (EventHandler) base.Events[EventDateChanged];
    		if (handler != null)
    		{
    			handler(this, e);
    		}
    	}

    	/// <summary>
    	/// Occurs when the value of the radio button group changes between postbacks to the server.
    	/// </summary>
    	public event EventHandler DateChanged
    	{
    		add { base.Events.AddHandler(EventDateChanged, value); }
    		remove { base.Events.RemoveHandler(EventDateChanged, value); }
    	}
    }
}