/*
 * UIProgressBar.java		Date created: 19.12.2007
 * Last modified by: $Author$
 * $Revision$	$Date$
 */

package org.richfaces.component;

import java.math.BigDecimal;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;
import javax.faces.event.FacesEvent;

import org.ajax4jsf.component.UIPoll;
import org.ajax4jsf.context.AjaxContext;
import org.ajax4jsf.context.AjaxContextImpl;
import org.ajax4jsf.event.AjaxEvent;
import org.ajax4jsf.javascript.JSLiteral;
import org.ajax4jsf.javascript.ScriptUtils;
import org.ajax4jsf.renderkit.AjaxRendererUtils;
import org.ajax4jsf.renderkit.RendererUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.richfaces.renderkit.AbstractProgressBarRenderer;

/**
 * Class provides base component class for progress bar
 * 
 * @author "Andrey Markavtsov"
 * 
 */
// TODO add @since declaration
public abstract class UIProgressBar extends UIPoll {

	/** Logger */
	private final Log logger = LogFactory.getLog(UIProgressBar.class);

	/** Component type */
	public static final String COMPONENT_TYPE = "org.richfaces.ProgressBar";

	/** Component family */
	public static final String COMPONENT_FAMILY = "org.richfaces.ProgressBar";

	/** Request parameter name containing component state to render */
	public static final String FORCE_PERCENT_PARAM = "forcePercent";

	/** Percent param name */
	private static final String PERCENT_PARAM = "percent";

	/** Max value attribute name */
	private static final String _maxValue = "maxValue";

	/** Min value attribute name */
	private static final String _minValue = "minValue";

	/** Enabled attribute name */
	private static final String _enabled = "enabled";

	/** Enabled attribute name */
	private static final String _interval = "interval";

	/** Complete class attribute name */
	private static final String _completeClass = "completeClass";

	/** Remain class attribute name */
	private static final String _remainClass = "remainClass";

	/** Style class attribute name */
	private static final String _styleClass = "styleClass";

	/** Markup data key */
	private static final String _markup = "markup";

	/** Context key */
	private static final String _context = "context";

