package com.mindoo.ec09.example5.ca;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Stack;

/**
 * This class contains methods to separate the creation/queuing of property change events and
 * the listener notification. It's the base class of Java beans that handle the
 * communication between a CA-aware Eclipse viewpart and the property broker.
 */
public class PCSBean {
	public static boolean debug = false;

	private ArrayList   m_genericListeners;
	private HashMap     m_specificListerners;

	private Stack       m_queueEvent;
	private boolean		m_broadcast;

	public PCSBean() {
		m_genericListeners = new ArrayList();
		m_specificListerners = new HashMap();
		m_queueEvent = new Stack();
		m_broadcast = true;
	}

	private String shortName(Object o) {
		Class c;
		if (o instanceof Class)
			c = (Class)o;
		else
			c = o.getClass();
		String ret = c.getName();
		int off = ret.lastIndexOf(".");
		return ret.substring(off+1);
	}

	public void addPropertyChangeListener(String prop, java.beans.PropertyChangeListener pcl) {
		if (pcl == null)
			DebugUtils.println("addPropertyChangeListener - WTF???");
		synchronized (m_specificListerners) {
			ArrayList listeners = (ArrayList)m_specificListerners.get(prop);
			if (listeners == null) {
				listeners = new ArrayList();
				m_specificListerners.put(prop, listeners);
			}
			listeners.add(pcl);
		}
		if (debug)
			DebugUtils.println(shortName(pcl)+" listening for changes to "+prop+" on "+shortName(this));
	}
	public void addPropertyChangeListener(java.beans.PropertyChangeListener pcl) {
		if (pcl == null)
			DebugUtils.println("addPropertyChangeListener - wtf???");
		synchronized (m_genericListeners) {
			m_genericListeners.add(pcl);
		}
		if (debug)
			DebugUtils.println(shortName(pcl)+" listening for changes on "+shortName(this));
	}
	public void removePropertyChangeListener(java.beans.PropertyChangeListener pcl) {
		synchronized (m_genericListeners) {
			m_genericListeners.remove(pcl);
		}
		synchronized (m_specificListerners) {
			for (Iterator i = m_specificListerners.values().iterator(); i.hasNext(); ) {
				ArrayList listeners = (ArrayList)i.next();
				listeners.remove(pcl);
			}
		}
		if (debug)
			DebugUtils.println(shortName(pcl)+" no longer listening for changes on "+shortName(this));
	}

	private void pushChangeEvent(PropertyChangeEvent ev) {
		synchronized (m_queueEvent) {
			m_queueEvent.push(ev);
		}
	}

	private PropertyChangeEvent popChangeEvent() {
		synchronized (m_queueEvent) {
			return (PropertyChangeEvent)m_queueEvent.pop();
		}
	}

	protected void queuePropertyChange(String name, Object oldVal, Object newVal) {
		if (newVal == null) {
			if (oldVal == null)
				pushChangeEvent(null);
			else
				pushChangeEvent(new PropertyChangeEvent(this, name, oldVal, newVal));
		}
		else
			if (newVal.equals(oldVal))
				pushChangeEvent(null);
			else
				pushChangeEvent(new PropertyChangeEvent(this, name, oldVal, newVal));
	}

	protected void queuePropertyChange(String name, int oldVal, int newVal) {
		pushChangeEvent(new PropertyChangeEvent(this, name, new Integer(oldVal), new Integer(newVal)));
	}

	protected void queuePropertyChange(String name, long oldVal, long newVal) {
		pushChangeEvent(new PropertyChangeEvent(this, name, new Long(oldVal), new Long(newVal)));
	}

	protected void queuePropertyChange(String name, double oldVal, double newVal) {
		pushChangeEvent(new PropertyChangeEvent(this, name, new Double(oldVal), new Double(newVal)));
	}

	protected void queuePropertyChange(String name, boolean oldVal, boolean newVal) {
		pushChangeEvent(new PropertyChangeEvent(this, name, new Boolean(oldVal), new Boolean(newVal)));
	}

	private PropertyChangeListener[] getPropertyChangeListeners(String prop) {
		Object[] list1;
		synchronized (m_genericListeners) {
			list1 = m_genericListeners.toArray();
		}
		Object[] list2;
		synchronized (m_specificListerners) {
			ArrayList listeners = (ArrayList)m_specificListerners.get(prop);
			if (listeners != null)
				list2 = listeners.toArray();
			else
				list2 = new Object[0];
		}
		PropertyChangeListener[] ret = new PropertyChangeListener[list1.length + list2.length];
		System.arraycopy(list1, 0, ret, 0, list1.length);
		System.arraycopy(list2, 0, ret, list1.length, list2.length);
		return ret;
	}

	protected void firePropertyChange() {
		PropertyChangeEvent queueEvent = popChangeEvent();
		if (queueEvent == null)
			return;
		PropertyChangeListener[] pcls = getPropertyChangeListeners(queueEvent.getPropertyName());
		for (int i = 0; i < pcls.length; i++) {
			if (debug)
				DebugUtils.println(shortName(pcls[i])+" hears change on "+shortName(this)+" for "+queueEvent.getPropertyName());
			if (m_broadcast)
				pcls[i].propertyChange(queueEvent);
		}
		queueEvent = null;
	}

	public void fireMonotonicPropertyChange(String name, Object val) {
		queuePropertyChange(name, null, val);
		firePropertyChange();
	}

	public boolean isBroadcast() {
		return m_broadcast;
	}

	public void setBroadcast(boolean broadcast) {
		m_broadcast = broadcast;
	}
}