/*
 * This file is part of Nuts Framework.
 * Copyright (C) 2009 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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import nuts.core.lang.StringUtils;
import nuts.exts.xwork2.util.ContextUtils;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.components.Form;
import org.apache.struts2.components.UIBean;
import org.apache.struts2.util.MakeIterator;
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;


/**
 * Render an HTML pager.
 */
@StrutsTag(
		name = "pager",
		tldTagClass = "nuts.exts.struts2.views.jsp.ui.PagerTag",
		description = "Renders a pager navigator",
		allowDynamicAttributes = true)
public class Pager extends UIBean {

	private final static String DEFAULT_LINK_SIZE = "5";
	private final static String DEFAULT_COMMAND = "goto(#)";
	private final static String DEFAULT_LIMIT_LIST = "10,20,30,40,50,60,70,80,90,100";

	private final static String LABEL_EMPTY = "pager-label-empty";
	private final static String LABEL_ITEM = "pager-label-item";
	private final static String LABEL_PREV = "pager-label-prev";
	private final static String LABEL_NEXT = "pager-label-next";
	private final static String LABEL_LIMIT = "pager-label-limit";
	private final static String LIMIT_LIST = "pager-limit-list";
	private final static String LINK_SIZE = "pager-link-size";
	private final static String LINK_STYLE = "pager-link-style";

	protected final static String TEMPLATE = "n-pager";

	// attributes
	protected String command;
	protected String start;
	protected Object count;
	protected String limit;
	protected String total;

	protected String emptyText;
	protected String itemText;
	protected String prevText;
	protected String nextText;

	protected String linkSize;
	protected String linkStyle;

	protected String limitName;
	protected String limitLabel;
	protected Object limitList;
	protected String onLimitChange;

	private static int sequence = 0;

	/**
	 * @see UIBean
	 * @param stack value stack
	 * @param request http servlet request
	 * @param response http servlet response
	 */
	public Pager(ValueStack stack, HttpServletRequest request,
		HttpServletResponse response) {
		super(stack, request, response);
	}

	/**
	 * @return default template
	 */
	protected String getDefaultTemplate() {
		return TEMPLATE;
	}

	/**
	 * Override UIBean's implementation, such that component Html id is determined
	 * in the following order :-
	 * <ol>
	 *   <li>This component id attribute</li>
	 *   <li>pager_[an increasing sequential number]</li>
	 * </ol>
	 *
	 * @param form form
	 */
	protected void populateComponentHtmlId(Form form) {
		String tryId;
		if (id != null) {
			// this check is needed for backwards compatibility with 2.1.x
			tryId = findStringIfAltSyntax(id);
		} 
		else {
			tryId = "pager_" + (sequence++);
		}
		addParameter("id", tryId);
	}

	private int defi(Object i) {
		return i == null || (Integer)i < 0 ? 0 : (Integer)i;
	}
	
	/**
	 * Evaluate extra parameters
	 */
	protected void evaluateExtraParams() {
		super.evaluateExtraParams();

		TextProvider txt = ContextUtils.findTextProviderInStack(getStack());

		if (emptyText != null) {
			addParameter("emptyText", findString(emptyText));
		}
		else if (parameters.get("emptyText") == null) {
			addParameter("emptyText", txt.getText(LABEL_EMPTY, (String)null));
		}

		if (itemText != null) {
			addParameter("itemText", findString(itemText));
		}
		else if (parameters.get("itemText") == null) {
			addParameter("itemText", txt.getText(LABEL_ITEM, (String)null));
		}

		if (prevText != null) {
			addParameter("prevText", findString(prevText));
		}
		else if (parameters.get("prevText") == null) {
			addParameter("prevText", txt.getText(LABEL_PREV, (String)null));
		}

		if (nextText != null) {
			addParameter("nextText", findString(nextText));
		}
		else if (parameters.get("nextText") == null) {
			addParameter("nextText", txt.getText(LABEL_NEXT, (String)null));
		}

		if (linkSize != null) {
			addParameter("linkSize", findValue(linkSize, Integer.class));
		}
		if (parameters.get("linkSize") == null) {
			addParameter("linkSize", Integer.parseInt(txt.getText(LINK_SIZE, DEFAULT_LINK_SIZE)));
		}

		if (linkStyle != null) {
			addParameter("linkStyle", findString(linkStyle));
		}
		if (parameters.get("linkStyle") == null) {
			addParameter("linkStyle", txt.getText(LINK_STYLE, (String)null));
		}

		if (command != null) {
			addParameter("command", findString(command));
		}
		if (parameters.get("command") == null) {
			addParameter("command", DEFAULT_COMMAND);
		}

		if (limitLabel != null) {
			addParameter("limitLabel", findString(limitLabel));
		}
		else if (parameters.get("limitLabel") == null) {
			addParameter("limitLabel", txt.getText(LABEL_LIMIT, (String)null));
		}
		addParameter("limitName", limitName);
		addParameter("onLimitChange", onLimitChange);

		Object value;
		if (limitList == null) {
			limitList = parameters.get("limitList");
		}

		value = null;
		if (limitList instanceof String) {
			value = findValue((String) limitList);
		}
		else if (MakeIterator.isIterable(limitList)) {
			value = limitList;
		}
		else {
			value = StringUtils.parseCsv(txt.getText(LIMIT_LIST, DEFAULT_LIMIT_LIST));
		}
		addParameter("limitList", value);

		Integer istart = 0;
		Integer ilimit = 0;
		Integer itotal = 0;
		Integer icount = 0;

		if (start != null) {
			istart = defi(findValue(start, Integer.class));
		}
		if (limit != null) {
			ilimit = defi(findValue(limit, Integer.class));
		}
		if (total != null) {
			itotal = defi(findValue(total, Integer.class));
		}
		if (count instanceof String) {
			icount = defi(findValue((String)count, Integer.class));
		}
		else if (count instanceof Integer) {
			icount = (Integer)count;
		}

		if (itotal > 0) {
			Integer ic2 = (ilimit < 1 ? itotal : ilimit);
			if (istart + ic2 > itotal) {
				ic2 = itotal - istart;
			}
			if (icount == null || icount <= 0 || icount > ic2) {
				icount = ic2;
			}
		}

		Integer ipages = 0;
		if (itotal > 0) {
			ipages = ilimit > 0 ? ((itotal - 1) / ilimit) + 1 : 1;
		}
		Integer ipage = ilimit > 0 ? (int)Math.ceil(istart.doubleValue() / ilimit) + 1 : 1;
		if (ipage == 1 && istart > 0) {
			ipage = 2;
			if (ipages == 1) {
				ipages = 2;
			}
		}

		addParameter("start", istart);
		addParameter("count", icount);
		addParameter("limit", ilimit);
		addParameter("total", itotal);

		addParameter("page", ipage);
		addParameter("pages", ipages);
	}

