/*
 * 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.Collections;
import nuts.core.lang.Strings;

import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

import org.apache.struts2.components.ContextBean;
import org.apache.struts2.components.Param;
import org.apache.struts2.util.TextProviderHelper;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;

import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;

/**
 * <!-- START SNIPPET: javadoc --> Render a I18n text message.
 * <p/>
 * The message must be in a resource bundle with the same name as the action that it is associated
 * with. In practice this means that you should create a properties file in the same package as your
 * Java class with the same name as your class, but with .properties extension.
 * <p/>
 * If the named message is not found in a property file, then the body of the tag will be used as
 * default message. If no body is used, then the stack will be searched, and if a value is returned,
 * it will written to the output. If no value is found on the stack, the key of the message will be
 * written out. <!-- END SNIPPET: javadoc --> <!-- START SNIPPET: params -->
 * <ul>
 * <li>name* (String) - the i18n message key</li>
 * </ul>
 * <!-- END SNIPPET: params -->
 * <p/>
 * Example:
 * 
 * <pre>
 * &lt;!-- START SNIPPET: exdescription --&gt;
 * 
 * Accessing messages from a given bundle (the i18n Shop example bundle in the first example) and using bundle defined through the framework in the second example.
 * 
 * </p>
 * <!-- END SNIPPET: exdescription --> </pre>
 * 
 * <pre>
 * &lt;!-- START SNIPPET: example --&gt;
 * 
 * &lt;!-- First Example --&gt;
 * &lt;s:i18n name=&quot;struts.action.test.i18n.Shop&quot;&gt;
 *     &lt;s:text name=&quot;main.title&quot;/&gt;
 * &lt;/s:i18n&gt;
 * 
 * &lt;!-- Second Example --&gt;
 * &lt;s:text name=&quot;main.title&quot; /&gt;
 * 
 * &lt;!-- Third Examlpe --&gt;
 * &lt;s:text name=&quot;i18n.label.greetings&quot;&gt;
 *    &lt;s:param &gt;Mr Smith&lt;/s:param&gt;
 * &lt;/s:text&gt;
 * 
 * &lt;!-- END SNIPPET: example --&gt;
 * </pre>
 * 
 * <pre>
 * &lt;!-- START SNIPPET: i18nExample --&gt;
 * 
 * &lt;-- Fourth Example --&gt;
 * &lt;s:text name=&quot;some.key&quot; /&gt;
 * 
 * &lt;-- Fifth Example --&gt;
 * &lt;s:text name=&quot;some.invalid.key&quot; &gt;
 *    The Default Message That Will Be Displayed
 * &lt;/s:text&gt;
 * 
 * &lt;!-- END SNIPPET: i18nExample --&gt;
 * </pre>
 * 
 * @see Param
 */
@StrutsTag(
		name = "text", 
		tldTagClass = "nuts.exts.struts2.views.jsp.TextTag", 
		description = "Render a I18n text message")
public class Text extends ContextBean implements Param.UnnamedParametric {
	private static final Logger LOG = LoggerFactory.getLogger(Text.class);

	protected List values = Collections.EMPTY_LIST;
	protected String actualName;
	protected String name;
	protected String searchStack;

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

	/**
	 * @param name the name to set
	 */
	@StrutsTagAttribute(description = " Name of resource property to fetch", required = true)
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @param searchStack the searchStack to set
	 */
	@StrutsTagAttribute(description = "Search the stack if property is not found on resources", type = "Boolean", defaultValue = "false")
	public void setSearchValueStack(String searchStack) {
		this.searchStack = searchStack;
	}

	/**
	 * @return true
	 */
	public boolean usesBody() {
		// overriding this to true such that EVAL_BODY_BUFFERED is return and
		// bodyContent will be valid hence, text between start & end tag will
		// be honoured as default message (WW-1268)
		return true;
	}

	/**
	 * @param writer the output writer.
	 * @param body the rendered body.
	 * @return true if the body should be evaluated again
	 */
	@SuppressWarnings("unchecked")
	public boolean end(Writer writer, String body) {
		actualName = findString(name, "name",
			"You must specify the i18n key. Example: welcome.header");
		String defaultMessage;
		if (Strings.isNotEmpty(body)) {
			defaultMessage = body;
		}
		else {
			defaultMessage = actualName;
		}

		Boolean doSearchStack = searchStack != null ? (Boolean) findValue(searchStack,
			Boolean.class) : false;
		String msg = TextProviderHelper.getText(actualName, defaultMessage, values, getStack(),
			doSearchStack == null || doSearchStack);

		if (msg != null) {
			try {
				if (getVar() == null) {
					writer.write(msg);
				}
				else {
					putInContext(msg);
				}
			}
			catch (IOException e) {
				LOG.error("Could not write out Text tag", e);
			}
		}

		return super.end(writer, "");
	}

	/**
	 * @see org.apache.struts2.components.Component#addParameter(java.lang.String, java.lang.Object)
	 */
	public void addParameter(String key, Object value) {
		addParameter(value);
	}

	/**
	 * @see org.apache.struts2.components.Param.UnnamedParametric#addParameter(java.lang.Object)
	 */
	@SuppressWarnings("unchecked")
	public void addParameter(Object value) {
		if (values.isEmpty()) {
			values = new ArrayList(4);
		}

		values.add(value);
	}
}
