/*
 * Copyright (c) 2006-2008 Maskat Project.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Maskat Project - initial API and implementation
 */
package org.maskat.core.event;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.maskat.core.DefChildIterator;

/**
 * The instance of this class represents the "eventDef" node of the event
 * definition file.
 */
public class EventDef extends AbstractEventElement {

	private String id;

	private RemoteUrl remoteUrl;

	private Map events;

	private Map eventRefs;

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getRemoteUrl() {
		return (remoteUrl == null) ? null : remoteUrl.getUrl();
	}

	public void setRemoteUrl(String remoteUrl) {
		if (this.remoteUrl == null) {
			addChild(new RemoteUrl());
		}
		this.remoteUrl.setUrl(remoteUrl);
	}

	/**
	 * Check if there are "equal" events, put them into eventRef
	 */
	public void shrinkEvent() {
		// Loop all the events
		if (this.getUnmodifiableChildren() == null) {
			return;
		}

		List eventList = new ArrayList();
		// Increasing refId
		int refId = 1;
		Iterator childrenIt = this.getUnmodifiableChildren().iterator();
		while (childrenIt.hasNext()) {
			Object child = childrenIt.next();
			if (child instanceof Component) {
				Component comp = (Component) child;
				Iterator it = comp.getUnmodifiableChildren().iterator();
				while (it.hasNext()) {
					Object compChild = it.next();
					if (compChild instanceof Event) {
						refId = processOneEvent(eventList, refId, (Event) compChild);
					}
				}
			}
		}
	}

	private int processOneEvent(List eventList, int refId, Event temp) {
		// put the event into a list
		int idx = eventList.indexOf(temp);
		if (idx != -1) {
			Event existed = (Event) eventList.get(idx);
			existed.removeAllChildren();
			if (existed.getRef() != null) {
				// There is already one eventRef.
				temp.setRef(existed.getRef());
			} else {
				String ref = "ref" + (refId++);
				existed.setRef(ref);
				temp.setRef(ref);

				EventRef eventRef = new EventRef();
				eventRef.setRefid(ref);
				Iterator childrenIt = temp.getUnmodifiableChildren().iterator();
				while (childrenIt.hasNext()) {
					eventRef.addChild(childrenIt.next());
				}
				// Add a eventRef to the Layout
				this.addChild(eventRef);
			}
			temp.removeAllChildren();
		} else {
			eventList.add(temp);
			temp.setRef(null);
			if (eventRefs != null && eventRefs.values() != null) {
				Iterator it = this.eventRefs.values().iterator();
				while (it.hasNext()) {
					EventRef eventRef = (EventRef) it.next();
					if (eventRef.contentEquals(temp)) {
						temp.setRef(eventRef.getRefid());
						temp.removeAllChildren();
					}
				}
			}
		}
		return refId;
	}

	/**
	 * イベント定義XMLに参照される画面オブジェクト名の集合を取得
	 */
	public Set getAllObjNames() {
		Set result = new HashSet();
		if (events != null) {
			for (Iterator it = events.values().iterator(); it != null && it.hasNext();) {
				Event event = (Event) it.next();
				event.getRelatedObjNames(result);
			}
		}
		if (eventRefs != null) {
			for (Iterator it = eventRefs.values().iterator(); it != null && it.hasNext();) {
				EventRef event = (EventRef) it.next();
				event.getRelatedObjNames(result);
			}
		}
		for (Iterator it = getTypedChildren(Component.class); it != null && it.hasNext();) {
			Component component = (Component) it.next();
			for (Iterator it2 = component.getAllEvents(); it2 != null && it2.hasNext();) {
				Event event = (Event) it2.next();
				event.getRelatedObjNames(result);
			}
		}
		return result;
	}

	public void addChild(Object child) {
		super.addChild(child);
		if (child instanceof Component) {
			addComponent((Component) child);
		}
		if (child instanceof Header) {
			addHeader((Header) child);
		}
		if (child instanceof Event) {
			addEvent((Event) child);
		}
		if (child instanceof RemoteUrl) {
			remoteUrl = (RemoteUrl) child;
		}
	}

	private void addComponent(Component comp) {
		if (comp == null || comp.getId() == null) {
			throw new IllegalArgumentException();
		}
	}

	private void addHeader(Header header) {
		if (header == null || header.getName() == null) {
//			throw new IllegalArgumentException();
		}
	}

	public Component findComponent(String id) {
		Iterator iter = getAllComponents();
		if (iter != null) {
			while (iter.hasNext()) {
				Component component = (Component) iter.next();
				if (id.equals(component.getId())) {
					return component;
				}
			}
		}
		return null;
	}
	
	public Iterator getAllComponents() {
		return this.getTypedChildren(Component.class);
	}

	public boolean hasComponents() {
		return (this.getTypedChildren(Component.class) == null) ? false : true;
	}

	private void addEvent(Event event) {
		if (event instanceof EventRef) {
			EventRef er = (EventRef) event;
			if (eventRefs == null) {
				eventRefs = new HashMap();
			}
			eventRefs.put(er.getRefid(), er);
			return;
		}
		// event定義
		if (this.events == null) {
			events = new HashMap();
		}
		events.put(event.getId(), event);

	}

	public Event findEvent(String id) {
		if (events == null) {
			return null;
		}
		return (Event) events.get(id);
	}

	public void removeEvent(String id) {
		if (events == null) {
			return;
		}
		events.remove(id);
	}

	public Iterator getAllEvents() {
		if (events == null) {
			return null;
		}
		return new DefChildIterator(this, events.values().iterator());
	}

	public Event findEventRef(String id) {
		if (eventRefs == null) {
			return null;
		}
		return (Event) eventRefs.get(id);
	}

	public Iterator getAllEventRefs() {
		if (eventRefs == null) {
			return null;
		}
		return new DefChildIterator(this, eventRefs.values().iterator());
	}

	/**
	 * Set the children of eventRef to the event that refers to it. For all
	 * event that have no param, add one param to it with enabled=false. For all
	 * event that have no result, add one result to it with enabled=false.
	 */
	public void prepareEvent() {
		// resultにあるcomponent/eventのeventEntityの評価(refがあるevent)
		Iterator compIt = getAllComponents();
		while (compIt != null && compIt.hasNext()) {
			Component comp = (Component) compIt.next();
			Iterator eventIt = comp.getAllEvents();
			while (eventIt != null && eventIt.hasNext()) {
				Event event = (Event) eventIt.next();
				if (event.getRef() != null) {
					Event evtRef = findEventRef(event.getRef());
					if (evtRef != null) {
						// ref参照する場合、子要素を無視
						event.removeAllChildren();
						// evtRefの子要素をeventにコピー
						for (Iterator it = evtRef.getChildren(); it != null
								&& it.hasNext();) {
							event.addChild(it.next());
						}
					}
				}
			}
		}
	}

}
