/*
 * 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 java.lang.reflect.Array;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

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

import nuts.core.collections.CollectionUtils;
import nuts.core.util.Filter;
import nuts.exts.xwork2.TextProvider;
import nuts.exts.xwork2.util.ContextUtils;

import org.apache.struts2.components.Form;
import org.apache.struts2.components.UIBean;
import org.apache.struts2.util.ContainUtil;
import org.apache.struts2.util.MakeIterator;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;

import com.opensymphony.xwork2.util.ValueStack;

/**
 * Render an HTML List View.
 */
@StrutsTag(
	name = "listview", 
	tldTagClass = "nuts.exts.struts2.views.jsp.ui.ListViewTag", 
	description = "Renders a list view", 
	allowDynamicAttributes = true)
public class ListView extends UIBean {

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

	// attributes
	protected Object list;
	protected Object columns;
	protected Object querys;

	protected String start;
	protected String limit;
	protected String total;
	protected String sort;
	protected String dir;
	protected String filters;
	protected String query;

	protected String sortable;
	protected String cssColumn;
	protected String action;
	protected String method;
	protected String target;
	protected String onsubmit;
	protected String onreset;

	protected String autosize;
	protected String script;

	protected String pager;
	protected String tools;
	protected String addon;
	protected String headPager;
	protected String headTools;
	protected String headAddon;
	protected String footPager;
	protected String footTools;
	protected String footAddon;

	protected Object link;

	protected String singleSelect;
	protected String toggleSelect;
	protected String onrowclick;

	protected String checkAllToolButton;
	
	private static int sequence = 0;

	private Map boolFilterMap;
	private Map dateFilterMap;
	private Map stringFilterMap;
	private Map numberFilterMap;
	private Map queryKindMap;
	
	/**
	 * @see UIBean
	 * @param stack value stack
	 * @param request http servlet request
	 * @param response http servlet response
	 */
	public ListView(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
		super(stack, request, response);
	}

	/**
	 * getTextAsMap
	 * @param name resource name
	 * @return map value
	 */
	private Map getTextAsMap(String name) {
		ValueStack stack = getStack();
		TextProvider tp = ContextUtils.findValueInStack(stack, TextProvider.class);
		return tp.getTextAsMap(name, Collections.EMPTY_MAP);
	}

	/**
	 * getTextAsBoolean
	 * @param name resource name
	 * @param defv default value
	 * @return boolean value
	 */
	private Boolean getTextAsBoolean(String name, Boolean defv) {
		ValueStack stack = getStack();
		TextProvider tp = ContextUtils.findValueInStack(stack, TextProvider.class);
		return tp.getTextAsBoolean(name, defv);
	}

	/**
	 * @return the queryKindMap
	 */
	@Deprecated
	public Map getQueryKindMap() {
		if (queryKindMap == null) {
			queryKindMap = getTextAsMap("listview-query-kinds");
		}
		return queryKindMap;
	}

	/**
	 * @return the boolFilterMap
	 */
	public Map getBoolFilterMap() {
		if (boolFilterMap == null) {
			boolFilterMap = getTextAsMap("listview-filter-bools");
		}
		return boolFilterMap;
	}

	/**
	 * @return the dateFilterMap
	 */
	public Map getDateFilterMap() {
		if (dateFilterMap == null) {
			dateFilterMap = getTextAsMap("listview-filter-dates");
		}
		return dateFilterMap;
	}

	/**
	 * @return the numberFilterMap
	 */
	public Map getNumberFilterMap() {
		if (numberFilterMap == null) {
			numberFilterMap = getTextAsMap("listview-filter-numbers");
		}
		return numberFilterMap;
	}

	/**
	 * @return the stringFilterMap
	 */
	public Map getStringFilterMap() {
		if (stringFilterMap == null) {
			stringFilterMap = getTextAsMap("listview-filter-strings");
		}
		return stringFilterMap;
	}

