/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 *
 * $Header:$
 */
package org.apache.beehive.netui.tags.html;

import org.apache.beehive.netui.tags.ByRef;
import org.apache.beehive.netui.tags.rendering.AbstractHtmlState;
import org.apache.beehive.netui.tags.rendering.TagRenderingBase;
import org.apache.beehive.netui.tags.rendering.WriteRenderAppender;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;

/**
 * <p>
 * Generates a URL-encoded hyperlink to a specified URI.
 * Also adds support for URL re-writing and JavaScript-based form submission.
 * An anchor must have one of the following attributes to correctly create the hyperlink:
 * <ul>
 * <li>action - an action invoked by clicking the hyperlink.</li>
 * <li>href - a URL to go to</li>
 * <li>linkName - an internal place in the page to move to</li>
 * <li>clientAction - the action to run on the client</li>
 * <li>tagId - the ID of the tag</li>
 * <li>formSubmit - indicates whether or not the enclosing Form should be submitted</li>
 * </ul>
 * </p>
 * @jsptagref.tagdescription <p>
 * Generates an anchor that can link to another document or invoke an action method in the Controller file.
 * The &lt;netui:anchor> tag also supports JavaScript-based form submission and URL re-writing.
 * <p>
 * An anchor must have one of the following attributes to correctly create the hyperlink:
 * <blockquote>
 * <ul>
 * <li><code>action</code> - the action method invoked by clicking the hyperlink</li>
 * <li><code>href</code> - the URL to go to</li>
 * <li><code>linkName</code> - an internal place in the page to move to</li>
 * <li><code>clientAction</code> - the action to run on the client</li>
 * <li><code>tagId</code> - the ID of the tag</li>
 * <li><code>formSubmit</code> - indicates whether or not the enclosing Form should be submitted</li>
 * </ul>
 * </blockquote>
 * </p>
 * <p>
 * The Anchor tag can accept NetUI parameter tags that implement the
 * {@link IUrlParams} interface.  When parameter tags are contained inside of
 * the Anchor tag, they add URL parameters onto the rendered href.  For
 * example:
 * <pre>
 *   &lt;netui:anchor href="foo.jsp" value="Go To Foo">
 *     &lt;netui:parameter name="paramKey" value="paramValue"/>
 *   &lt;/netui:anchor>
 * </pre>
 * will render:
 * <pre>
 *   &lt;a href="foo.jsp?paramKey=paramValue">Go To Foo&lt;/a>
 * </pre>
 * </p>
 * @example <b>Submitting Form Data</b>
 * <p>In this sample, clicking on this anchor submits the form data and invokes the method
 * <code>submitForm</code>.
 * <pre>
 *      &lt;netui:form action="formSubmit">
 *          Firstname:
 *          &lt;netui:textBox dataSource="actionForm.firstname"/>
 *          Lastname:
 *          &lt;netui:textBox dataSource="actionForm.lastname"/>
 *          &lt;netui:anchor formSubmit="true">Submit&lt;/netui:anchor>
 *      &lt;/netui:form></pre>
 * <p>If the <code>formSubmit</code> attribute is set to <code>true</code> and no
 * <code>onClick</code> attribute is set, the following JavaScript function will be written to the HTML page.
 * This JavaScript function will be referenced by the <code>onclick</code> attribute of the generated anchor tag.</p>
 * <pre>
 *  function anchor_submit_form(netuiName, newAction)
 *  {
 *    for (var i=0; i&lt;document.forms.length; i++) {
 *       if (document.forms[i].id == netuiName) {
 *          document.forms[i].method = "POST";
 *          document.forms[i].action = newAction;
 *          document.forms[i].submit();
 *       }
 *     }
 *  }</pre>
 * <p> The JavaScript function will be invoked by the generated HTML anchor tag as follows:
 * <pre>
 * &lt;a href="/WebApp/tagSamples/anchor/formSubmit.do"
 *       onClick='anchor_submit_form("Netui_Form_0","/WebApp/tagSamples/anchor/formSubmit.do");return false;'>Submit&lt;/a></pre>
 * <p>
 * <b>Custom JavaScript Functions</b>
 * </p>
 * <p>It is possible to write a custom <code>onClick</code> JavaScript event handler that would
 * do additional work, for example form validation, and still POST the form correctly.  To
 * accomplish this, add the custom JavaScript method to the page:
 * <pre>
 * function SubmitFromAnchor()
 * {
 *   // implement custom logic here
 *
 *   for(var i=0; i&lt;document.forms.length; i++)
 *   {
 *     // submit to the action /aWebapp/formPost.do
 *     if (document.forms[i].action == "/aWebapp/formPost.do")
 *     {
 *       document.forms[i].method="POST";
 *       document.forms[i].action="/aWebapp/formPost.do";
 *       document.forms[i].submit();
 *     }
 *   }
 * }</pre>
 * Then reference the JavaScript method from the &lt;netui:anchor> tag:
 * <pre>
 * &lt;netui:anchor formSubmit="true" onClick="SubmitFromAnchor(); return false;"&gt;Submit&lt;/netui:anchor&gt;</pre>
 * @netui:tag name="anchor" description="Generates a URL-encoded hyperlink to a specified URI."
 * @see Attribute
 * @see java.lang.String
 */
