/* StubsComponent.java

	Purpose:
		
	Description:
		
	History:
		Sat Jun  4 21:24:11 TST 2011, Created by tomyeh

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

*/
package org.zkoss.zk.ui.sys;

import java.util.LinkedList;
import java.util.List;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.StubEvent;
import org.zkoss.zk.ui.ext.Scope;

/**
 * Represents a tree of {@link StubComponent} that are merged into
 * a single component.
 *
 * @author tomyeh
 * @since 6.0.0
 */
public class StubsComponent extends StubComponent {
	private String[] _uuids;
	/** [0]: uuid, [1]: id */
	private String[][] _idmap;
	/** [0]: uuid, [1]: EventListenerMap. */
	private Object[][] _evtmap;

	/** Called when this component replaced the given component,
	 * and the children of the given component shall be 'merged' to this component.
	 * @param replaced the component that this component will replace.
	 * @exception IllegalStateException if this method has been called twice
	 * (we can modify the algorithm to support but not worth).
	 */
	public void onChildrenMerged(Component replaced, boolean bListener) {
		if (_uuids != null)
			throw new IllegalStateException("called twice");

		final List<String> uuids = new LinkedList<String>();
		final List<String[]> idmap = new LinkedList<String[]>();
		final List<Object[]> evtmap = bListener ? new LinkedList<Object[]>() : null;
		final Page page = getPage();

		mapChildren(page != null ? (DesktopCtrl) page.getDesktop() : null, uuids, idmap, evtmap, replaced);

		_uuids = uuids.toArray(new String[uuids.size()]);
		_idmap = !idmap.isEmpty() ? idmap.toArray(new String[idmap.size()][]) : null;
		_evtmap = evtmap != null && !evtmap.isEmpty() ? evtmap.toArray(new Object[evtmap.size()][]) : null;
	}

	private void mapChildren(DesktopCtrl desktopCtrl, List<String> uuids, List<String[]> idmap, List<Object[]> evtmap,
			Component comp) {
		for (Component p = comp.getFirstChild(); p != null; p = p.getNextSibling()) {
			if (p instanceof StubsComponent) {
				final String[] kiduuids = ((StubsComponent) p)._uuids;
				if (kiduuids != null)
					for (String uuid : kiduuids) {
						uuids.add(uuid);
						if (desktopCtrl != null)
							desktopCtrl.mapComponent(uuid, this);
					}

				final String[][] kidids = ((StubsComponent) p)._idmap;
				if (kidids != null)
					for (String[] idinf : kidids)
						idmap.add(idinf);

				if (evtmap != null) {
					final Object[][] kidevts = ((StubsComponent) p)._evtmap;
					if (kidevts != null)
						for (Object[] evtinf : kidevts)
							evtmap.add(evtinf);
				}
			}

			final String uuid = p.getUuid();
			uuids.add(uuid);
			if (desktopCtrl != null)
				desktopCtrl.mapComponent(uuid, this);

			final String id = p.getId();
			if (id != null && id.length() > 0)
				idmap.add(new String[] { uuid, id });
			if (evtmap != null) {
				EventListenerMap em = ((ComponentCtrl) p).getEventListenerMap();
				if (em != null)
					evtmap.add(new Object[] { uuid, em });
			}

			mapChildren(desktopCtrl, uuids, idmap, evtmap, p); //recursive
		}
	}

	//--super--//
	public String getId(String uuid) {
		if (_idmap != null)
			for (String[] idinf : _idmap)
				if (uuid.equals(idinf[0]))
					return idinf[1];
		return super.getId(uuid);
	}

	public void onPageAttached(Page newpage, Page oldpage) {
		super.onPageAttached(newpage, oldpage);

		if (newpage != null) {
			final DesktopCtrl desktopCtrl = (DesktopCtrl) newpage.getDesktop();
			for (String uuid : _uuids)
				desktopCtrl.mapComponent(uuid, this);
		}
	}

	public void onPageDetached(Page page) {
		super.onPageDetached(page);

		final DesktopCtrl desktopCtrl = (DesktopCtrl) page.getDesktop();
		for (String uuid : _uuids)
			desktopCtrl.mapComponent(uuid, null);
	}

	public void service(Event event, Scope scope) throws Exception {
		final StubEvent stubevt = event instanceof StubEvent ? (StubEvent) event : null;
		final String uuid = stubevt != null ? stubevt.getUuid() : null;
		if (uuid == null || uuid.equals(getUuid())) {
			super.service(event, scope);
		} else if (_evtmap != null) {
			for (Object[] evtinf : _evtmap) {
				if (uuid.equals(evtinf[0])) { //matched
					((EventListenerMap) evtinf[1]).service(event, scope, this, stubevt.getCommand());
					break; //done
				}
			}
			postToNonStubAncestor(stubevt);
		}
	}

	/** Returns the widget class, "#stubs".
	 */
	public String getWidgetClass() {
		return "#stubs";
	}

	/** {@link StubsComponent} represents a collection of {@link StubComponent},
	 * so it does not allow any child.
	 */
	protected boolean isChildable() {
		return false;
	}

	public String toString() {
		final StringBuffer sb = new StringBuffer(super.toString());
		if (_uuids != null) {
			sb.append('(');
			for (int j = 0; j < _uuids.length; ++j) {
				if (j != 0)
					sb.append(", ");
				sb.append(_uuids[j]);
			}
			sb.append(')');
		}
		if (_idmap != null) {
			sb.append('(');
			for (int j = 0; j < _idmap.length; ++j) {
				if (j != 0)
					sb.append(", ");
				sb.append(_idmap[j][0]).append('=').append(_idmap[j][1]);
			}
			sb.append(')');
		}
		return sb.toString();
	}
}
