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

import nuts.core.lang.StringEscapes;
import nuts.core.lang.Strings;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * Map of tag attributes, used for rendering the tags
 */
public class Attributes {

	private Map<String, String> attributes = new LinkedHashMap<String, String>();
	
	public Set<Entry<String, String>> entrySet() {
		return attributes.entrySet();
	}
	
	public Attributes add(String key, String value) {
		return add(key, value, true);
	}

	public Attributes add(String key, String value, boolean encode) {
		if (encode) {
			value = StringEscapes.escapeHtml(value);
		}
		attributes.put(key, value);
		return this;
	}

	/**
	 * Add a key/value pair to the attributes only if the value is not null.
	 * Value is html encoded
	 * 
	 * @param attrName attribute name
	 * @param paramValue value of attribute
	 * @return this
	 */
	public Attributes addIfExists(String attrName, Object paramValue) {
		return addIfExists(attrName, paramValue, true);
	}

	/**
	 * Add a key/value pair to the attributes only if the value is not null.
	 * 
	 * @param attrName attribute name
	 * @param paramValue value of attribute
	 * @param encode html encode the value
	 * @return this
	 */
	public Attributes addIfExists(String attrName, Object paramValue,
			boolean encode) {
		if (paramValue != null) {
			String val = paramValue.toString();
			if (Strings.isNotEmpty(val)) {
				if (encode) {
					val = StringEscapes.escapeHtml(val);
				}
				attributes.put(attrName, val);
			}
		}
		return this;
	}

	/**
	 * Add a key/value pair to the attributes only if the value is not null and
	 * is a boolean with a value of 'true'. Value is html encoded
	 * 
	 * @param attrName attribute name
	 * @param paramValue value of attribute
	 * @return this
	 */
	public Attributes addIfTrue(String attrName, Object paramValue) {
		if (isTrue(paramValue)) {
			attributes.put(attrName, Boolean.TRUE.toString());
		}
		return this;
	}

	public static boolean isTrue(Object paramValue) {
		return isTrue(paramValue, false);
	}

	public static boolean isTrue(Object paramValue, boolean def) {
		if (paramValue != null) {
			return ((paramValue instanceof Boolean && ((Boolean)paramValue).booleanValue())
					|| (Boolean.valueOf(paramValue.toString()).booleanValue()));
		}
		return def;
	}

	public Attributes commons(Map<String, Object> params) {
		addIfExists("accesskey", params.get("accesskey"));
		return this;
	}

	@SuppressWarnings("unchecked")
	public Attributes dynamics(Map<String, Object> params) {
		Map<String, Object> das = (Map<String, Object>)params.get("dynamicAttributes");
		if (das != null) {
			for (Entry<String, Object> e : das.entrySet()) {
				addIfExists(e.getKey(), e.getValue());
			}
		}
		return this;
	}

	public Attributes events(Map<String, Object> params) {
		addIfExists("onclick", params.get("onclick"));
		addIfExists("ondblclick", params.get("ondblclick"));
		addIfExists("onmousedown", params.get("onmousedown"));
		addIfExists("onmouseup", params.get("onmouseup"));
		addIfExists("onmouseover", params.get("onmouseover"));
		addIfExists("onmousemove", params.get("onmousemove"));
		addIfExists("onmouseout", params.get("onmouseout"));
		addIfExists("onfocus", params.get("onfocus"));
		addIfExists("onblur", params.get("onblur"));
		addIfExists("onkeypress", params.get("onkeypress"));
		addIfExists("onkeydown", params.get("onkeydown"));
		addIfExists("onkeyup", params.get("onkeyup"));
		addIfExists("onselect", params.get("onselect"));
		addIfExists("onchange", params.get("onchange"));
		return this;
	}

	public Attributes name(Map<String, Object> params) {
		addIfExists("name", params.get("name"));
		return this;
	}

	public Attributes id(Map<String, Object> params) {
		addIfExists("id", params.get("id"));
		return this;
	}

	public Attributes css(AbstractTemplateRenderer tr) {
		return css(tr, null);
	}
	