	/**
	 * Method performs broadcasting of jsf events to progress bar component
	 * 
	 * @param event -
	 *            Faces Event instance
	 */
	public void broadcast(FacesEvent event) throws AbortProcessingException {

		FacesContext facesContext = FacesContext.getCurrentInstance();
		AjaxContext ajaxContext = AjaxContextImpl
				.getCurrentInstance(facesContext);
		Map<String, String> params = facesContext.getExternalContext().getRequestParameterMap();
		String clientId = this.getClientId(facesContext);

		if (!params.containsKey(clientId)) {
			return;
		}

		if (event instanceof ActionEvent) {
			ActionListener listeners[] = getActionListeners();
			for (ActionListener l : listeners) {
				l.processAction((ActionEvent) event);
			}
		} else if (event instanceof AjaxEvent) {

			if (!params.containsKey(FORCE_PERCENT_PARAM)
					&& params.containsKey(PERCENT_PARAM)) {
				Number value = getNumber(this.getAttributes().get(
						RendererUtils.HTML.value_ATTRIBUTE));
				ajaxContext.removeRenderedArea(clientId);
				ajaxContext
						.setResponseData(getResponseData(value, facesContext));

				AjaxRendererUtils.addRegionsFromComponent(this, facesContext);
			} else if (params.containsKey(FORCE_PERCENT_PARAM)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Force state: "
							+ this.getClientId(facesContext));
				}
				ajaxContext.addComponentToAjaxRender(this);
				String forcedState = params.get(FORCE_PERCENT_PARAM);
				if ("complete".equals(forcedState)) {
					Object reRender = this.getAttributes().get("reRenderAfterComplete");
					Set<String> ajaxRegions = AjaxRendererUtils.asSet(reRender);

					if (ajaxRegions != null) {
						for (Iterator<String> iter = ajaxRegions.iterator(); iter
								.hasNext();) {
							String id = iter.next();
							ajaxContext.addComponentToAjaxRender(this, id);
						}
					}
				}
			}
		}
	}
	
	/**
	 * Returns ajax response data
	 * 
	 * @param uiComponent
	 * @param percent
	 * @return
	 */
	private Map<Object, Object> getResponseData(Number value,
			FacesContext facesContext) {

		AbstractProgressBarRenderer renderer = (AbstractProgressBarRenderer) this
				.getRenderer(facesContext);

		Map<Object, Object> map = new HashMap<Object, Object>();
		map.put(RendererUtils.HTML.value_ATTRIBUTE, value);
		map.put(_interval, this.getInterval());

		if (this.getAttributes().get(RendererUtils.HTML.style_ATTRIBUTE) != null) {
			map.put(RendererUtils.HTML.style_ATTRIBUTE, this.getAttributes()
					.get(RendererUtils.HTML.style_ATTRIBUTE));
		}

		boolean enabled = (Boolean) this.getAttributes().get(_enabled);
		map.put(_enabled, Boolean.toString(enabled));

		if (!isSimple(renderer)) {
			map.put(_markup, getMarkup(facesContext, renderer));
			map.put(_context, getContext(renderer, value));
		}

		addStyles2Responce(map, _completeClass, this.getAttributes().get(
				_completeClass));
		addStyles2Responce(map, _remainClass, this.getAttributes().get(
				_remainClass));
		addStyles2Responce(map, _styleClass, this.getAttributes().get(
				_styleClass));
		return map;

	}

	/**
	 * Returns context for macrosubstitution
	 * 
	 * @param renderer
	 * @param percent
	 * @return
	 */
	private JSLiteral getContext(AbstractProgressBarRenderer renderer,
			Number percent) {
		StringBuffer buffer = new StringBuffer("{");
		buffer.append("value:");
		buffer.append(ScriptUtils.toScript(percent.toString())).append(",");
		buffer.append("minValue:");
		buffer.append(
				ScriptUtils.toScript(this.getAttributes().get(_minValue)
						.toString())).append(",");
		buffer.append("maxValue:");
		buffer.append(ScriptUtils.toScript(this.getAttributes().get(_maxValue)
				.toString()));

		String parameters = renderer.getParameters(this);
		if (parameters != null) {
			buffer.append(",");
			buffer.append(parameters);
		}
		buffer.append("}");
		return new JSLiteral(buffer.toString());
	}

	/**
	 * Return true if markup is simple
	 * 
	 * @return
	 */
	private boolean isSimple(AbstractProgressBarRenderer renderer) {
		return renderer.isSimpleMarkup(this);
	}

	/**
	 * Returns label markup
	 * 
	 * @param context
	 * @param renderer
	 * @return
	 */
	private JSLiteral getMarkup(FacesContext context,
			AbstractProgressBarRenderer renderer) {
		JSLiteral literal = null;
		try {
			StringBuffer markup = renderer.getMarkup(context, this);
			if (markup != null) {
				literal = new JSLiteral(markup.toString());
			}
		} catch (Exception e) {

		}
		return literal;
	}

	/**
	 * Add component classes to ajax response
	 * 
	 * @param buffer
	 * @param attr
	 * @param newValue
	 */
	private void addStyles2Responce(Map<Object, Object> map, String key,
			Object className) {
		if (className != null) {
			map.put(key, className);
		}
	}

	/**
	 * Converts value attr to number value
	 * 
	 * @param v -
	 *            value attr
	 * @return result
	 */
	public Number getNumber(Object v) {
		Number result = null;
		if (v != null) {
			try {
				if (v instanceof String) { // String
					result = Double.parseDouble((String) v);
				} else {
					Number n = (Number) v;
					if ((n instanceof BigDecimal) || (n instanceof Double) // Double
																			// or
																			// BigDecimal
							|| (n instanceof Float)) {
						result = n.floatValue();
					} else if (n instanceof Integer || n instanceof Long) { // Integer
						result = n.longValue();
					}
				}
			} catch (Exception e) {
				e.getMessage();
			}
			return result;
		}
		return new Integer(0);
	}

}
