package it.mice.voila.runtime.springmvc.taglibs;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import javax.servlet.jsp.JspException;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import org.springframework.web.servlet.tags.form.AbstractHtmlElementTag;
import org.springframework.web.servlet.tags.form.TagWriter;

public class InternalFrameTag extends AbstractHtmlElementTag {
    /**
	 */
	private static final long serialVersionUID = 1L;

	private static final String STYLE_PRIMARY = "primary";
	private static final String STYLE_DEFAULT = "default";
	private static final String STYLE_TAB_CONTENT = "tab-content";
	
	private String frameId = null;
	private String style = STYLE_PRIMARY;
    private String legendLabel = null;
    private boolean collapsable = false;
    private boolean closeable = false;
    private boolean goTopLink = false;
    private String initialState = "expanded";
    private String modes = null;
    private boolean useFieldset = true;

    private String cmdLabel1;
    private String cmdAction1;
    private String cmdTitle1;
    private String cmdLabel2;
    private String cmdAction2;
    private String cmdTitle2;
    private String cmdLabel3;
    private String cmdAction3;
    private String cmdTitle3;
    private String cmdLabel4;
    private String cmdAction4;
    private String cmdTitle4;

	private TagWriter tagWriter;
	private Collection<FrameCommandTag> commandList = new ArrayList<FrameCommandTag>();
	
	public boolean isStyleTabContent() {
		return STYLE_TAB_CONTENT.equals(getStyle());
	}
	
	public String getModes() {
		return modes;
	}

	public void setModes(String modes) {
		this.modes = modes;
	}

	public String getStyle() {
		return style;
	}

	public void setStyle(String style) {
		this.style = style;
	}

	public String getCmdLabel1() {
		return cmdLabel1;
	}

	public void setCmdLabel1(String cmdLabel1) {
		this.cmdLabel1 = cmdLabel1;
	}

	public String getCmdAction1() {
		return cmdAction1;
	}

	public void setCmdAction1(String cmdAction1) {
		this.cmdAction1 = cmdAction1;
	}

	public String getCmdTitle1() {
		return cmdTitle1;
	}

	public void setCmdTitle1(String cmdTitle1) {
		this.cmdTitle1 = cmdTitle1;
	}

	public String getCmdLabel2() {
		return cmdLabel2;
	}

	public void setCmdLabel2(String cmdLabel2) {
		this.cmdLabel2 = cmdLabel2;
	}

	public String getCmdAction2() {
		return cmdAction2;
	}

	public void setCmdAction2(String cmdAction2) {
		this.cmdAction2 = cmdAction2;
	}

	public String getCmdTitle2() {
		return cmdTitle2;
	}

	public void setCmdTitle2(String cmdTitle2) {
		this.cmdTitle2 = cmdTitle2;
	}

	public String getCmdLabel3() {
		return cmdLabel3;
	}

	public void setCmdLabel3(String cmdLabel3) {
		this.cmdLabel3 = cmdLabel3;
	}

	public String getCmdAction3() {
		return cmdAction3;
	}

	public void setCmdAction3(String cmdAction3) {
		this.cmdAction3 = cmdAction3;
	}

	public String getCmdTitle3() {
		return cmdTitle3;
	}

	public void setCmdTitle3(String cmdTitle3) {
		this.cmdTitle3 = cmdTitle3;
	}

	public String getCmdLabel4() {
		return cmdLabel4;
	}

	public void setCmdLabel4(String cmdLabel4) {
		this.cmdLabel4 = cmdLabel4;
	}

	public String getCmdAction4() {
		return cmdAction4;
	}

	public void setCmdAction4(String cmdAction4) {
		this.cmdAction4 = cmdAction4;
	}

	public String getCmdTitle4() {
		return cmdTitle4;
	}

	public void setCmdTitle4(String cmdTitle4) {
		this.cmdTitle4 = cmdTitle4;
	}

	public Collection<FrameCommandTag> getCommandList() {
		return commandList;
	}

	public void setCommandList(Collection<FrameCommandTag> commandList) {
		this.commandList = commandList;
	}

	public String getFrameId() {
		return frameId;
	}

	public void setFrameId(String frameId) {
		this.frameId = frameId;
	}

	public String getLegendLabel() {
		return legendLabel;
	}

	public void setLegendLabel(String legendLabel) {
		this.legendLabel = legendLabel;
	}

	public String getInitialState() {
		return initialState;
	}

	public void setInitialState(String initialState) {
		this.initialState = initialState;
	}