	@SuppressWarnings("unchecked")
	public Attributes css(AbstractTemplateRenderer tr, String cdef) {
		Map<String, Object> params = tr.params;

		String name = (String)params.get("name");
		String cssClass = (String)params.get("cssClass");
		if (cdef != null) {
			cssClass = cdef + (Strings.isEmpty(cssClass) ? "" : ' ' + cssClass);
		}
		String cssErrorClass = (String)params.get("cssErrorClass");
		String cssStyle = (String)params.get("cssStyle");
		String cssErrorStyle = (String)params.get("cssErrorStyle");
		
		Map<String, List<String>> errors = (Map<String, List<String>>)tr.findValue("fieldErrors");
		
		boolean hasFieldErrors = (name != null && errors != null && errors.get(name) != null);
		
		if (cssClass != null && !(hasFieldErrors && cssErrorClass != null)) {
			add("class", cssClass);
		}
		else if (cssClass != null && hasFieldErrors && cssErrorClass != null) {
			add("class", cssClass + ' ' + cssErrorClass);
		}
		else if (cssClass == null && hasFieldErrors && cssErrorClass != null) {
			add("class", cssErrorClass);
		}
		
		if (cssStyle != null && !(hasFieldErrors && cssErrorStyle != null)) {
			add("style", cssStyle);
		}
		else if (cssStyle != null && hasFieldErrors && cssErrorStyle != null) {
			add("style", cssStyle + ' ' + cssErrorStyle);
		}
		else if (cssStyle == null && hasFieldErrors && cssErrorStyle != null) {
			add("style", cssErrorStyle);
		}
		
		return this;
	}

	public Attributes cssClass(Map<String, Object> params) {
		addIfExists("class", params.get("cssClass"));
		return this;
	}

	public Attributes cssClass(Map<String, Object> params, String basic) {
		String cc = (String)params.get("cssClass");
		cc = basic + (Strings.isEmpty(cc) ? "" : ' ' + cc);
		add("class", cc);
		return this;
	}

	public Attributes cssStyle(Map<String, Object> params) {
		addIfExists("style", params.get("cssStyle"));
		return this;
	}

	public Attributes href(Map<String, Object> params) {
		addIfExists("href", params.get("href"), false);
		return this;
	}

	public Attributes title(Map<String, Object> params) {
		String title = (String)params.get("title");
		if (Strings.isEmpty(title)) {
			addIfExists("title", params.get("tooltip"));
		}
		else {
			add("title", title);
		}
		addIfExists("tooltip", params.get("tooltip"));
		return this;
	}

	public Attributes tooltip(Map<String, Object> params) {
		addIfExists("tooltip", params.get("tooltip"));
		return this;
	}

	public Attributes tabindex(Map<String, Object> params) {
		addIfExists("tabindex", params.get("tabindex"));
		return this;
	}

	public Attributes size(Map<String, Object> params) {
		addIfExists("size", params.get("size"));
		return this;
	}

	public Attributes nameValue(Map<String, Object> params) {
		add("value", (String)params.get("nameValue"));
		return this;
	}

	public Attributes nameValue(AbstractTemplateRenderer tr) {
		add("value", tr.nameValue(), true);
		return this;
	}

	public Attributes formatValue(AbstractTemplateRenderer tr) {
		add("value", tr.formatValue(), false);
		return this;
	}

	public Attributes accept(Map<String, Object> params) {
		addIfExists("accept", params.get("accept"));
		return this;
	}

	public Attributes readonly(Map<String, Object> params) {
		addIfTrue("readonly", params.get("readonly"));
		return this;
	}

	public Attributes disabled(Map<String, Object> params) {
		addIfTrue("disabled", params.get("disabled"));
		return this;
	}

	public Attributes onsubmit(Map<String, Object> params) {
		addIfExists("onsubmit", params.get("onsubmit"));
		return this;
	}

	public Attributes onreset(Map<String, Object> params) {
		addIfExists("onreset", params.get("onreset"));
		return this;
	}

	public Attributes action(Map<String, Object> params) {
		addIfExists("action", params.get("action"));
		return this;
	}

	public Attributes target(Map<String, Object> params) {
		addIfExists("target", params.get("target"));
		return this;
	}

	public Attributes enctype(Map<String, Object> params) {
		addIfExists("enctype", params.get("enctype"));
		return this;
	}

	public Attributes acceptcharset(Map<String, Object> params) {
		addIfExists("accept-charset", params.get("acceptcharset"));
		return this;
	}

	public Attributes labelFor(Map<String, Object> params) {
		addIfExists("for", params.get("for"));
		return this;
	}

	public Attributes maxlength(Map<String, Object> params) {
		addIfExists("maxlength", params.get("maxlength"));
		return this;
	}

	public Attributes src(Map<String, Object> params) {
		addIfExists("src", params.get("src"), false);
		return this;
	}

	public Attributes multiple(Map<String, Object> params) {
		addIfExists("multiple", params.get("multiple"), false);
		return this;
	}

	public Attributes cols(Map<String, Object> params) {
		addIfExists("cols", params.get("cols"), false);
		return this;
	}

	public Attributes rows(Map<String, Object> params) {
		addIfExists("rows", params.get("rows"), false);
		return this;
	}

	public Attributes wrap(Map<String, Object> params) {
		addIfExists("wrap", params.get("wrap"), false);
		return this;
	}
}