	/**
	 * @param emptyText the emptyText to set
	 */
	@StrutsTagAttribute(description = "the property for the emptyText")
	public void setEmptyText(String emptyText) {
		this.emptyText = emptyText;
	}

	/**
	 * @param itemText the itemText to set
	 */
	@StrutsTagAttribute(description = "the property for the itemText")
	public void setItemText(String itemText) {
		this.itemText = itemText;
	}

	/**
	 * @param prevText the prevText to set
	 */
	@StrutsTagAttribute(description = "the property for the prevText")
	public void setPrevText(String prevText) {
		this.prevText = prevText;
	}

	/**
	 * @param nextText the nextText to set
	 */
	@StrutsTagAttribute(description = "the property for the nextText")
	public void setNextText(String nextText) {
		this.nextText = nextText;
	}

	/**
	 * @param linkSize the linkSize to set
	 */
	@StrutsTagAttribute(description = "the property for the linkSize")
	public void setLinkSize(String linkSize) {
		this.linkSize = linkSize;
	}

	/**
	 * @param linkStyle the linkStyle to set
	 */
	@StrutsTagAttribute(description = "the property for the linkStyle")
	public void setLinkStyle(String linkStyle) {
		this.linkStyle = linkStyle;
	}

	/**
	 * @param limitLabel the limitLabel to set
	 */
	@StrutsTagAttribute(description = "the property for the limitLabel")
	public void setLabelLimit(String limitLabel) {
		this.limitLabel = limitLabel;
	}

	/**
	 * @param limitName the limitName to set
	 */
	@StrutsTagAttribute(description = "the property for the limitName")
	public void setLimitName(String limitName) {
		this.limitName = limitName;
	}

	/**
	 * @param limitList the limitList to set
	 */
	@StrutsTagAttribute(description = "the property for the limitList")
	public void setLimitList(Object limitList) {
		this.limitList = limitList;
	}

	/**
	 * @param onLimitChange the onLimitChange to set
	 */
	@StrutsTagAttribute(description = "the property for the onLimitChange")
	public void setOnLimitChange(String onLimitChange) {
		this.onLimitChange = onLimitChange;
	}

	/**
	 * @param command the command to set
	 */
	@StrutsTagAttribute(description = "the action property for the pager command")
	public void setCommand(String command) {
		this.command = command;
	}

	/**
	 * @param start the start to set
	 */
	@StrutsTagAttribute(description = "the property for the start")
	public void setStart(String start) {
		this.start = start;
	}

	/**
	 * @param count the count to set
	 */
	@StrutsTagAttribute(description = "the property for the count")
	public void setCount(Object count) {
		this.count = count;
	}

	/**
	 * @param limit the limit to set
	 */
	@StrutsTagAttribute(description = "the property for the limit")
	public void setLimit(String limit) {
		this.limit = limit;
	}

	/**
	 * @param total the total to set
	 */
	@StrutsTagAttribute(description = "the property for the total")
	public void setTotal(String total) {
		this.total = total;
	}

}