	public boolean isUseFieldset() {
		return useFieldset;
	}

	public void setUseFieldset(boolean useFieldset) {
		this.useFieldset = useFieldset;
	}

	public boolean isCollapsable() {
		return collapsable;
	}

	public void setCollapsable(boolean collapsable) {
		this.collapsable = collapsable;
	}

	public boolean isCloseable() {
		return closeable;
	}

	public void setCloseable(boolean closeable) {
		this.closeable = closeable;
	}

	public boolean isGoTopLink() {
		return goTopLink;
	}

	public void setGoTopLink(boolean goTopLink) {
		this.goTopLink = goTopLink;
	}

	@Override
	protected int writeTagContent(TagWriter tagWriter) throws JspException {
		this.tagWriter = tagWriter;

        MessageSource messageSource = getRequestContext().getMessageSource();
        
        processFrameCommands(messageSource);

        writeFieldsetAndLegend(messageSource);
		
        if (TagUtils.isBootstrapTheme()) {
    		tagWriter.startTag("div");
    		tagWriter.writeAttribute("id", "IFB" + getFrameId());
       		tagWriter.writeAttribute(CLASS_ATTRIBUTE, "box-body");
            if (isCollapsable() && getInitialState() != null && getInitialState().equalsIgnoreCase("collapsed")) {
        		tagWriter.writeAttribute(STYLE_ATTRIBUTE, "display: none;");
            }
            tagWriter.forceBlock();
    		if (isStyleTabContent()) {
        		String label = TagUtils.getMessage(getRequestContext(), evaluate("legendLabel", legendLabel));
        		label = getDisplayString(label);
        		writeBoxTitle(label);
    		}
        } else {
    		tagWriter.startTag("div");
    		tagWriter.writeAttribute("id", "IFB" + getFrameId());
            if (isCollapsable() && getInitialState() != null && getInitialState().equalsIgnoreCase("collapsed")) {
        		tagWriter.writeAttribute(CLASS_ATTRIBUTE, "layerFrameBodyCollapsed");
            } else {
        		tagWriter.writeAttribute(CLASS_ATTRIBUTE, "layerFrameBody");
            }
            tagWriter.forceBlock();
        }

        return EVAL_BODY_INCLUDE;
	}
	
	private void processFrameCommands(MessageSource messageSource) throws JspException {
		processFrameCommand(messageSource, getCmdLabel1(), getCmdAction1(), getCmdTitle1());
		processFrameCommand(messageSource, getCmdLabel2(), getCmdAction2(), getCmdTitle2());
		processFrameCommand(messageSource, getCmdLabel3(), getCmdAction3(), getCmdTitle3());
		processFrameCommand(messageSource, getCmdLabel4(), getCmdAction4(), getCmdTitle4());
	}

	private void processFrameCommand(MessageSource messageSource,
			String label, String action, String title) throws JspException {

		if (StringUtils.isEmpty(action)) {
			return;
		}
		FrameCommandTag commandBean = new FrameCommandTag();
		commandBean.setAction(evaluate("action", action).toString());
		commandBean.setLabel(TagUtils.getMessage(getRequestContext(), evaluate("label", label)));
		commandBean.setTitle(TagUtils.getMessage(getRequestContext(), evaluate("title", title)));

    	getCommandList().add(commandBean);
	}

	private void writeFieldsetAndLegend(MessageSource messageSource) throws JspException {
		if (TagUtils.isBootstrapTheme()) {
			writeBootstrapFieldsetAndLegend(messageSource);
		} else {
			writeStandardFieldsetAndLegend(messageSource);
		}
	}

