/* BindListitemRenderer.java

	Purpose:
		
	Description:
		
	History:
		Aug 17, 2011 3:35:42 PM, Created by henrichen

Copyright (C) 2011 Potix Corporation. All Rights Reserved.
*/

package org.zkoss.bind.impl;

import java.io.Serializable;

import org.zkoss.bind.Binder;
import org.zkoss.bind.sys.BinderCtrl;
import org.zkoss.bind.sys.TemplateResolver;
import org.zkoss.lang.Objects;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.UiException;
import org.zkoss.zk.ui.util.ForEachStatus;
import org.zkoss.zk.ui.util.Template;
import org.zkoss.zul.Attributes;
import org.zkoss.zul.ListModel;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listgroup;
import org.zkoss.zul.Listgroupfoot;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.ListitemRenderer;

/**
 * Listitem renderer for binding.
 * @author henrichen
 * @since 6.0.0
 */
public class BindListitemRenderer extends AbstractRenderer implements ListitemRenderer<Object>, Serializable {
	private static final long serialVersionUID = 1463169907348730644L;

	public void render(final Listitem item, final Object data, final int index) throws Exception {
		final Listbox listbox = (Listbox) item.getParent();
		final int size = listbox.getModel().getSize();
		//B70-ZK-2555: in order to handle dynamic template name
		final String subtype = item instanceof Listgroup ? "group" : item instanceof Listgroupfoot ? "groupfoot" : null;
		final Template tm = resolveTemplate(listbox, item, data, index, size, "model", subtype);
		if (tm == null) {
			item.setLabel(Objects.toString(data));
			item.setValue(data);
		} else {
			final ForEachStatus iterStatus = new AbstractForEachStatus() { //provide iteration status in this context
				private static final long serialVersionUID = 1L;

				public int getIndex() {
					return index;
				}

				public Object getCurrent() {
					return data;
				}

				public Integer getEnd() {
					return size;
				}
			};

			final String var = (String) tm.getParameters().get(EACH_ATTR);
			final String varnm = var == null ? EACH_VAR : var; //var is not specified, default to "each"
			final String itervar = (String) tm.getParameters().get(STATUS_ATTR);
			final String itervarnm = itervar == null ? (var == null ? EACH_STATUS_VAR : varnm + STATUS_POST_VAR)
					: itervar; //provide default value if not specified

			//bug 1188, EL when nested var and itervar
			Object oldVar = listbox.getAttribute(varnm);
			Object oldIter = listbox.getAttribute(itervarnm);
			listbox.setAttribute(varnm, data);
			listbox.setAttribute(itervarnm, iterStatus);

			final Component[] items = filterOutShadows(listbox, tm.create(listbox, item, null, null));

			//Bug ZK-2789: do not use setAttribute when actually trying to removeAttribute
			if (oldVar != null) {
				listbox.setAttribute(varnm, oldVar);
			} else {
				listbox.removeAttribute(varnm);
			}
			if (oldIter != null) {
				listbox.setAttribute(itervarnm, oldIter);
			} else {
				listbox.removeAttribute(itervarnm);
			}

			if (items.length != 1)
				throw new UiException("The model template must have exactly one item, not " + items.length);

			final Listitem nli = (Listitem) items[0];
			nli.setAttribute(BinderCtrl.VAR, varnm); // for the converter to get the value

			// ZK-2552
			nli.setAttribute(AbstractRenderer.IS_TEMPLATE_MODEL_ENABLED_ATTR, true);

			nli.setAttribute(AbstractRenderer.CURRENT_INDEX_RESOLVER_ATTR, new IndirectBinding(data) {
				public Binder getBinder() {
					return BinderUtil.getBinder(nli, true);
				}

				protected ListModel getModel() {
					return listbox.getListModel();
				}

				public Component getComponent() {
					return nli;
				}
			});
			addItemReference(listbox, nli, index, varnm); //kept the reference to the data, before ON_BIND_INIT

			nli.setAttribute(itervarnm, iterStatus);

			//sync open state
			if (nli instanceof Listgroup && item instanceof Listgroup) {
				((Listgroup) nli).setOpen(((Listgroup) item).isOpen());
			}

			//ZK-1787 When the viewModel tell binder to reload a list, the other component that bind a bean in the list will reload again
			//move TEMPLATE_OBJECT (was set in resoloveTemplate) to current for check in addTemplateTracking
			Object obj = item.removeAttribute(TemplateResolver.TEMPLATE_OBJECT);
			//Bug ZK-2789: do not use setAttribute when actually trying to removeAttribute
			if (obj != null) {
				nli.setAttribute(TemplateResolver.TEMPLATE_OBJECT, obj);
			} else {
				nli.removeAttribute(TemplateResolver.TEMPLATE_OBJECT);
			}

			//add template dependency
			addTemplateTracking(listbox, nli, data, index, size);

			if (nli.getValue() == null) //template might set it
				nli.setValue(data);
			item.setAttribute(Attributes.MODEL_RENDERAS, nli);
			//indicate a new item is created to replace the existent one
			item.detach();
		}
	}
}