public class Anchor extends AnchorBase
{
    private String _text;         // The body content of this tag (if any).
    private String _value;

    /**
     * Returns the name of the Tag.
     */
    public String getTagName()
    {
        return "Anchor";
    }

    /**
     * This method will return the state associated with the tag.  This is used by this
     * base class to access the individual state objects created by the tags.
     * @return a subclass of the <code>AbstractHtmlState</code> class.
     */
    public AbstractHtmlState getState()
    {
        return _state;
    }

    /**
     * Sets the onClick javascript event.
     * @param onclick the onClick event.
     * @jsptagref.attributedescription The onClick JavaScript event.
     * @jsptagref.databindable false
     * @jsptagref.attributesyntaxvalue <i>string_onClick</i>
     * @netui:attribute required="false" rtexprvalue="true"
     * description="The onClick JavaScript event."
     */
    public void setOnClick(String onclick)
    {
        _state.registerAttribute(AbstractHtmlState.ATTR_JAVASCRIPT, ONCLICK, setNonEmptyValueAttribute(onclick));
        // Jira 299
        //_state.onClick = setNonEmptyValueAttribute(onclick);
    }

    /**
     * Set a client action to run on the client.  When set on an anchor, a NetUI JavaScript action
     * will be run.  This attribute may not be set if <code>href</code> or <code>action</code> is set.
     * @param action an action to run on the client.
     * @jsptagref.attributedescription The action (NetUI JavaScript) to run on the client.
     * @jsptagref.databindable false
     * @jsptagref.attributesyntaxvalue <i>string_clientAction</i>
     * @netui:attribute required="false" rtexprvalue="true" description="The client action."
     * description="The action (NetUI JavaScript) to run on the client."
     */
    public void setClientAction(String action)
            throws JspException
    {
        _clientAction = setRequiredValueAttribute(action, "clientAction");
    }

    /**
     * Sets the link name of the Anchor.  The link name is treated as a fragment
     * identifier and may or may not contain the "#" character.  If it does, the
     * link name will not be qualified into a ScriptContainer.  If the link name
     * does not contain the "#" the normal tagId qualification will take place
     * to produce the actual fragment identifier.
     * @param linkName the link name for the Anchor.
     * @jsptagref.attributedescription An internal place on the page to go to.
     * @jsptagref.databindable false
     * @jsptagref.attributesyntaxvalue <i>string_linkName</i>
     * @netui:attribute required="false"  rtexprvalue="true"
     * description="An internal place on the page to go to."
     */
    public void setLinkName(String linkName)
            throws JspException
    {
        _linkName = setRequiredValueAttribute(linkName, "linkName");
    }

    /**
     * Sets <code>charset</code> attribute for the anchor.
     * @param charSet the window target.
     * @jsptagref.attributedescription The character set.
     * @jsptagref.databindable false
     * @jsptagref.attributesyntaxvalue <i>string_charset</i>
     * @netui:attribute required="false"  rtexprvalue="true"
     * description="The character set."
     */
    public void setCharSet(String charSet)
    {
        _state.registerAttribute(AbstractHtmlState.ATTR_GENERAL, CHARSET, charSet);
    }

