/*
 * 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 java.io.Writer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import nuts.exts.struts2.NutsStrutsConstants;

import org.apache.struts2.components.ComponentUrlProvider;
import org.apache.struts2.components.ContextBean;
import org.apache.struts2.components.ExtraParameterProvider;
import org.apache.struts2.components.Param;
import org.apache.struts2.components.UrlProvider;
import org.apache.struts2.components.UrlRenderer;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;

import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;

/**
 * <!-- START SNIPPET: javadoc -->
 *
 * <p>This tag is used to create a URL.</p>
 *
 * <p>You can use the &lt;param&gt; tag inside the body to provide
 * additional request parameters. If the value of a param is an Array or
 * an Iterable all the values will be added to the URL.</p>
 *
 * <b>NOTE:</b>
 * <p>By default request parameters will be separated using escaped ampersands (i.e., &amp;amp;).
 * This is necessary for XHTML compliance, however, when using the URL generated by this tag
 * with the &lt;s:property&gt; tag, the <b>escapeAmp</b> attribute should be used to disable
 * ampersand escaping.</p>
 *
 * <b>NOTE:</b>
 * <p>When includeParams is 'all' or 'get', the parameter defined in a &lt;param&gt;
 * tag will take precedence over any params included due to the includeParams attribute. For
 * example, in Example 3 below, if there is a id parameter in the url where the page this
 * tag is included like http://&lt;host&gt;:&lt;port&gt;/&lt;context&gt;/editUser.action?id=3333&name=John
 * the generated url will be http://&lt;host&gt;:&lt;port&gt;/&lt;context&gt;/editUser.action?id=22&name=John
 * because the parameter defined in the param tag will take precedence.</p>
 *
 * <!-- END SNIPPET: javadoc -->
 *
 *
 * <!-- START SNIPPET: params -->
 *
 * <ul>
 *      <li>action (String) - (value or action choose either one, if both exist value takes precedence) action's name (alias) <li>
 *      <li>value (String) - (value or action choose either one, if both exist value takes precedence) the url itself</li>
 *      <li>scheme (String) - http scheme (http, https) defaults to the scheme this request is in</li>
 *      <li>namespace - action's namespace</li>
 *      <li>method (String) - action's method name, defaults to 'execute'</li>
 *      <li>encode (Boolean) - url encode the generated url. Defaults to 'true'.</li>
 *      <li>includeParams (String) - The includeParams attribute may have the value 'none', 'get' or 'all'. Defaults to 'get'.
 *                                   none - include no parameters in the URL
 *                                   get  - include only GET parameters in the URL (default)
 *                                   all  - include both GET and POST parameters in the URL
 *      </li>
 *      <li>includeContext (Boolean) - Specifies whether to include the web app context path. Defaults to 'true'.</li>
 *      <li>escapeAmp (Boolean) - Specifies whether to escape ampersand (&amp;) to (&amp;amp;) or not. Defaults to 'true'.</li>
 *      <li>portletMode (String) - The resulting portlet mode.</li>
 *      <li>windowState (String) - The resulting portlet window state.</li>
 *      <li>portletUrlType (String) - Specifies if this should be a portlet render or action URL.</li>
 *      <li>forceAddSchemeHostAndPort (Boolean) - Specifies whether to force the addition of scheme, host and port or not.</li>
 * </ul>
 *
 * <!-- END SNIPPET: params -->
 *
 * <p/> <b>Examples</b>
 * <pre>
 * <!-- START SNIPPET: example -->
 *
 * &lt;-- Example 1 --&gt;
 * &lt;n:url value="editGadget.action"&gt;
 *     &lt;s:param name="id" value="%{selected}" /&gt;
 * &lt;/n:url&gt;
 *
 * &lt;-- Example 2 --&gt;
 * &lt;n:url action="editGadget"&gt;
 *     &lt;s:param name="id" value="%{selected}" /&gt;
 * &lt;/n:url&gt;
 *
 * &lt;-- Example 3--&gt;
 * &lt;n:url includeParams="get"&gt;
 *     &lt;s:param name="id" value="%{'22'}" /&gt;
 * &lt;/n:url&gt;
 *
 * <!-- END SNIPPET: example -->
 * </pre>
 *
 * @see Param
 *
 */
@StrutsTag(
		name="url", 
		tldTagClass="nuts.exts.struts2.views.jsp.URLTag", 
		description="This tag is used to create a URL")
public class URL extends ContextBean {
    private UrlProvider urlProvider;
    private UrlRenderer urlRenderer;

    /**
     * Constructor
     * 
     * @param stack value stack
     * @param req request
     * @param res response
     */
    public URL(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
        super(stack);
        urlProvider = new ComponentUrlProvider(this, this.parameters);
        urlProvider.setHttpServletRequest(req);
        urlProvider.setHttpServletResponse(res);
        urlProvider.setEscapeAmp(true);
    }

    /**
     * @param encode encode
     */
    @Inject(value=NutsStrutsConstants.NUTS_URL_ENCODE, required=false)
    public void setUrlEncode(String encode) {
    	urlProvider.setEncode(Boolean.parseBoolean(encode));
    }

