/*
 * Copyright 2006 Maskat Project.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.maskat.framework.eventdef;

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.framework.BasicDef;
import org.maskat.framework.DefChildIterator;

/**
 * The instance of this class represents the "eventDef" node of the event
 * definition file.
 * 
 * @author shengshen
 * 
 */
public class Layout extends BasicDef {
	private String id;

	private RemoteUrl remoteUrl;

	// private Map headers;

	private Map events;

	private Map eventRefs;

	// private Map components;

	/**
	 * Check if there are "equal" events, put them into eventRef
	 * 
	 */
	public void shrinkEvent() {
		List eventList = new ArrayList();
		int refId = 1;// Increasing refId
		// Loop all the events
		if (this.getUnmodifiableChildren() == null)
			return;

		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に参照される画面オブジェクト名の集合を取得<br>
	 */
	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();
		}
		// if (components == null)
		// components = new HashMap();
		// components.put(comp.getId(), comp);
	}

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

	public String getId() {
		return id;
	}

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

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

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

	public Component findComponent(String id) {
		for (Iterator it = getTypedChildren(Component.class); it != null && it.hasNext();) {
			Component component = (Component) it.next();
			if (id.equals(component.getId())) {
				return component;
			}
		}
		return null;
	}

	public Iterator getAllComponents() {
		return this.getTypedChildren(Component.class);
	}

	public boolean hasComponents() {
		if (this.getTypedChildren(Component.class) == null)
			return false;
		return 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 void addEventEntity(EventEntity eventEntity) {
	// if (eventRefs == null)
	// eventRefs = new HashMap();
	// eventRefs.put(eventEntity.getRefid(), eventEntity);
	// }

	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.getAllChildren(); it != null
								&& it.hasNext();) {
							event.addChild(it.next());
						}
					}
				}
			}
		}
	}

}