    /**
     * Sets <code>type</code> attribute for the anchor.
     * @param type the window target.
     * @jsptagref.attributedescription The type.
     * @jsptagref.databindable false
     * @jsptagref.attributesyntaxvalue <i>string_type</i>
     * @netui:attribute required="false"  rtexprvalue="true"
     * description="The type."
     */
    public void setType(String type)
    {
        _state.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TYPE, type);
    }

    /**
     * Sets <code>hreflang</code> attribute for the anchor.
     * @param hreflang the window target.
     * @jsptagref.attributedescription The HREF lang.
     * @jsptagref.databindable false
     * @jsptagref.attributesyntaxvalue <i>string_hreflang</i>
     * @netui:attribute required="false"  rtexprvalue="true"
     * description="The HREF lang."
     */
    public void setHrefLang(String hreflang)
    {
        _state.registerAttribute(AbstractHtmlState.ATTR_GENERAL, HREFLANG, hreflang);
    }

    /**
     * Sets <code>rel</code> attribute for the anchor.
     * @param rel the window target.
     * @jsptagref.attributedescription The relationship between the current document and the target Url.
     * @jsptagref.databindable false
     * @jsptagref.attributesyntaxvalue <i>string_rel</i>
     * @netui:attribute required="false"  rtexprvalue="true"
     * description="The rel."
     */
    public void setRel(String rel)
    {
        _state.registerAttribute(AbstractHtmlState.ATTR_GENERAL, REL, rel);
    }

    /**
     * Sets <code>rev</code> attribute for the anchor.
     * @param rev the window target.
     * @jsptagref.attributedescription Describes a reverse link from the anchor specified to the current document.
     * @jsptagref.databindable false
     * @jsptagref.attributesyntaxvalue <i>string_rev</i>
     * @netui:attribute required="false" rtexprvalue="true"
     * description="The rev."
     */
    public void setRev(String rev)
    {
        _state.registerAttribute(AbstractHtmlState.ATTR_GENERAL, REV, rev);
    }


    /**
     * Sets the window target.
     * @param target the window target.
     * @jsptagref.attributedescription The window target.
     * @jsptagref.databindable false
     * @jsptagref.attributesyntaxvalue <i>string_action</i>
     * @netui:attribute required="false"  rtexprvalue="true"
     * description="The window target."
     */
    public void setTarget(String target)
    {
        _state.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TARGET, target);
    }

    /**
     * This will set the text of the anchor.  If there is body content, this
     * will override that value.
     * @param value the text of the anchor.
     * @jsptagref.attributedescription Set the text of the anchor, overriding the body content.
     * @jsptagref.databindable false
     * @jsptagref.attributesyntaxvalue <i>string_value</i>
     * @netui:attribute required="false"  rtexprvalue="true"
     * description="Set the text of the anchor overriding the body content"
     */
    public void setValue(String value)
    {
        _value = setNonEmptyValueAttribute(value);
    }

    /**
     * Prepare the hyperlink for rendering
     * @throws JspException if a JSP exception has occurred
     */
    public int doStartTag() throws JspException
    {
        if (hasErrors())
            return SKIP_BODY;
        return EVAL_BODY_BUFFERED;
    }

    /**
     * Save the body content of the Anchor.
     * @throws JspException if a JSP exception has occurred
     */
    public int doAfterBody() throws JspException
    {
        if (bodyContent != null && _value == null) {
            String value = bodyContent.getString().trim();
            bodyContent.clearBody();
            if (value.length() > 0)
                _text = value;
        }
        return SKIP_BODY;
    }

    /**
     * Render the hyperlink.
     * @throws JspException if a JSP exception has occurred
     */
    public int doEndTag() throws JspException
    {
        // report errors that may have occurred when the required attributes are being set
        if (hasErrors())
            return reportAndExit(EVAL_PAGE);

        if (_value != null) {
            _text = _value;
        }
        
        // build the anchor into the results
        ByRef script = new ByRef();

        WriteRenderAppender writer = new WriteRenderAppender(pageContext);
        HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
        TagRenderingBase trb = TagRenderingBase.Factory.getRendering(TagRenderingBase.ANCHOR_TAG, request);

        if (!createAnchorBeginTag(request, script, trb, writer, REQUIRED_ATTR)) {
            if (!script.isNull())
                write(script.getRef().toString());
            return reportAndExit(EVAL_PAGE);
        }

        if (_text != null)
            write(_text);

        assert(trb != null) : "trb is null";
        trb.doEndTag(writer);

        if (!script.isNull())
            write(script.getRef().toString());

        // Render the remainder to the output stream
        localRelease();
        return EVAL_PAGE;
    }

    @Override
    protected void localRelease() {
        super.localRelease();
        _text = null;
        _value = null;
    }
}