    /**
     * @param escapeAmp escapeAmp
     */
    @Inject(value=NutsStrutsConstants.NUTS_URL_ESCAPEAMP, required=false)
    public void setUrlEscapeAmp(String escapeAmp) {
    	urlProvider.setEscapeAmp(Boolean.parseBoolean(escapeAmp));
    }

    /**
     * @param urlIncludeParams urlIncludeParams
     */
    @Inject(value=NutsStrutsConstants.NUTS_URL_INCLUDEPARAMS, required=false)
    public void setUrlIncludeParams(String urlIncludeParams) {
       urlProvider.setUrlIncludeParams(urlIncludeParams);
    }

    /**
     * @param urlRenderer urlRenderer
     */
    @Inject
	public void setUrlRenderer(UrlRenderer urlRenderer) {
		urlProvider.setUrlRenderer(urlRenderer);
        this.urlRenderer = urlRenderer;
	}

    /**
     * @param provider provider
     */
    @Inject(required=false)
    public void setExtraParameterProvider(ExtraParameterProvider provider) {
        urlProvider.setExtraParameterProvider(provider);
    }

    /**
     * Callback for the start tag of this component.
     * Should the body be evaluated?
     *
     * @param writer  the output writer.
     * @return true if the body should be evaluated
     */
    public boolean start(Writer writer) {
        boolean result = super.start(writer);
        urlRenderer.beforeRenderUrl(urlProvider);
        return result;
    }

    /**
     * Callback for the end tag of this component.
     * Should the body be evaluated again?
     * <p/>
     * <b>NOTE:</b> will pop component stack.
     * @param writer  the output writer.
     * @param body    the rendered body.
     * @return true if the body should be evaluated again
     */
    public boolean end(Writer writer, String body) {
    	urlRenderer.renderUrl(writer, urlProvider);
        return super.end(writer, body);
    }


    /**
     * @return urlProvider
     */
    public UrlProvider getUrlProvider() {
        return urlProvider;
    }

    /**
     * @param includeParams includeParams
     */
    @StrutsTagAttribute(description="The includeParams attribute may have the value 'none', 'get' or 'all'", defaultValue="none")
    public void setIncludeParams(String includeParams) {
        urlProvider.setIncludeParams(includeParams);
    }

    /**
     * @param scheme scheme
     */
    @StrutsTagAttribute(description="Set scheme attribute")
    public void setScheme(String scheme) {
        urlProvider.setScheme(scheme);
    }

    /**
     * @param value value
     */
    @StrutsTagAttribute(description="The target value to use, if not using action")
    public void setValue(String value) {
        urlProvider.setValue(value);
    }

    /**
     * @param action action
     */
    @StrutsTagAttribute(description="The action to generate the URL for, if not using value")
    public void setAction(String action) {
        urlProvider.setAction(action);
    }

    /**
     * @param namespace namespace
     */
    @StrutsTagAttribute(description="The namespace to use")
    public void setNamespace(String namespace) {
        urlProvider.setNamespace(namespace);
    }

    /**
     * @param method method
     */
    @StrutsTagAttribute(description="The method of action to use")
    public void setMethod(String method) {
        urlProvider.setMethod(method);
    }

    /**
     * @param encode encode
     */
    @StrutsTagAttribute(description="Whether to encode parameters", type="Boolean", defaultValue="true")
    public void setEncode(boolean encode) {
        urlProvider.setEncode(encode);
    }

    /**
     * @param includeContext includeContext
     */
    @StrutsTagAttribute(description="Whether actual context should be included in URL", type="Boolean", defaultValue="true")
    public void setIncludeContext(boolean includeContext) {
        urlProvider.setIncludeContext(includeContext);
    }

    /**
     * @param portletMode portletMode
     */
    @StrutsTagAttribute(description="The resulting portlet mode")
    public void setPortletMode(String portletMode) {
        urlProvider.setPortletMode(portletMode);
    }

    /**
     * @param windowState windowState
     */
    @StrutsTagAttribute(description="The resulting portlet window state")
    public void setWindowState(String windowState) {
        urlProvider.setWindowState(windowState);
    }

    /**
     * @param portletUrlType portletUrlType
     */
    @StrutsTagAttribute(description="Specifies if this should be a portlet render or action URL. Default is \"render\". To create an action URL, use \"action\".")
    public void setPortletUrlType(String portletUrlType) {
       urlProvider.setPortletUrlType(portletUrlType);
    }

    /**
     * @param anchor anchor
     */
    @StrutsTagAttribute(description="The anchor for this URL")
    public void setAnchor(String anchor) {
        urlProvider.setAnchor(anchor);
    }

    /**
     * @param escapeAmp escapeAmp
     */
    @StrutsTagAttribute(description="Specifies whether to escape ampersand (&amp;) to (&amp;amp;) or not", type="Boolean", defaultValue="false")
    public void setEscapeAmp(boolean escapeAmp) {
        urlProvider.setEscapeAmp(escapeAmp);
    }

    /**
     * @param forceAddSchemeHostAndPort forceAddSchemeHostAndPort
     */
    @StrutsTagAttribute(description="Specifies whether to force the addition of scheme, host and port or not", type="Boolean", defaultValue="false")
    public void setForceAddSchemeHostAndPort(boolean forceAddSchemeHostAndPort) {
        urlProvider.setForceAddSchemeHostAndPort(forceAddSchemeHostAndPort);
    }
}
