/*
 * 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.layout;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;

import org.maskat.core.event.Event;
import org.maskat.core.event.EventDef;
import org.maskat.core.event.Source;
import org.maskat.core.event.Target;

public abstract class AbstractComponent extends AbstractLayoutElement
		implements Component {

	private String name;

	private int tabIndex;

	private int left;

	private int top;

	private int width;

	private int height;

	/** モデルの状態変化を通知するリスナのリスト */
	private PropertyChangeSupport listeners = new PropertyChangeSupport(this);

	public AbstractComponent() {
		super();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.screendef.IComponentDef#getName()
	 */
	public String getName() {
		return name;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.screendef.IComponentDef#setName(java.lang.String)
	 */
	public void setName(String name) {
		String oldName = this.name;
		this.name = name;
		updateComponentIdInEvent(oldName, name);
		firePropertyChange("name", oldName, name);
	}

	public int getTabIndex() {
		return tabIndex;
	}

	public void setTabIndex(int tabIndex) {
		this.tabIndex = tabIndex;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.screendef.IComponentDef#getHeight()
	 */
	public int getHeight() {
		return height;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.screendef.IComponentDef#setHeight(int)
	 */
	public void setHeight(int height) {
		int oldHeight = this.height;
		this.height = height;
		if (oldHeight != height) {
			firePropertyChange("constraint", "", null);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.screendef.IComponentDef#getLeft()
	 */
	public int getLeft() {
		return left;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.screendef.IComponentDef#setLeft(int)
	 */
	public void setLeft(int left) {
		int oldLeft = this.left;
		this.left = left;
		if (oldLeft != left) {
			firePropertyChange("constraint", "", null);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.screendef.IComponentDef#getTop()
	 */
	public int getTop() {
		return top;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.screendef.IComponentDef#setTop(int)
	 */
	public void setTop(int top) {
		int oldTop = this.top;
		this.top = top;
		if (oldTop != top) {
			firePropertyChange("constraint", "", null);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.screendef.IComponentDef#getWidth()
	 */
	public int getWidth() {
		return width;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.screendef.IComponentDef#setWidth(int)
	 */
	public void setWidth(int width) {
		int oldWidth = this.width;
		this.width = width;
		if (oldWidth != width) {
			firePropertyChange("constraint", "", null);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.screendef.IComponentDef#setConstraint(int, int,
	 *      int, int)
	 */
	public void setConstraint(int left, int top, int width, int height) {
		this.left = left;
		this.top = top;
		this.width = width;
		this.height = height;
		firePropertyChange("constraint", "", null);
	}

	public void addJavaScript(String content) {
		JavaScript javascriptDef = new JavaScript();
		javascriptDef.setContent(content);
		addChild(javascriptDef);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.screendef.IComponentDef#getParent()
	 */
	public Container getContainer() {
		if (getParent() instanceof Container) {
			return (Container) getParent();
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.core.layout.IComponent#getEventTypes()
	 */
	public String[] getEventTypes() {
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.core.layout.IComponent#hasEvent(java.lang.String)
	 */
	public boolean hasEvent(String eventType) {
		String[] eventTypes = getEventTypes();
		if (eventTypes != null && eventTypes.length > 0) {
			for (int i = 0; i < eventTypes.length; i++) {
				if (eventTypes[i].equals(eventType)) {
					return true;
				}
			}
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.core.layout.IComponent#getEvent(java.lang.String)
	 */
	public Event getEvent(String eventType) {
		if (hasEvent(eventType)) {
			EventDef eventDef = getLayout().getLayoutDef().getEventDef();

			org.maskat.core.event.Component component = eventDef.findComponent(name);
			if (component == null) {
				component = new org.maskat.core.event.Component();
				component.setId(name);
				eventDef.addChild(component);
			}

			Event event = component.findEvent(eventType);
			if (event == null) {
				event = new Event(eventType);
				component.addChild(event);
			}
			return event;
		}
		return null;
	}

	public void addPropertyChangeListener(PropertyChangeListener l) {
		listeners.addPropertyChangeListener(l);
	}

	public void firePropertyChange(String propName, Object oldValue, Object newValue) {
		listeners.firePropertyChange(propName, oldValue, newValue);
	}

	public void removePropertyChangeListener(PropertyChangeListener l) {
		listeners.removePropertyChangeListener(l);
	}

	public Object clone() throws CloneNotSupportedException {
		AbstractComponent def = (AbstractComponent) super.clone();
		def.listeners = new PropertyChangeSupport(def);
		return def;
	}
	
	private void updateComponentIdInEvent(String oldId, String newId) {
		if (oldId == null || oldId.equals(newId)) {
			return;
		}
		if (getLayout() == null || getLayout().getLayoutDef() == null
				|| getLayout().getLayoutDef().getEventDef() == null) {
			return;
		}
		
		EventDef eventDef = getLayout().getLayoutDef().getEventDef();
		org.maskat.core.event.Component component = eventDef.findComponent(oldId);
		if (component != null) {
			component.setId(newId);
		}
		
		//TODO should update all Source.obj , Source.idxRef, Target.in, Target.out who equals to oldId ?
		ArrayList sources = new ArrayList();
		getAllDescendants(eventDef, Source.class, sources);
		for (int i=0; i<sources.size(); i++) {
			Source source = (Source)sources.get(i);
			if (oldId.equals(source.getObj())) {
				source.setObj(newId);
			}
			if (oldId.equals(source.getIdxRef())) {
				source.setIdxRef(newId);
			}
		}
		ArrayList targets = new ArrayList();
		getAllDescendants(eventDef, Target.class, targets);
		for (int i=0; i<targets.size(); i++) {
			Target target = (Target)targets.get(i);
			if (oldId.equals(target.getIn())) {
				target.setIn(newId);
			}
			if (oldId.equals(target.getOut())) {
				target.setOut(newId);
			}
		}
	}

}