	private void writeBootstrapFieldsetAndLegend(MessageSource messageSource) throws JspException {
		if (isUseFieldset()) {
			tagWriter.startTag("fieldset");
		} else {
			tagWriter.startTag("div");
		}
		
		if (StringUtils.isEmpty(getCssClass())) {
   			setCssClass("box box-solid box-primary");
		}
        if (isCollapsable() && getInitialState() != null && getInitialState().equalsIgnoreCase("collapsed")) {
			setCssClass(getCssClass() + " collapsed-box");
        }
		tagWriter.writeAttribute("id", "IF" + getFrameId());

		try {
			writeOptionalAttributes(tagWriter);
		} catch (Exception e) {
			logger.debug(e + " raised during internal frame rendering. Probably the frame was used out of a BindingResult object resolution. Ignoring...");
		}

		if (isStyleTabContent()) {
			return;
		}
		
		String label = TagUtils.getMessage(getRequestContext(), evaluate("legendLabel", legendLabel));
		label = getDisplayString(label);
		if (StringUtils.isEmpty(label) && !isUseFieldset()) {
			return;
		}
		if (isUseFieldset()) {
			tagWriter.startTag("div");
			tagWriter.writeAttribute("id", "le" + getFrameId());
			if (StringUtils.isEmpty(label)) {
				label = "&nbsp;";
			}
		} else {
			tagWriter.startTag("div");
		}
		tagWriter.writeAttribute(CLASS_ATTRIBUTE, "box-header");
		
		writeBoxTitle(label);

		tagWriter.startTag("div");
		tagWriter.writeAttribute(CLASS_ATTRIBUTE, "box-tools pull-right");

		int ctr = 1;
		for (Iterator<FrameCommandTag> i = commandList.iterator(); i.hasNext();) {
			FrameCommandTag tag = i.next();
			processBootstrapCommand(tag, ctr++);
		}

		if (!StringUtils.isEmpty(getModes())) {
			writeModesWidget();
		}

		if (isCollapsable()) {
			writeHeaderWidget("collapse", getInitialState().equalsIgnoreCase("collapsed") ? "fa-plus" : "fa-minus", null, null, null);
		}

		if (isCloseable()) {
			writeHeaderWidget("remove", "fa-times", null, null, null);
		}

		tagWriter.endTag(true); //div box-tools

		tagWriter.endTag(true); //legend or div
	}

	private void writeBoxTitle(String label) throws JspException {
		tagWriter.startTag("h3");
		tagWriter.writeAttribute(CLASS_ATTRIBUTE, "box-title");
		tagWriter.appendValue(label);
		tagWriter.endTag();
	}

	private void writeModesWidget() throws JspException {
		String[] theModes = getModes().split(" ");
		for (String mode : theModes) {
			writeModeWidget(mode.trim());
		}
	}

	private void writeModeWidget(String mode) throws JspException {
//		<div class="btn-group">
//			<button type="submit"
//				name="button.submit.search?listViewMode=calendar"
//				id="button_submit_search_listViewMode_calendar"
//				class="btn btn-primary btn-sm dropdown-toggle" title="Calendar view mode"
//				data-toggle="dropdown">
//				<i class="fa fa-clock-o"></i>
//				&nbsp;<span class="caret"></span>
//			</button>
//			<ul class="dropdown-menu" role="menu">
//	            <li><a href="search?listViewMode=calendar&listViewModeFor=orderDate" title="Calendar view mode based upon Order Date">per Order Date</a></li>
//	            <li><a href="search?listViewMode=calendar&listViewModeFor=shipDate" title="Calendar view mode based upon Ship Date">per Ship Date</a></li>
//	        </ul>
//      </div>

		boolean hasOneModeFor = false;
		boolean hasMoreModeFor = false;
		int squareBracketStart = mode.indexOf("[");
		int squareBracketEnd = mode.indexOf("]");
		String[] modeFors = null;
		if (squareBracketStart > -1 && squareBracketEnd > -1) {
			String modeFor = mode.substring(squareBracketStart + 1, squareBracketEnd);
			modeFors = modeFor.split(",");
			mode = mode.substring(0, squareBracketStart);
			hasOneModeFor = modeFors.length == 1;
			hasMoreModeFor = modeFors.length > 1;
		}
		
		String name = "button.submit.search?listViewMode=" + mode + (hasOneModeFor ? "&listViewModeFor=" + modeFors[0] : "");
		tagWriter.startTag("div");
		tagWriter.writeAttribute("class", "btn-group");

		tagWriter.startTag("button");
		tagWriter.writeAttribute("type", "submit");
		tagWriter.writeAttribute("name", name);
		tagWriter.writeAttribute("id", "button_submit_search_listViewMode_" + mode);
		tagWriter.writeAttribute("class", "btn btn-primary btn-sm");
		tagWriter.writeAttribute("title", getRequestContext().getMessage(mode + ".view.mode.title", mode + " view mode"));

		if (hasMoreModeFor) {
			tagWriter.writeAttribute("data-toggle", "dropdown");
		}
		
		String icon = "fa-" + mode;
		if ("list".equals(mode)) {
			icon = "fa-table";
		}
		if ("calendar".equals(mode)) {
			icon = "fa-clock-o";
		}
		if ("map".equals(mode)) {
			icon = "fa-globe";
		}
		if ("kanban".equals(mode)) {
			icon = "fa-columns";
		}
		if ("chart".equals(mode)) {
			icon = "fa-bar-chart";
		}
		tagWriter.startTag("i");
		tagWriter.writeAttribute("class", "fa " + icon);
		tagWriter.endTag(true); //i

		if (hasMoreModeFor) {
			tagWriter.appendValue("&nbsp;");
			tagWriter.startTag("span");
			tagWriter.writeAttribute("class", "caret");
			tagWriter.endTag(true); //span
		}

		tagWriter.endTag(true); //button

		if (hasMoreModeFor) {
			tagWriter.startTag("ul");
			tagWriter.writeAttribute("class", "dropdown-menu dropdown-menu-right");
			tagWriter.writeAttribute("role", "menu");

			for (String modeFor : modeFors) {
				String trimmedModeFor = modeFor.trim();
				String fieldLabel = resolveFieldLabel(trimmedModeFor.replaceAll(":", ""));
//	            <li><a href="search?listViewMode=calendar&listViewModeFor=orderDate" title="Calendar view mode based upon Order Date">per Order Date</a></li>
				tagWriter.startTag("li");
				tagWriter.startTag("a");
				tagWriter.writeAttribute("href", "search?listViewMode=" + mode + "&listViewModeFor=" + trimmedModeFor);
				tagWriter.writeAttribute("title", getRequestContext().getMessage("view.mode.for.title", new String[] {fieldLabel}, mode + " view mode on " + trimmedModeFor));
				tagWriter.appendValue(getRequestContext().getMessage("view.mode.for.label", new String[] {fieldLabel}, "for " + trimmedModeFor));
				
				tagWriter.endTag(true); //a
				tagWriter.endTag(true); //li
			}
			
			tagWriter.endTag(true); //ul
		}
		
		tagWriter.endTag(true); //div.btn-group
	}