	/**
	 * @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>list_[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 = "list_" + (sequence++);
		}
		addParameter("id", tryId);
	}

	/**
	 * Evaluate extra parameters
	 */
	protected void evaluateExtraParams() {
		super.evaluateExtraParams();

		Object value;

		// list
		if (list == null) {
			list = parameters.get("list");
		}

		value = null;
		if (list instanceof String) {
			if (!getParameters().containsKey("listName")) {
				addParameter("listName", list);
			}
			value = findValue((String) list);
		}
		else if (MakeIterator.isIterable(list)) {
			value = list;
		}
		addParameter("list", value);

		if (value instanceof Collection) {
			addParameter("listSize", new Integer(((Collection) value).size()));
		}
		else if (value instanceof Map) {
			addParameter("listSize", new Integer(((Map) value).size()));
		}
		else if (value != null && value.getClass().isArray()) {
			addParameter("listSize", new Integer(Array.getLength(value)));
		}
		else {
			addParameter("listSize", 0);
		}

		// columns
		if (columns == null) {
			columns = parameters.get("columns");
		}

		value = null;
		if (columns instanceof String) {
			value = findValue((String) columns);
		}
		else if (MakeIterator.isIterable(columns)) {
			value = columns;
		}

		if (value == null) {
			fieldError(
				"columns",
				"The requested columns '"
						+ columns
						+ "' could not be resolved as a collection/array/map/enumeration/iterator type.",
				null);
		}
		else {
			addParameter("columns", value);
		}

		// querys
		if (querys == null) {
			querys = parameters.get("querys");
		}

		if (querys instanceof String) {
			value = findValue((String)querys);
		}
		else {
			value = querys;
		}
		
		if (value instanceof Map) {
			addParameter("querys", value);
		}
		else if (querys != null) {
			fieldError(
				"querys",
				"The requested querys '"
						+ querys
						+ "' could not be resolved as a map type.",
				null);
		}

		// link
		if (link == null) {
			link = parameters.get("link");
		}

		value = null;
		if (link instanceof String) {
			value = findValue((String) link);
		}
		else {
			value = link;
		}
		addParameter("link", value);

		// other
		if (start != null) {
			addParameter("start", findString(start));
		}
		if (limit != null) {
			addParameter("limit", findString(limit));
		}
		if (total != null) {
			addParameter("total", findString(total));
		}
		if (sort != null) {
			addParameter("sort", findString(sort));
		}
		if (dir != null) {
			addParameter("dir", findString(dir));
		}
		if (filters != null) {
			addParameter("filters", findString(filters));
		}
		if (query != null) {
			addParameter("query", findString(query));
		}


		if (sortable != null) {
			addParameter("sortable", (Boolean)findValue(this.sortable, Boolean.class));
		}
		else {
			Boolean s = getTextAsBoolean("listview-sortable", null);
			if (s != null) {
				addParameter("sortable", s);
			}
		}
		if (checkAllToolButton != null) {
			addParameter("checkAllToolButton", findValue(checkAllToolButton, Boolean.class));
		}
		else {
			Boolean s = getTextAsBoolean("listview-checkAllToolButton", null);
			if (s != null) {
				addParameter("checkAllToolButton", s);
			}
		}

		if (cssColumn != null) {
			addParameter("cssColumn", findString(cssColumn));
		}
		if (action != null) {
			addParameter("action", findString(action));
		}
		if (method != null) {
			addParameter("method", findString(method));
		}
		if (target != null) {
			addParameter("target", findString(target));
		}
		if (onsubmit != null) {
			addParameter("onsubmit", findString(onsubmit));
		}
		if (onreset != null) {
			addParameter("onreset", findString(onreset));
		}
		if (autosize != null) {
			addParameter("autosize", findValue(autosize, Boolean.class));
		}
		if (script != null) {
			addParameter("script", findValue(script, Boolean.class));
		}

		if (pager != null) {
			addParameter("pager", findValue(pager, Boolean.class));
		}
		if (tools != null) {
			addParameter("tools", findString(tools));
		}
		if (addon != null) {
			addParameter("addon", findString(addon));
		}
		if (headPager != null) {
			addParameter("headPager", findValue(headPager, Boolean.class));
		}
		if (headTools != null) {
			addParameter("headTools", findString(headTools));
		}
		if (headAddon != null) {
			addParameter("headAddon", findString(headAddon));
		}
		if (footPager != null) {
			addParameter("footPager", findValue(footPager, Boolean.class));
		}
		if (footTools != null) {
			addParameter("footTools", findString(footTools));
		}
		if (footAddon != null) {
			addParameter("footAddon", findString(footAddon));
		}
		if (parameters.get("headPager") == null) {
			addParameter("headPager", parameters.get("pager"));
		}
		if (parameters.get("footTools") == null) {
			addParameter("footTools", parameters.get("tools"));
		}
		if (parameters.get("footAddon") == null) {
			addParameter("footAddon", parameters.get("addon"));
		}
		
		if (singleSelect != null) {
			addParameter("singleSelect", findValue(singleSelect, Boolean.class));
		}
		if (toggleSelect != null) {
			addParameter("toggleSelect", findValue(toggleSelect, Boolean.class));
		}
		if (onrowclick != null) {
			addParameter("onrowclick", findString(onrowclick));
		}
	}

	/**
	 * @param pqfs filters
	 * @return true if filters is empty
	 */
	public Map<String, Filter> getQueryFilters(String pqfs) {
		Map<String, Filter> fs = new HashMap<String, Filter>();
		
		Map<String, Filter> ofs = (Map<String, Filter>)stack.findValue(pqfs);
		if (CollectionUtils.isNotEmpty(ofs)) {
			for (Entry<String, Filter> en : ofs.entrySet()) {
				Filter f = en.getValue();
				if (f != null && CollectionUtils.isNotEmpty(f.getValues())) {
					fs.put(en.getKey(), f);
				}
			}
		}
		return fs;
	}
	
	/**
	 * @param obj1 object 1
	 * @param obj2 object 2
	 * @return true if obj2 exists in obj1
	 */
	public boolean contains(Object obj1, Object obj2) {
		return ContainUtil.contains(obj1, obj2);
	}

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

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

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

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

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

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

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

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

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

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

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

	/**
	 * @param sortable the sortable to set
	 */
	@StrutsTagAttribute(description = "the property for the sortable", type="Boolean")
	public void setSortable(String sortable) {
		this.sortable = sortable;
	}

	/**
	 * @param autosize the autosize to set
	 */
	@StrutsTagAttribute(description = "the property for the autosize", type="Boolean", defaultValue="true")
	public void setAutosize(String autosize) {
		this.autosize = autosize;
	}

	/**
	 * @param script the script to set
	 */
	@StrutsTagAttribute(description = "the property for the script", type="Boolean", defaultValue="true")
	public void setScript(String script) {
		this.script = script;
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	/**
	 * @param footAddon the footAddon to set
	 */
	@StrutsTagAttribute(description = "the property for the footAddon")
	public void setFootAddon(String footAddon) {
		this.footAddon = footAddon;
	}
	
	/**
	 * @param singleSelect the singleSelect to set
	 */
	@StrutsTagAttribute(description = "the property for the singleSelect")
	public void setSingleSelect(String singleSelect) {
		this.singleSelect = singleSelect;
	}

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

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

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