/*
 * This file is part of Nuts Framework.
 * Copyright (C) 2009 Nuts Develop Team.
 *
 * Nuts Framework is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License any later version.
 * 
 * Nuts Framework is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Nuts Framework. If not, see <http://www.gnu.org/licenses/>.
 */
package nuts.exts.struts2.components;

import java.io.IOException;
import java.io.Writer;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;

import nuts.core.lang.StringUtils;
import nuts.exts.xwork2.converter.converters.DateTypeConverter;
import nuts.exts.xwork2.util.ContextUtils;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.components.ContextBean;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;

import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.util.ValueStack;


/**
 * <!-- START SNIPPET: javadoc -->
 *
 * Format Date object in different ways.
 *
 * <p/>
 *
 * Configurable attributes are :-
 * <ul>
 *    <li>name</li>
 *    <li>value</li>
 *    <li>format</li>
 * </ul>
 *
 * <p/>
 *
 * <table border="1">
 *   <tr>
 *      <td>date-format-date</td>
 *      <td>yyyy/MM/dd</td>
 *   </tr>
 *   <tr>
 *      <td>date-format-time</td>
 *      <td>HH:mm:ss</td>
 *   </tr>
 *   <tr>
 *      <td>date-format-datetime</td>
 *      <td>yyyy/MM/dd HH:mm:ss</td>
 *   </tr>
 *   <tr>
 *      <td>date-format</td>
 *      <td>yyyy/MM/dd HH:mm:ss.SSS</td>
 *   </tr>
 *   <tr>
 *      <td>else</td>
 *      <td>String.valueOf(date.getTime()) will be used</td>
 *   </tr>
 * </table>
 *
 * <p/>
 *
 * <!-- END SNIPPET: javadoc -->
 *
 * <p/> <b>Examples</b>
 * <pre>
 *  <!-- START SNIPPET: example -->
 *  &lt;r:date name="person.birthday" format="date" /&gt;
 *  &lt;r:date name="person.birthday" format="time" /&gt;
 *  &lt;r:date name="person.birthday" format="datetime" /&gt;
 *  &lt;r:date name="person.birthday" /&gt;
 *  <!-- END SNIPPET: example -->
 * </pre>
 *
 * <code>Date</code>
 *
 */
@StrutsTag(
		name="date", 
		tldBodyContent="empty", 
		tldTagClass="nuts.exts.struts2.views.jsp.DateTag", 
		description="Render a formatted date.")
public class CDate extends ContextBean {

	private static final Log log = LogFactory.getLog(CDate.class);

	private String name;

	private Object value;

	private String format;
	
	private String pattern;

	/**
	 * Constructor
	 * 
	 * @param stack value stack
	 */
	public CDate(ValueStack stack) {
		super(stack);
	}

	protected Date convertDate(Object dv) throws Exception {
		Date date = null;
		if (dv instanceof Number) {
			date = new Date(((Number)dv).longValue());
		}
		else if (dv instanceof Date) {
			date = (Date)dv;
		}
		else if (dv instanceof Calendar) {
			date = ((Calendar)dv).getTime();
		}
		else if (dv != null) {
			throw new Exception();
		}
		return date;
	}
	
	/**
	 * @see org.apache.struts2.components.Component#end(java.io.Writer, java.lang.String)
	 */
	public boolean end(Writer writer, String body) {
		Date date = null;

		if (value != null) {
			if (value instanceof Number) {
				date = new Date(((Number)value).longValue());
			}
			else if (value instanceof Date) {
				date = (Date)value; 
			}
			else if (value instanceof Calendar) {
				date = ((Calendar)value).getTime(); 
			}
			else {
				getStack().push(value);
				try {
					date = (Date)findValue("top", Date.class);
				}
				catch (Exception e) {
					log.warn("Could not convert value '" + value
						+ "' to a java.util.Date instance");
				}
				getStack().pop();
			}
		}
		else if (name != null) {
			// find the name on the valueStack, and cast it to a date
			try {
				Object dv = findValue(name);
				date = convertDate(dv);
			}
			catch (Exception e) {
				log.warn("Could not convert object with key '" + name
						+ "' to a java.util.Date instance");
			}
		}

		if (date != null) {
			if (pattern == null) {
				pattern = getDatePattern(getStack(), format);
			}

			String msg = null;
			if (pattern == null) {
				msg = String.valueOf(date.getTime());
			}
			else {
				DateFormat df = DateTypeConverter.getDateFormat(pattern, ContextUtils.getLocale());
				msg = df.format(date);
			}

			if (msg != null) {
				try {
					if (getVar() == null) {
						writer.write(msg);
					}
					else {
						putInContext(msg);
					}
				}
				catch (IOException e) {
					log.warn("Could not write out Date tag", e);
				}
			}
		}
		return super.end(writer, "");
	}

	/**
	 * get date pattern from text
	 * @param stack value stack
	 * @param format date format
	 * @return date pattern
	 */
	public static String getDatePattern(ValueStack stack, String format) {
		TextProvider tp = ContextUtils.findTextProviderInStack(stack);
		String pattern = null;

		if (StringUtils.isNotEmpty(format)) {
			pattern = tp.getText(DateTypeConverter.DATE_FORMAT_PREFIX + format, (String)null);
			if (pattern == null) {
				pattern = format;
			}
		}
		else {
			pattern = tp.getText(DateTypeConverter.DATE_FORMAT_DEFAULT, (String)null);
		}

		return pattern;
	}
	
	/**
	 * @param pattern the pattern to set
	 */
	@StrutsTagAttribute(description="The date pattern to use", rtexprvalue=false)
	public void setPattern(String pattern) {
		this.pattern = pattern;
	}

	/**
	 * @param format the format to set
	 */
	@StrutsTagAttribute(description="The date format to use", rtexprvalue=false)
	public void setFormat(String format) {
		this.format = format;
	}

	/**
	 * @param name the name to set
	 */
	@StrutsTagAttribute(description="The date value to format", required=false)
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @param value the value to set
	 */
	@StrutsTagAttribute(description="The date value to format", required=false)
	public void setValue(Object value) {
		this.value = value;
	}

}