	private String resolveFieldLabel(String trimmedModeFor) {
		//Try by prepending label.getFrameId() to the field name
		String result = getRequestContext().getMessage("label." + getFrameId() + "." + trimmedModeFor, trimmedModeFor);
		return result;
	}

	private void writeHeaderWidget(String widget, String icon, String label, String title, String onClick) throws JspException {
		//<button type="button" class="btn btn-primary btn-sm" data-widget="collapse"><i class="fa fa-minus"></i></button>
		tagWriter.startTag("button");
		tagWriter.writeAttribute("type", "button");
		tagWriter.writeAttribute("class", "btn btn-primary btn-sm");
		if (!StringUtils.isEmpty(title)) {
			tagWriter.writeAttribute("title", title);
		}
		if (!StringUtils.isEmpty(widget)) {
			tagWriter.writeAttribute("data-widget", widget);
		}
		if (!StringUtils.isEmpty(onClick)) {
			tagWriter.writeAttribute("onClick", onClick);
		}

		if (!StringUtils.isEmpty(icon)) {
			//<i class="fa fa-minus"></i>
			tagWriter.startTag("i");
			tagWriter.writeAttribute("class", "fa " + icon);
			tagWriter.endTag(true); //i
		}

		if (!StringUtils.isEmpty(label)) {
			tagWriter.appendValue(label);;
		}

		tagWriter.endTag(true); //button
	}

	private void writeStandardFieldsetAndLegend(MessageSource messageSource) throws JspException {
		if (isUseFieldset()) {
			tagWriter.startTag("fieldset");
		} else {
			tagWriter.startTag("div");
			setCssClass(TagUtils.safePrepend(getCssClass(), "fieldset"));
		}
		tagWriter.writeAttribute("id", "IF" + getFrameId());

		try {
			writeOptionalAttributes(tagWriter);
		} catch (Exception e) {
			logger.debug(e + " raised during internal frame rendering. Probably the frame was used out of a BindingResult object resolution. Ignoring...");
		}

		String label = TagUtils.getMessage(getRequestContext(), evaluate("legendLabel", legendLabel));
		label = getDisplayString(label);
		if (StringUtils.isEmpty(label) && !isUseFieldset()) {
			return;
		}
		if (isUseFieldset()) {
			tagWriter.startTag("legend");
			tagWriter.writeAttribute("id", "le" + getFrameId());
			if (StringUtils.isEmpty(label)) {
				tagWriter.writeAttribute(CLASS_ATTRIBUTE, "empty");
				label = "&nbsp;";
			}
		} else {
			tagWriter.startTag("h3");
			tagWriter.writeAttribute(CLASS_ATTRIBUTE, "legend");
		}
		if (isCollapsable()) {
			tagWriter.writeAttribute(ONCLICK_ATTRIBUTE, "switchFrameLayout('" + getFrameId() + "', true)");
		}
		tagWriter.appendValue(label);
		tagWriter.endTag();
		
		writeFieldsetBanner(messageSource);
	}

