/*
 * This file is part of Nuts Framework.
 * Copyright(C) 2009-2012 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 nuts.core.lang.Strings;
import nuts.core.log.Log;
import nuts.core.log.Logs;
import nuts.exts.xwork2.converter.converters.NumberTypeConverter;
import nuts.exts.xwork2.util.ContextUtils;

import java.io.IOException;
import java.io.Writer;
import java.text.NumberFormat;

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 Number 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>number-format-*</td>
 *      <td>#</td>
 *   </tr>
 * </table>
 *
 * <p/>
 *
 * <!-- END SNIPPET: javadoc -->
 *
 * <p/> <b>Examples</b>
 * <pre>
 *  <!-- START SNIPPET: example -->
 *  &lt;r:number name="person.age" format="decimal" /&gt;
 *  <!-- END SNIPPET: example -->
 * </pre>
 *
 * <code>Number</code>
 *
 */
@StrutsTag(
		name="number", 
		tldBodyContent="empty", 
		tldTagClass="nuts.exts.struts2.views.jsp.NumberTag", 
		description="Render a formatted number.")
public class CNumber extends ContextBean {

	/**
	 * log
	 */
	private static final Log log = Logs.getLog(CNumber.class);

	private String name;

	private Object value;

	private String format;
	
	private String pattern;

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

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

		if (Strings.isNotEmpty(format)) {
			pattern = tp.getText(NumberTypeConverter.NUMBER_FORMAT_PREFIX + format, (String)null);
			if (pattern == null) {
				pattern = format;
			}
		}
		else {
			pattern = tp.getText(NumberTypeConverter.NUMBER_FORMAT_DEFAULT, (String)null);
		}

		return pattern;
	}
	
	/**
	 * @see org.apache.struts2.components.Component#end(java.io.Writer, java.lang.String)
	 */
	public boolean end(Writer writer, String body) {
		Number n = null;

		if (value != null) {
			if (value instanceof Number) {
				n = (Number)value;
			}
			else {
				getStack().push(value);
				try {
					n = (Number)findValue("top", Number.class);
				}
				catch (Exception e) {
					log.warn("Could not convert value '" + name
						+ "' to a Number instance");
				}
				getStack().pop();
			}
		}
		else if (name != null) {
			// find the name on the valueStack, and cast it to a number
			try {
				n = (Number)findValue(name, Number.class);
			}
			catch (Exception e) {
				log.warn("Could not convert object with key '" + name
						+ "' to a Number instance");
			}
		}

		if (n != null) {
			if (pattern == null) {
				pattern = getNumberPattern(getStack(), format);
			}

			String msg = null;
			if (pattern == null) {
				msg = n.toString();
			}
			else {
				NumberFormat df = NumberTypeConverter.getNumberFormat(pattern, ContextUtils.getLocale());
				msg = df.format(n);
			}

			if (msg != null) {
				try {
					if (getVar() == null) {
						writer.write(msg);
					}
					else {
						putInContext(msg);
					}
				}
				catch (IOException e) {
					log.warn("Could not write out Number tag", e);
				}
			}
		}
		return super.end(writer, "");
	}
	
	/**
	 * @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 number format to use", rtexprvalue=false)
	public void setFormat(String format) {
		this.format = format;
	}

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

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

}