	private void writeFieldsetBanner(MessageSource messageSource)
			throws JspException {
		tagWriter.startTag("div");
		tagWriter.writeAttribute(CLASS_ATTRIBUTE, "fieldsetBanner");
		tagWriter.forceBlock();
		
		int ctr = 1;
		for (Iterator<FrameCommandTag> i = commandList.iterator(); i.hasNext();) {
			FrameCommandTag tag = i.next();
			processCommand(tag, ctr++);
		}
		
        writeCollapseableCommand(messageSource);
        writeCloseableCommand(messageSource);
		tagWriter.endTag();
		writeClearDiv();
	}

	private void writeClearDiv() throws JspException {
		tagWriter.startTag("div");
		tagWriter.writeAttribute(CLASS_ATTRIBUTE, "cleardiv");
		tagWriter.appendValue("&nbsp;");
		tagWriter.endTag();
	}

	private void writeCloseableCommand(MessageSource messageSource)
			throws JspException {
		if (isCloseable()) {
        	String msgClose = messageSource.getMessage("label.frame.close", TagUtils.EMPTY_ARRAY, getRequestContext().getLocale());
        	TagUtils.safeWrite(pageContext, "|&nbsp;");
            tagWriter.startTag("a");
    		tagWriter.writeAttribute("id", "IFCI" + getFrameId());
    		tagWriter.writeAttribute("href", "javascript: closeFrame('" + getFrameId() + "')");
    		tagWriter.writeAttribute(CLASS_ATTRIBUTE, "close");
    		tagWriter.writeAttribute(TITLE_ATTRIBUTE, msgClose);
    		tagWriter.appendValue(msgClose);
    		tagWriter.endTag();
        	TagUtils.safeWrite(pageContext, "&nbsp;");
        }
	}

	private void writeCollapseableCommand(MessageSource messageSource)
			throws JspException {
		if (isCollapsable()) {
    		tagWriter.startTag("span");
    		tagWriter.writeAttribute("id", "IFTI" + getFrameId() + "_c");
    		tagWriter.writeAttribute(CLASS_ATTRIBUTE, "container");
    		tagWriter.forceBlock();
    		tagWriter.endTag();

        	if (TagUtils.isMessageCode(getTitle())) {
            	setTitle(messageSource.getMessage(TagUtils.getMessageCode(getTitle()), TagUtils.EMPTY_ARRAY, getTitle(), getRequestContext().getLocale()));
        	}
            String trottleClass;
            if (getInitialState() != null && getInitialState().equalsIgnoreCase("collapsed")) {
                trottleClass = "collapsed";
            } else {
                trottleClass = "expanded";
            }

            addComplexAttribute("IFTI" + getFrameId(), 
            					"javascript: switchFrameLayout('" + getFrameId() + "', true)", 
            					messageSource.getMessage("label.frame.collapseExpand", new String[] {getTitle()}, getRequestContext().getLocale()),
            					messageSource.getMessage("label.frame.collapseExpandTitle", new String[] {getTitle()}, getRequestContext().getLocale()),
            					"trottle " + trottleClass);
        }
	}

	private void addComplexAttribute(String property, String href, String name, String title, String className) throws JspException {
    	tagWriter.startTag("script");
    	tagWriter.writeAttribute("type", "text/JavaScript");

        StringBuffer scriptContent = new StringBuffer("AddLoadEvent(function() { initInternalFrameField(\"");

        scriptContent.append(property).append("\"");

        if (href != null) {
            scriptContent.append(", \"").append(href).append("\"");
        } else {
            scriptContent.append(",undefined ");
        }
        if (name != null) {
            scriptContent.append(", \"").append(name).append("\"");
        } else {
            scriptContent.append(",undefined ");
        }
        if (title != null) {
            scriptContent.append(", \"").append(StringEscapeUtils.escapeJavaScript(title)).append("\"");
        } else {
            scriptContent.append(",undefined ");
        }
        if (className != null) {
            scriptContent.append(", \"").append(className).append("\"");
        } else {
            scriptContent.append(",undefined ");
        }
        scriptContent.append(");");

    	tagWriter.appendValue(scriptContent.toString());

    	tagWriter.appendValue("})");
    	tagWriter.endTag();
	}

	private void processCommand(FrameCommandTag tag, int ctr) throws JspException {
        if (tag.getAction().startsWith("javascript:")) {
            tagWriter.startTag("span");
    		tagWriter.writeAttribute("id", "IFTI" + getFrameId() + ctr + "_c");
    		tagWriter.writeAttribute(CLASS_ATTRIBUTE, "container");
    		tagWriter.forceBlock();
    		tagWriter.endTag();
            addComplexAttribute("IFTI" + getFrameId() + ctr, tag.getAction(), tag.getLabel(), tag.getTitle(), null);
        } else {
        	TagUtils.safeWrite(pageContext, "|&nbsp;");
            tagWriter.startTag("a");
    		tagWriter.writeAttribute("href", tag.getAction());
    		tagWriter.writeAttribute(TITLE_ATTRIBUTE, tag.getTitle());
    		tagWriter.appendValue(tag.getLabel());
    		tagWriter.endTag();
        	TagUtils.safeWrite(pageContext, "&nbsp;");
        }
	}

	private void processBootstrapCommand(FrameCommandTag tag, int ctr) throws JspException {
        if (tag.getAction().startsWith("widget:")) {
        	String dataWidget = tag.getAction().substring("widget:".length()).trim();
    		writeHeaderWidget(dataWidget, null, tag.getLabel(), tag.getTitle(), null);
        } else if (tag.getAction().startsWith("javascript:")) {
        	String onClick = tag.getAction().substring("javascript:".length()).trim();
    		writeHeaderWidget(null, null, tag.getLabel(), tag.getTitle(), onClick);
        } else {
        	if (ctr > 1) {
            	TagUtils.safeWrite(pageContext, "|&nbsp;");
        	}
            tagWriter.startTag("a");
    		tagWriter.writeAttribute("href", tag.getAction());
    		tagWriter.writeAttribute(TITLE_ATTRIBUTE, tag.getTitle());
    		tagWriter.appendValue(tag.getLabel());
    		tagWriter.endTag();
        	TagUtils.safeWrite(pageContext, "&nbsp;");
        }
	}

	/**
	 * Closes the '<code>form</code>' block tag and removes the form object name
	 * from the {@link javax.servlet.jsp.PageContext}.
	 */
	@Override
	public int doEndTag() throws JspException {
        MessageSource messageSource = getRequestContext().getMessageSource();
        writeGoTopLink(messageSource);

        tagWriter.endTag();
        tagWriter.endTag();

		return EVAL_PAGE;
	}

	private void writeGoTopLink(MessageSource messageSource) throws JspException {
		if (isGoTopLink()) {
        	String goToTopLink = messageSource.getMessage("label.frame.goToTopLink", TagUtils.EMPTY_ARRAY, getRequestContext().getLocale());
        	String goToTopLinkTitle = messageSource.getMessage("label.frame.goToTopLinkTitle", TagUtils.EMPTY_ARRAY, getRequestContext().getLocale());
            tagWriter.startTag("div");
    		tagWriter.writeAttribute(CLASS_ATTRIBUTE, "goToTop");
            tagWriter.startTag("a");
    		tagWriter.writeAttribute("href", "#content");
    		tagWriter.writeAttribute("title", goToTopLinkTitle);
    		tagWriter.appendValue(goToTopLink);
            tagWriter.endTag();
            tagWriter.endTag();
        }
	}

	/**
	 * Clears the stored {@link TagWriter}.
	 */
	@Override
	public void doFinally() {
		super.doFinally();
		
		setCssClass(null);
		this.cmdAction1 = null;
		this.cmdLabel1 = null;
		this.cmdTitle1 = null;
		this.cmdAction2 = null;
		this.cmdLabel2 = null;
		this.cmdTitle2 = null;
		this.cmdAction3 = null;
		this.cmdLabel3 = null;
		this.cmdTitle3 = null;
		this.cmdAction4 = null;
		this.cmdLabel4 = null;
		this.cmdTitle4 = null;
		this.frameId = null;
	    this.legendLabel = null;
	    this.collapsable = false;
	    this.closeable = false;
	    this.goTopLink = false;
	    this.initialState = "expanded";
	    this.useFieldset = true;
		this.commandList = new ArrayList<FrameCommandTag>();
		this.tagWriter = null;
		this.modes = null;
		this.style = STYLE_PRIMARY;
	}

}
