public interface PulseInteractor {
	public void receiveProgram(long time, PulseInteractor source);

	public void sendProgram(long time, PulseInteractor source);

	public void receiveDigit(long time, long value, PulseInteractor source);

	public void sendDigit(long time, long value, PulseInteractor source);

	public boolean canReceiveDigit(long time, PulseInteractor source);

	public boolean canReceiveProgram(long time, PulseInteractor source);
} public class Connector extends EData implements PulseInteractor, EEventListener {

	// static key for observation messages
	public static final String CABLE_TRANSMITTION = "cable_transmittion"; //$NON-NLS-1$

	// static keys for pulse direction
	// private static final EnumSet<Tag> DIRECTION = EnumSet.of(Tag.IN, Tag.OUT,
// Tag.BOTH);

	// pulse direction of this connector
	private Tag _direction;

	// id of the connector, that we are connected with by a cable.
	// if no cable, then this is -1.
	private int _partner;

	// flag indicating whether this connector has a cable.
	// note: This is another thing than having a partner. If a cable ist just
	// created by users click, the connector is plugged, but the cable ends
	// at the mouse-dragging-point. So no partner in this case.
	private boolean _plugged = false;

	// timeslot where we got the last pulse.
	// note: if choose -1 for this, the connector will be painted in highlight
	// mode at startup. I don't know why.
	private long _lastPulse = -11;

	// value of the pulse. If this is a program connector, it is 0 or 1.
	// in case of a digit connector, it is according to the pulse transmission.
	private long _pulseValue = 0;

	// ============================ lifecycle
	// ===================================

	public Connector() {
		// empty constructor
	}

	public void setAttributes(Attributes attrs) {
		super.setAttributes(attrs);
		_direction = XMLUtil.parseEnum(attrs, Tag.IO, Tag.class);
		_partner = XMLUtil.parseInt(attrs, Tag.PARTNER);
		// _plugged = _partner >= 0;
		// note: the connector is plugged, when both partners are set to
		// the cable
	}

	// ================================ methods
	// =================================

	public String getAttributes() {
		return super.getAttributes() + XMLUtil.wrapAttribute(Tag.IO, _direction.toString())
				+ XMLUtil.wrapAttribute(Tag.PARTNER, Integer.toString(_partner));
	}

	public int getPartner() {
		return _partner;
	}

	public void setPartnerID(int id) {
		_partner = id;
	}

	public boolean isPlugged() {
		return _plugged;
	}

	public void setPlugged(boolean b) {
		if (_plugged != b) {
			_plugged = b;
			setChanged();
			notifyObservers(REPAINT);
		}
	}

	public Tag getDirection() {
		return _direction;
	}

	private boolean isSender() {
		return (_direction == Tag.OUT || _direction == Tag.BOTH);
	}

	private boolean isReceiver() {
		return (_direction == Tag.IN || _direction == Tag.BOTH);
	}

	private boolean isDigit() {
		return _type == EType.DIGIT_CONNECTOR || _type == EType.DIGIT_CONNECTOR_CROSS || _type == EType.INTER_CONNECTOR;
	}

	private boolean isProgram() {
		return _type == EType.PROGRAM_CONNECTOR;
	}

	public void setLastPulse(long time) {
		if (_lastPulse < time) {
			// adjust pulse time and value
			_lastPulse = time;

			// if we are hightlighting, call for repaint
			if ((boolean) Status.HIGHLIGHT_PULSE.getValue()) {
				setChanged();
				notifyObservers(EData.PAINT_IMMEDIATELY);
			}
			// set alarm clock for changing our highlightning in the following
			// eevent time slot.
			// note: maybe the highlightning-flag is not set now.
			// But if we are in stepping mode it might switched on before our
			// call-back occures. So we have to set alarm in any case.
			Configuration config = (Configuration) Status.CONFIGURATION.getValue();
			config.getCyclingLights().setAlarmClock(time, this);
		}
	}

	public long getLastPulse() {
		return _lastPulse;
	}

	// ========================= pulseInteractor methods
	// ========================

	/**
	 * @param time
	 * @param source
	 * @see eniac.data.PulseInteractor#canReceiveDigit(long,
	 *      eniac.data.PulseInteractor)
	 */
	public boolean canReceiveDigit(long time, PulseInteractor source) {
		return isReceiver() && isDigit() && ((PulseInteractor) getParent()).canReceiveDigit(time, this);
	}

	private long checkDigit(long time, long value) {

		// check, if this is the first pulse within this time slot
		if (_lastPulse < time) {

			// adjust pulse time and value
			setLastPulse(time);
			_pulseValue = value;

		}
		else {

			// there has been a pulse before. Adjust values
			long oldPulseValue = _pulseValue;
			_pulseValue |= value;
			value = _pulseValue ^ oldPulseValue;
		}
		return value;
	}

	/**
	 * @param time
	 * @param value
	 * @param source
	 * @see eniac.data.PulseInteractor#receive(long, int,
	 *      eniac.data.PulseInteractor)
	 */
	public void receiveDigit(long time, long value, PulseInteractor source) {

		// if value contains no pulse, there is nothing to do
		if (value == 0) {
			return;
		}

		// check value
		value = checkDigit(time, value);

		// transmitt pulse to parent
		// note: we already asked parent if it can receive
		if (value > 0) {
			((PulseInteractor) _parent).receiveDigit(time, value, this);
		}
	}

	/**
	 * @param time
	 * @param value
	 * @param source
	 * @see eniac.data.PulseInteractor#sendDigit(long, long, PulseInteractor)
	 */
	public void sendDigit(long time, long value, PulseInteractor source) {

		// if (getType() == EType.INTER_CONNECTOR) {
		//    System.out.println("jkjkj"); //$NON-NLS-1$
		// }

		// check that we potentially can send a digit pulse and haven't done
		// in current timeslice
		if (isDigit() && isSender() && value > 0) {

			// check value
			value = checkDigit(time, value);

			// if value contains no pulse, we don't need to send
			if (value == 0) {
				return;
			}

			// check, if we are plugged and have a valid partner
			if (_plugged && _partner >= 0) {

				// send pulse to partner if he can receive.
				Connector c = (Connector) getConfiguration().getIDManager().get(_partner);
				if (c.canReceiveDigit(time, this)) {
					c.receiveDigit(time, value, this);
				}
				// notify cable for highlightning
				setChanged();
				notifyObservers(CABLE_TRANSMITTION);
			}
		}
	}

	/**
	 * @param time
	 * @param source
	 * @see eniac.data.PulseInteractor#canReceiveProgram(long,
	 *      eniac.data.PulseInteractor)
	 */
	public boolean canReceiveProgram(long time, PulseInteractor source) {
		return isReceiver() && isProgram() && _lastPulse < time
				&& ((PulseInteractor) getParent()).canReceiveProgram(time, this);
	}

	/**
	 * @param time
	 * @param source
	 * @see eniac.data.PulseInteractor#receiveProgram(long,
	 *      eniac.data.PulseInteractor)
	 */
	public void receiveProgram(long time, PulseInteractor source) {

		// adjust pulse time
		setLastPulse(time);

		// transmitt pulse to parent.
		// note: it has already been checked, that parent can receive.
		// so we don't ask again.
		((PulseInteractor) _parent).receiveProgram(time, this);
	}

	/**
	 * @param time
	 * @param source
	 * @see eniac.data.PulseInteractor#sendProgram(long,
	 *      eniac.data.PulseInteractor)
	 */
	public void sendProgram(long time, PulseInteractor source) {

		// check if pulse can go and haven't already gone this time
		if (isProgram() && isSender() && _lastPulse < time) {

			// adjust pulse time
			setLastPulse(time);

			// if we are plugged and have a valid partner, transmitt pulse.
			if (_plugged && _partner >= 0) {

				// TODO: synchronize this. maybe cable is removed.
				Connector c = (Connector) getConfiguration().getIDManager().get(_partner);
				if (c.canReceiveProgram(time, this)) {
					c.receiveProgram(time, this);
				}
				// notify cable to paint
				setChanged();
				notifyObservers(CABLE_TRANSMITTION);
			}
		}
	}

	// ============================= event listening
	// ============================

	/**
	 * @param e
	 * @see eniac.simulation.EEventListener#process(eniac.simulation.EEvent)
	 */
	public void process(EEvent e) {
		if (e.type == EEvent.ALARM && (boolean) Status.HIGHLIGHT_PULSE.getValue()) {

			// we are called for downlightning
			setChanged();
			notifyObservers(EData.PAINT_IMMEDIATELY);
		}
	}
} public class CyclingLights extends EData implements Runnable, EEventListener, StatusListener, Observer {

	// ===================== eeventmanager fields //=======================

	// length of a single and an addition-cycle
	public static final int SINGLE_CYCLE = 10;

	public static final int ADDITION_CYCLE = 200;

	public static final int OFFSET_10P = 3;

	public static final int CPP_TIME = 170;

	// initial length and grow rate of event listeners
	private static final int ARRAY_LENGTH = 3;

	// prototype events for one addition cycle to be reused
	private EEvent[] _events;

	// 2-dim array of listeners
	private EEventListener[][] _listeners;

	private int[] _counters;

	// ================= simulator fields //==============

	private static final int ITERATION_0 = 0;

	private static final int ITERATION_1 = 1;

	private static final int ITERATION_2 = 2;

	private static final int ITERATION_INFINITY = 3;

	// flag indicating whether we should go on simulating.
	private Thread _thread;

	// maximum time until that events should be processed.
	// if there is continuous simulation (iteration-switch==infinity),
	// this will be Long.MAX_VALUE
	private long _stopTime = 0;

	// event queue to store our events
	private EventQueue _queue;

	// frequency we try to maintain during simulation
	private Frequency _frequency;

	// timestamps for computing the appropriate sleeping time between processing
	// two events
	private long _realTimestamp;

	private long _simTimestamp;

	// carry clear gate
	private boolean _ccg;

	// =============================== lifecycle
	// ================================

	/**
	 * @param type
	 */
	public CyclingLights() {
		// empty constructor
	}

	public void init() {
		super.init();

		// add as listener to heaters of cycling unit
		// EData unit =
		// getConfiguration().getGarten().getKind(EType.CYCLING_UNIT, 0);
		// Switch heaters = ((Unit) unit).getHeaters();
		// heaters.addObserver(this);

		// add to starter as busyListener
		Status.LIFECYCLE.addListener(this);

		// init eventqueue
		_queue = new EventQueue();

		// create and start simulation thread
		_thread = new Thread(this);
		_thread.start();

		// add ourself as listener to be notified, when new events should be
		// inserted.
		addEEventListener(this, EEvent.GENERATE_NEW);
		addEEventListener(this, EEvent.CCG_UP);
		addEEventListener(this, EEvent.CCG_DOWN);
	}

	public void dispose() {
		super.dispose();

		// if there are threads waiting, stop them.
		synchronized (this) {
			_thread = null;
			notifyAll();
		}
	}

	// =========================== private methods
	// ==============================

	private synchronized void simulate() {

		// check if there is work to do
		while (_queue.isEmpty() || Status.LIFECYCLE.getValue() != Manager.LifeCycle.RUNNING
				|| _queue.getFirst().time > _stopTime) {

			// if nothing to do, wait for new work
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			// check, if we should should stop simulation.
			// this will occure, when lifecycle is set to stopping
			if (_thread == null) {
				return;
			}
		}
		// remove the first event from the queue
		EEvent ev = _queue.removeFirst();

		// update time
		Status.SIMULATION_TIME.setValue(ev.time);

		// process event
		// check, if we have a listener specified
		// (this is the alarm clock case)
		if (ev.listener != null) {
			ev.listener.process(ev);
		}
		else {

			// otherwise look for registered listeners in our arrays.
			EEventListener[] array = _listeners[ev.type];
			for (int i = 0; i < _counters[ev.type]; ++i) {
				array[i].process(ev);
			}
		}

		// maybe sleep in order to maintain frequency.
		// don't sleep, if the following event has the same timestamp.
		if (_queue.getFirst().time > ev.time) {

			// compute time differences
			long realDiff = System.currentTimeMillis() - _realTimestamp;
			long simDiff = ev.time - _simTimestamp;

			// compute sleeping time
			long sleepTime = (long) (simToReal(simDiff) / _frequency.linear() - realDiff);

			// only sleep, if millis are positive
			if (sleepTime > 0) {
				try {
					wait(sleepTime);
				} catch (InterruptedException e) {
					// we should not be interrupted. In case of program exit,
					// we should be notified by propertyChange.
					e.printStackTrace();
				}
			}

			// adjust timestamps
			long realTime = System.currentTimeMillis();
			if (realTime - _realTimestamp > 100) {
				_realTimestamp = realTime;
				_simTimestamp = ev.time;
			}
		}
	}

	// ========================== public methods //=============================

	public void initEvents() {
		// init events.
		// create list for easy initialization
		List<EEvent> list = new LinkedList<>();

		// CPP
		list.add(new EEvent(CPP_TIME, EEvent.CPP));

		// 10P
		for (int i = 0; i < 10; ++i) {
			long time = i * SINGLE_CYCLE + OFFSET_10P;
			EEvent e = new EEvent(time, EEvent.PULSE_10P);
			list.add(e);
		}

		// 9P
		for (int i = 1; i < 10; ++i) {
			long time = i * SINGLE_CYCLE;
			EEvent e = new EEvent(time, EEvent.PULSE_9P);
			list.add(e);
		}

		// 1P
		list.add(new EEvent(10, EEvent.PULSE_1P));

		// 2P
		list.add(new EEvent(20, EEvent.PULSE_2P));
		list.add(new EEvent(30, EEvent.PULSE_2P));

		// 2'P
		list.add(new EEvent(40, EEvent.PULSE_2AP));
		list.add(new EEvent(50, EEvent.PULSE_2AP));

		// 4P
		list.add(new EEvent(60, EEvent.PULSE_4P));
		list.add(new EEvent(70, EEvent.PULSE_4P));
		list.add(new EEvent(80, EEvent.PULSE_4P));
		list.add(new EEvent(90, EEvent.PULSE_4P));

		// 1*P
		list.add(new EEvent(100, EEvent.PULSE_1AP));

		// CCG
		list.add(new EEvent(110, EEvent.CCG_UP));
		list.add(new EEvent(180, EEvent.CCG_DOWN));

		// RP
		list.add(new EEvent(130, EEvent.RP));
		list.add(new EEvent(190, EEvent.RP));

		// GENERATE_NEW
		list.add(new EEvent(ADDITION_CYCLE, EEvent.GENERATE_NEW));

		// NOP
		for (int i = 0; i < 20; ++i) {
			list.add(new EEvent(i * 10 + 6, EEvent.NOP));
		}
		for (int i = 10; i < 20; ++i) {
			list.add(new EEvent(i * 10 + 3, EEvent.NOP));
			list.add(new EEvent(i * 10 + 6, EEvent.NOP));
		}
		list.add(new EEvent(120, EEvent.NOP));
		list.add(new EEvent(140, EEvent.NOP));
		list.add(new EEvent(150, EEvent.NOP));
		list.add(new EEvent(160, EEvent.NOP));

		// init events array from list
		_events = new EEvent[list.size()];
		Collections.shuffle(list);
		list.toArray(_events);

		// insert events to simulator
		insertEvents(_events);
	}

	public void run() {
		while (_thread != null) {
			simulate();
		}
	}

	public synchronized void updateStopTime(int iterationMode) {
		switch (iterationMode) {
			case ITERATION_0 :
				// debug mode. Set stoptime to time of next event
				_stopTime = _queue.getFirst().time;
				break;

			case ITERATION_1 :
				// single single step mode. Icrease stoptime by single cycle.
				_stopTime += SINGLE_CYCLE;
				break;

			case ITERATION_2 :
				// addition cycle step mode. Increase stoptime by addition
// cycle.
				_stopTime += ADDITION_CYCLE;
				break;

			case ITERATION_INFINITY :
				// infinity. Nothing to do.
				break;
		}
		notifyAll();
	}

	public synchronized void setStopTime(long time) {
		_stopTime = time;
		notifyAll();
	}

	public synchronized void insertEvent(EEvent e) {
		_queue.insert(e);
		notifyAll();
	}

	public synchronized void insertEvents(EEvent[] events) {
		_queue.insert(events);
		notifyAll();
	}

	public synchronized void reset() {
		_queue.empty();
		_stopTime = 0L;
		// TODO: this is a hack, because there was a NuPoExc when changing
		// configuration. Find a proper way to init and dispose.
		if (Status.LIFECYCLE.getValue() == Manager.LifeCycle.RUNNING) {
			Status.SIMULATION_TIME.setValue(0L);
		}
		notifyAll();
	}

	public synchronized void setWantedFrequency(Frequency f) {
		_frequency = f;
		// notify because maybe our thread is sleeping for a long time
		// and in case of speed up we want to process events now.
		notifyAll();
	}

	public static long simToReal(long simTime) {
		return simTime * 5;
	}

	public void setAlarmClock(long time, EEventListener listener) {

		// compute next time slot for alarm
		if (time % 10 == 6) {
			time += 4;
		}
		else {
			time += 3;
		}
		// create and insert event
		EEvent e = new EEvent(time, EEvent.ALARM, listener);
		insertEvent(e);
	}

	public void addEEventListener(EEventListener listener, short eventType) {

		// check if initialized
		if (_listeners == null) {
			// init eeventListener array.
			// recurse on all event types
			Field[] fields = EEvent.class.getFields();
			int max = 0;
			for (int i = 0; i < fields.length; ++i) {
				if (Modifier.isFinal(fields[i].getModifiers())) {
					try {
						max = Math.max(max, fields[i].getInt(null));
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
			// length of arrays is maximum key + 1
			max += 1;
			_counters = new int[max];
			_listeners = new EEventListener[max][];
			for (int i = 0; i < _listeners.length; ++i) {
				_listeners[i] = new EEventListener[ARRAY_LENGTH];
			}
		}

		// check size
		int oldLength = _listeners[eventType].length;
		if (oldLength <= _counters[eventType]) {
			EEventListener[] temp = new EEventListener[oldLength + ARRAY_LENGTH];
			System.arraycopy(_listeners[eventType], 0, temp, 0, oldLength);
			_listeners[eventType] = temp;
		}
		// add listener
		_listeners[eventType][_counters[eventType]++] = listener;
	}

	public boolean isCCG() {
		return _ccg;
	}

	// ========================== eevent processing //==========================

	/**
	 * @param e
	 * @see eniac.simulation.EEventListener#process(eniac.simulation.EEvent)
	 */
	public void process(EEvent e) {

		// switch on the event type
		switch (e.type) {

			case EEvent.GENERATE_NEW :
				// adjust events for the new addition cycle
				for (int i = 0; i < _events.length; ++i) {
					_events[i].time += ADDITION_CYCLE;
				}
				// insert new events to the simulator.
				insertEvents(_events);
				break;

			case EEvent.CCG_UP :
				_ccg = true;
				break;

			case EEvent.CCG_DOWN :
				_ccg = false;
		}
	}

	@Override
	public synchronized void statusChanged(Status status, Object value) {

		// just notify simulation thread. This is useful, when the thread
		// is waiting for the runlevel to idle and go back to work.
		notifyAll();
	}

	/**
	 * @param o
	 * @param arg
	 * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
	 */
	public void update(Observable o, Object arg) {
		// adjust simulator and event processing to the new state of power
		// if (hasPower()) {
		// // if power on, then init simulator by inserting first set of events
		// getConfiguration().getCyclingLights().initEvents();
		// // update iteration. This will take place in case of infinity.
		// updateIteration();
		// } else {
		// // if power off, then reset simulator and array of events for reuse
		// getConfiguration().getCyclingLights().reset();
		// }
	}
} public class EData extends Observable implements Comparable<EData> {

	// static key indicating that that a repaint is recommended.
	public static final String REPAINT = "repaint"; //$NON-NLS-1$

	public static final String PAINT_IMMEDIATELY = "paint_immediately"; //$NON-NLS-1$

	public enum Tag {
		// tags
		ENIAC, PATH, NAME,

		// attributes
		ID, VALUE, NUMBER, POWER, PARTNER, LOCATION, IO, GRID, SIZE, INDEX, FLAG,

		// attribute values
		IN, OUT, BOTH, FALSE, TRUE,
	}

	// ============================= fields
	// =====================================

	// type of this dataObject as encoded by class DataTypes.
	// parsed from xml-tag
	protected EType _type;

	// unique id of this dataObject. All dataObjects have different ids
	// parsed from xml-attributes
	protected int _id;

	// name of this dataObject.
	// parsed from xml-attributes
	protected String _name;

	// numbers in parent's grid for layouting
	// parsed from xml-attributes
	protected int[] _gridNumbers;

	// index at parent.
	// this is not necessarily the same index as in the kindergarten.
	// two children may share the same index
	// (eg. two programconnectors in a tray)
	protected int _index;

	// parent dataObject. Configuration is root dataObject.
	protected ParentData _parent = null;

	// ============================= lifecycle
	// ==================================

	// default Constructor
	public EData() {
		// empty constructor
	}

	// constructor used by the parser's default handler
	public void setAttributes(Attributes attrs) {
		_id = XMLUtil.parseInt(attrs, Tag.ID);
		_index = XMLUtil.parseInt(attrs, Tag.INDEX);
		_name = XMLUtil.parseString(attrs, Tag.NAME);
		_gridNumbers = XMLUtil.parseIntArray(attrs, Tag.GRID);
	}

	/**
	 * initializes this dataObject. This default method just does nothing and
	 * should be overwritten by subclasses in case.
	 */
	public void init() {
		// register at idManager
		getConfiguration().getIDManager().put(this);
	}

	public void dispose() {
		// unregister at idManager
		getConfiguration().getIDManager().remove(_id);
	}

	// =========================== getter and setter
	// ============================

	public int getID() {
		return _id;
	}

	public void setID(int id) {
		_id = id;
	}

	public String getName() {
		return _name;
	}

	public void setName(String name) {
		_name = name;
	}

	public ParentData getParent() {
		return _parent;
	}

	public void setParent(ParentData pd) {
		_parent = pd;
	}

	public void setType(EType type) {
		_type = type;
	}

	public EType getType() {
		return _type;
	}

	public int[] getGridNumbers() {
		return _gridNumbers;
	}

	public void setIndex(int index) {
		_index = index;
	}

	public int getIndex() {
		return _index;
	}

	// =============================== methods
	// ==================================

	public Configuration getConfiguration() {
		return getParent().getConfiguration();
	}

	public boolean hasPower() {
		EData parent = getParent();
		if (parent instanceof Configuration) {
			Configuration config = (Configuration) parent;
			Unit unit = config.getUnit(_gridNumbers[0]);
			return unit.hasPower();
		}
		return parent.hasPower();
	}

	/**
	 * Returns the name of this dataObject. Useful to add this to a JList.
	 * 
	 * @return the string containing the name of this dataObject.
	 */
	public String toString() {
		return _name;
	}

	public int compareTo(EData data) {
		// compare by type, then by index
		int i = _type.compareTo(data.getType());
		if (i == 0) {
			i = _index - data.getIndex();
		}
		return i;
	}

	public List<Property> getProperties() {
		List<Property> l = new LinkedList<>();
		l.add(new ConstantProperty(Tag.NAME.toString(), _name));
		l.add(new ConstantProperty(Tag.ID.toString(), Integer.toString(_id)));
		return l;
	}

	public void setProperties(List<Property> l) {
		for (Property p : l) {
			if (p.getName().equals(Tag.NAME.name().toLowerCase())) {
				_name = ((ConditionedProperty) p).getValue();
			}
		}
	}

	public EPanel makePanel() {
		EPanel panel = _type.makeEPanel();
		panel.setData(this);
		return panel;
	}

	protected void assertInit(EData data) {
		IDManager idManager = getConfiguration().getIDManager();
		if (!idManager.containsID(data)) {
			data.init();
		}
	}

	// ============================= observer stuff
	// =============================

	public void addObserverToTree(Observer o) {
		addObserver(o);
	}

	// =========================== xml methods
	// ==================================

	/**
	 * Returns a String representing all Attributes and values for xml output.
	 * Over here the basic attributes <code>id</code> and <code>name</code> are
	 * added. If subclasses have more attributes to encode, then they should
	 * overwrite this method the following way: <br>
	 * 1. do a super-call to this method <br>
	 * 2. append additional attributes to the returned string <br>
	 * 3. return the string to caller. <br>
	 * 
	 * @return a <code>String</code> containing all attributes and values.
	 */
	protected String getAttributes() {
		return XMLUtil.wrapAttribute(Tag.ID, Integer.toString(_id)) + XMLUtil.wrapAttribute(Tag.NAME, _name)
				+ XMLUtil.wrapAttribute(Tag.GRID, StringConverter.toString(_gridNumbers))
				+ XMLUtil.wrapAttribute(Tag.INDEX, Integer.toString(_index));
	}

	/**
	 * Returns a list containing all Tag and child-Tag of this dataObject.
	 * 
	 * @return a <code>List</code> containing all tags.
	 */
	public void appendTags(List<String> l, int indent) {

		// if low indentation level, write comment line
		if (indent <= 2) {
			XMLUtil.appendCommentLine(l, indent, getName());
		}
		// add open-close-tag, a tag that closes in the same line.
		l.add(getOpenCloseTag(indent));
	}

	protected String getOpenTag(int indent) {
		return XMLUtil.TABS[indent] + XMLUtil.wrapOpenTag(_type.name().toLowerCase() + getAttributes());
	}

	protected String getCloseTag(int indent) {
		return XMLUtil.TABS[indent] + XMLUtil.wrapCloseTag(_type.name().toLowerCase());
	}

	protected String getOpenCloseTag(int indent) {
		return XMLUtil.TABS[indent] + XMLUtil.wrapOpenCloseTag(_type.name().toLowerCase() + getAttributes());
	}
} public class ConstantTransmittionLights extends ParentData implements Observer {

	public static final String PAINT_LIGHTS = "PAINT_LIGHTS"; //$NON-NLS-1$

	public void init() {
		super.init();

		// observe the constant transmitter unit
		EData data = getUnit();
		assertInit(data);
		data.addObserver(this);

		// observe the constant lights
		data = getConfiguration().getGarten().getKind(EType.CONSTANT_2_LIGHTS, 0);
		assertInit(data);
		data.addObserver(this);
	}

	public boolean hasPower() {
		ConstantTransmitter1 unit = getUnit();
		return unit.hasPower() && unit.isTransmitting();
	}

	private ConstantTransmitter1 getUnit() {
		EData unit = getConfiguration().getGarten().getKind(EType.CONSTANT_TRANSMITTER_1_UNIT, 0);
		return (ConstantTransmitter1) unit;
	}

	/**
	 * @param o
	 * @param arg
	 * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
	 */
	public void update(Observable o, Object arg) {

		if (arg == PAINT_LIGHTS) {
			// set number to our ciphers
			EData[] ciphers = getGarten().getKinder(EType.CONSTANT_TRANSMITTION_CIPHER);
			ConstantTransmitter1 unit = getUnit();
			long number = unit.getNumber();
			for (int i = ciphers.length - 1; i >= 0; --i) {
				((Switch) ciphers[i]).setValue((int) (number % 10));
				number /= 10;
			}
			// set sign
			EData sign = getGarten().getKind(EType.CONSTANT_TRANSMITTION_SIGN, 0);
			((Switch) sign).setValue(unit.isNegative() ? 0 : 1);

			// notify for repaint
			setChanged();
			notifyObservers(EData.REPAINT);
		}
	}
} public class ProgramConnectorPair extends ParentData implements PulseInteractor {

	private long _lastPulse;

	public ProgramConnectorPair() {
		// empty
	}

	/**
	 * @param time
	 * @return @see eniac.data.PulseInteractor#canReceiveProgram(long)
	 */
	public boolean canReceiveProgram(long time, PulseInteractor source) {
		return _lastPulse < time && ((PulseInteractor) getParent()).canReceiveProgram(time, this);
	}

	/**
	 * @param time
	 * @param source
	 * @see eniac.data.PulseInteractor#receiveProgram(long,
	 *      eniac.data.PulseInteractor)
	 */
	public void receiveProgram(long time, PulseInteractor source) {
		// note: we already asked parent whether it can receive
		((PulseInteractor) _parent).receiveProgram(time, this);
		_lastPulse = time;
	}

	/**
	 * @param time
	 * @param source
	 * @see eniac.data.PulseInteractor#sendProgram(long,
	 *      eniac.data.PulseInteractor)
	 */
	public void sendProgram(long time, PulseInteractor source) {
		if (_lastPulse < time) {
			EData[] children = getGarten().getAllKinder();
			for (int i = 0; i < children.length; ++i) {
				Connector c = (Connector) children[i];
				c.sendProgram(time, this);
			}
			_lastPulse = time;
		}
	}

	/**
	 * @param time
	 * @param source
	 * @return @see eniac.data.PulseInteractor#canReceiveDigit(long,
	 *         eniac.data.PulseInteractor)
	 */
	public boolean canReceiveDigit(long time, PulseInteractor source) {
		return false;
	}

	/**
	 * @param time
	 * @param value
	 * @param source
	 * @see eniac.data.PulseInteractor#receiveDigit(long, int,
	 *      eniac.data.PulseInteractor)
	 */
	public void receiveDigit(long time, long value, PulseInteractor source) {
		// nop
	}

	/**
	 * @param time
	 * @param value
	 * @param source
	 * @see eniac.data.PulseInteractor#sendDigit(long, int,
	 *      eniac.data.PulseInteractor)
	 */
	public void sendDigit(long time, long value, PulseInteractor source) {
		// nop
	}
} public class Switch extends EData {

	protected int _value;

	private boolean _enabled = true;

	// ============================== lifecycle
	// =================================

	public Switch() {
		// empty
	}

	public void setAttributes(Attributes attrs) {
		super.setAttributes(attrs);

		// parse value from attributes
		String[] codes = _type.getCodes();
		int value = XMLUtil.parseInt(attrs, _type.getCodeName(), codes);

		// If value is in bounds, set it. Otherwise throw exception.
		if (isInbound(value)) {
			setValue(value);
		}
		else {
			throw new DataParsingException(value, _type.getCodeName(), getClass());
		}
	}

	// ============================== methods
	// ===================================

	public void setValue(int value) {
		if (_value != value) {
			_value = value;
			setChanged();
			notifyObservers(EData.REPAINT);
		}
	}

	public int getValue() {
		return _value;
	}

	public void toggleValue() {
		_value = 1 - _value;
		setChanged();
		notifyObservers(EData.REPAINT);
	}

	public boolean isValue() {
		return _value == 1;
	}

	public String encode() {
		String[] codes = _type.getCodes();
		return codes[_value];
	}

	protected boolean isInbound(int value) {
		String[] codes = _type.getCodes();
		return value >= 0 && value < codes.length;
	}

	public String getAttributes() {
		return super.getAttributes() + XMLUtil.wrapAttribute(_type.getCodeName(), encode().toString());
	}

	public List<Property> getProperties() {
		List<Property> l = super.getProperties();
		String[] codes = _type.getCodes();
		l.add(new ChoiceProperty(_type.getCodeName(), codes, _value));
		return l;
	}

	public void setProperties(List<Property> l) {
		for (Property p : l) {
			if (p.getName().equals(_type.getCodeName().name().toLowerCase())) {
				setValue(((ChoiceProperty) p).getSelection());
				// it.remove(); ===> need to remove ???
			}
		}
		super.setProperties(l);
	}

	public void setEnabled(boolean b) {
		_enabled = b;
	}

	public boolean isEnabled() {
		return _enabled;
	}
} public class SwitchAndFlag extends Switch {

	private static String[] FALSE_TRUE = {EData.Tag.FALSE.name().toLowerCase(), EData.Tag.TRUE.name().toLowerCase()};

	private boolean _flag;

	/**
	 * @param type
	 */
	public SwitchAndFlag() {
		// empty
	}

	public void setAttributes(Attributes attrs) {
		super.setAttributes(attrs);

		// parse clearCorrect from attributes
		// TODO: maybe include name of flag to etype
		_flag = XMLUtil.parseBoolean(attrs, Tag.FLAG);
	}

	public void rotateValue() {
		// System.out.println("rotate value: " + _value + " -> " + (_value +
		// 1));
		_value = (_value + 1) % _type.getCodes().length;
		if (_value == 0) {
			_flag = true;
		}
		setChanged();
		notifyObservers(EData.REPAINT);
	}

	public void toggleFlag() {
		_flag = !_flag;
		setChanged();
		notifyObservers(EData.REPAINT);
	}

	public boolean isFlag() {
		// System.out.println("flag is checked. Result=" + _flag);
		return _flag;
	}

	public void setFlag(boolean b) {
		if (_flag != b) {
			_flag = b;
			setChanged();
			notifyObservers(EData.REPAINT);
		}
	}

	public String getAttributes() {
		return super.getAttributes() + XMLUtil.wrapAttribute(Tag.FLAG, Boolean.toString(_flag));
	}

	public List<Property> getProperties() {
		List<Property> l = super.getProperties();
		l.add(new ChoiceProperty(Tag.FLAG.name(), FALSE_TRUE, _flag ? 1 : 0));
		return l;
	}

	public void setProperties(List<Property> l) {
		for (Property p : l) {
			if (p.getName().equalsIgnoreCase(Tag.FLAG.name())) {
				setFlag(((ChoiceProperty) p).getSelection() == 1);
// it.remove(); ===> need to remove from list ???
			}
		}
		super.setProperties(l);
	}

} public enum EType {

	/**
	 * A configuration of 3 units
	 */
	CONFIGURATION_3,

	/**
	 * A configuration of 4 units
	 */
	CONFIGURATION_4,

	/**
	 * A configuration of 8 units
	 */
	CONFIGURATION_8,

	/**
	 * A configuration of 12 units
	 */
	CONFIGURATION_12,

	/**
	 * A configuration of 16 units
	 */
	CONFIGURATION_16,

	/**
	 * A configuration of 20 units
	 */
	CONFIGURATION_20,

	/**
	 * the accumulator
	 */
	ACCUMULATOR_UNIT,

	/**
	 * the cycling unit
	 */
	CYCLING_UNIT,

	/**
	 * the initiating unit
	 */
	INITIATING_UNIT,

	/**
	 * the 1st constant transmitter unit
	 */
	CONSTANT_TRANSMITTER_1_UNIT,

	/**
	 * the 2nd constant transmitter unit
	 */
	CONSTANT_TRANSMITTER_2_UNIT,

	/**
	 * an upper trunk which is 2 units width
	 */
	UPPER_TRUNK_2,

	/**
	 * an upper trunk which is 4 units width
	 */
	UPPER_TRUNK_4,

	/**
	 * a lower trunk which is 3 units width
	 */
	LOWER_TRUNK_3,

	/**
	 * a lower trunk which is 4 units width
	 */
	LOWER_TRUNK_4,

	/**
	 * a tray (part of a trunk) which is 2 units width
	 */
	TRAY_2,

	/**
	 * a tray (part of a trunk) which is 3 units width
	 */
	TRAY_3,

	/**
	 * a tray (part of a trunk) which is 4 units width
	 */
	TRAY_4,

	/**
	 * a blinken lights of an accumulator
	 */
	BLINKEN_LIGHTS,

	/**
	 * the heaters switch of an accumulator (may display the accumulator icon)
	 */
	ACCU_HEATERS,

	/**
	 * a standard heaters switch
	 */
	HEATERS,

	/**
	 * a heaters switch that displays 0 - 1
	 */
	HEATERS_01,

	/**
	 * the step button of the cycling unit
	 */
	STEP_BUTTON,

	/**
	 * the go button of the initiating unit
	 */
	GO_BUTTON,

	/**
	 * the clear button of the initiating unit
	 */
	CLEAR_BUTTON,

	/**
	 * the clear button for the cycle counter
	 */
	CYCLE_COUNTER_CLEAR,

	/**
	 * the iteration switch of the cycling unit
	 */
	ITERATION_SWITCH,

	/**
	 * the significant figures switch of an accumulator
	 */
	SIGNIFICIANT_FIGURES_SWITCH,

	/**
	 * the operations switch of an accumulator
	 */
	OPERATION_SWITCH,

	/**
	 * the repeat switch of an accumulator
	 */
	REPEAT_SWITCH,

	/**
	 * a cipher of the cycle counter at the cycling unit
	 */
	CIPHER,

	/**
	 * a digit of the blinken lights
	 */
	BLINKEN_NUMBER_SWITCH,

	/**
	 * the sign of the blinken lights
	 */
	BLINKEN_SIGN_SWITCH,

	/**
	 * the cycling lights -- display the pulse
	 */
	CYCLING_LIGHTS,

	/**
	 * the go lights of the initiating unit
	 */
	GO_LIGHTS,

	/**
	 * a digit connector (10 digits + sign + carryover)
	 */
	DIGIT_CONNECTOR,

	/**
	 * a program connector (1 pulse)
	 */
	PROGRAM_CONNECTOR,

	/**
	 * an inter-connector (can connect 2 accumulator units)
	 */
	INTER_CONNECTOR,

	/**
	 * a digit connector in horizontal layout (e.g. at the edges of a tray)
	 */
	DIGIT_CONNECTOR_CROSS,

	/**
	 * a pair of program connectors (in + out)
	 */
	PROGRAM_CONNECTOR_PAIR,

	/**
	 * a cover, without functionality (accumulator, between heaters and
	 * significant figures switch)
	 */
	BLEND_8,

	/**
	 * a cover, without functionality (accumulator, above the repeat switches)
	 */
	BLEND_16,

	/**
	 * a cover, without functionality (constant transmitter panel)
	 */
	BLEND_A_10,

	/**
	 * the symbol for the cycling unit
	 */
	CYCLING_SYMBOL,

	/**
	 * the symbol for the initiating unit
	 */
	INITIATING_SYMBOL,

	/**
	 * the symbol for the constant transmitter unit 1
	 */
	CONSTANT_TRANSMITTER_1_SYMBOL,

	/**
	 * the symbol for the constant transmitter unit 2
	 */
	CONSTANT_TRANSMITTER_2_SYMBOL,

	/**
	 * the constant selector switch for A+B
	 */
	CONSTANT_SELECTOR_SWITCH_AB,

	/**
	 * the constant selector switch for C+D
	 */
	CONSTANT_SELECTOR_SWITCH_CD,

	/**
	 * the constant selector switch for E+F
	 */
	CONSTANT_SELECTOR_SWITCH_EF,

	/**
	 * the constant selector switch for G+H
	 */
	CONSTANT_SELECTOR_SWITCH_GH,

	/**
	 * the constant selector switch for J+K
	 */
	CONSTANT_SELECTOR_SWITCH_JK,

	/**
	 * a number switch at the constant transmitter unit 2
	 */
	CONSTANT_SWITCH,

	/**
	 * the image displayed at the initiating unit
	 */
	INITIATING_IMAGE,

	/**
	 * the frequency slider (cycling unit)
	 */
	FREQUENCY_SLIDER,

	/**
	 * the benchmark, displaying the number of addition cycles per second
	 */
	BENCHMARK,

	/**
	 * the addition cycle counter
	 */
	CYCLE_COUNTER,

	/**
	 * image to be used in the overview panel, as marker for the currently
	 * visible area
	 */
	XOR_IMAGE,

	/**
	 * toggle switch for the constant transmitter 2, row J, left
	 */
	CONSTANT_SIGN_TOGGLE_JL,

	/**
	 * toggle switch for the constant transmitter 2, row J, right
	 */
	CONSTANT_SIGN_TOGGLE_JR,

	/**
	 * toggle switch for the constant transmitter 2, row K, left
	 */
	CONSTANT_SIGN_TOGGLE_KL,

	/**
	 * toggle switch for the constant transmitter 2, row K, right
	 */
	CONSTANT_SIGN_TOGGLE_KR,

	/**
	 * the numbers display at the constant transmitter 2
	 */
	CONSTANT_2_LIGHTS,

	/**
	 * a decimal cipher of the constant transmitter 2 lights
	 */
	CONSTANT_BLINKEN_CIPHER,

	/**
	 * the +/- sign for a number at the constant transmitter 2 lights
	 */
	CONSTANT_BLINKEN_SIGN,

	/**
	 * a cipher displayed at constant transmitter 1, to be shown during a
	 * transmission
	 */
	CONSTANT_TRANSMITTION_CIPHER,

	/**
	 * a =/- sign displayed at constant transmitter 1, to be shown during a
	 * transmission
	 */
	CONSTANT_TRANSMITTION_SIGN,

	/**
	 * the lights at constant transmitter 1, will display number and sign during
	 * a transmission
	 */
	CONSTANT_TRANSMITTION_LIGHTS;

	public enum Tag {
		NAME, TYPE, MODEL, VIEW, CODE, CODES, VALUE;
	}

	private String _edataClass;

	private String _epanelClass;

	private EType.Tag _codeName;

	private String[] _codes;

	private Descriptor[] _descriptors;

	private Grid[] _gridCache;

	private EType() {
		_gridCache = new Grid[StringConverter.toInt(EProperties.getInstance().getProperty("GRID_CACHE_SIZE"))];
	}

	// ========================== getters and setters
	// ===========================

	public void setCodes(String[] codes) {
		_codes = codes;
	}

	public String[] getCodes() {
		return _codes;
	}

	public void setCodeName(EType.Tag codeName) {
		_codeName = codeName;
	}

	public EType.Tag getCodeName() {
		return _codeName;
	}

	public void setEDataClass(String edataClass) {
		_edataClass = edataClass;
	}

	public void setEPanelClass(String epanelClass) {
		_epanelClass = epanelClass;
	}

	public void setDescriptors(Descriptor[] descriptors) {
		// empty grid cache
		Arrays.fill(_gridCache, null);
		_descriptors = descriptors;
	}

	// ================================ methods
	// =================================

	public EData makeEData() throws InstantiationException, ClassNotFoundException, IllegalAccessException {

		EData edata = (EData) Class.forName(_edataClass).newInstance();
		edata.setType(this);
		return edata;
	}

	public EPanel makeEPanel() {
		try {
			return (EPanel) Class.forName(_epanelClass).newInstance();
		} catch (Exception e) {
			// System.out.println(_epanelClass);
			e.printStackTrace();
		}
		return null;
	}

	public Descriptor getDescriptor(int lod) {
		return _descriptors[lod];
	}

	public Grid getGrid(int width, int height, int lod) {
		int index = computeIndex(width, height);
		if (_gridCache[index] == null || _gridCache[index].width != width || _gridCache[index].height != height) {

			_gridCache[index] = getDescriptor(lod).makeGrid(width, height);
		}
		return _gridCache[index];
	}

	private int computeIndex(int width, int height) {
		return (width + height) % _gridCache.length;
	}
} public class EEvent {

	public static final short
	// event types
			GENERATE_NEW = 0,
			CPP = 1, PULSE_10P = 2, PULSE_9P = 3, PULSE_1P = 4, PULSE_2P = 5, PULSE_2AP = 6,
			PULSE_4P = 7,
			PULSE_1AP = 8, CCG_UP = 9, CCG_DOWN = 10, RP = 11, NOP = 12, ALARM = 13;

	/**
	 * Timestamp of this event
	 */
	public long time;

	/**
	 * Type of this event
	 */
	public short type;

	/**
	 * eevent listener for this event. If this is null, this event is dispatched
	 * by the eeventManager.dispatch(eevent) method.
	 */
	public EEventListener listener;

	public EEvent(long time, short type) {
		this.time = time;
		this.type = type;
	}

	public EEvent(long time, short type, EEventListener listener) {
		this.time = time;
		this.type = type;
		this.listener = listener;
	}

	public String toString() {
		return "[EEvent; time=" + time + ", type=" + type + "]"; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
	}
} public interface EEventListener {

	public void process(EEvent e);

}
import java.util.Observable;
import eniac.data.PulseInteractor;
import eniac.data.model.Connector;
import eniac.data.model.CyclingLights;
import eniac.data.model.EData;
import eniac.data.model.parent.ConstantTransmittionLights;
import eniac.data.model.parent.ProgramConnectorPair;
import eniac.data.model.sw.Switch;
import eniac.data.model.sw.SwitchAndFlag;
import eniac.data.type.EType;
import eniac.simulation.EEvent;
import eniac.simulation.EEventListener;
/*******************************************************************************
 * Copyright (c) 2003-2005, 2013 Till Zoppke.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 * 
 * Contributors:
 *     Till Zoppke - initial API and implementation
 ******************************************************************************/
/*
 * Created on 28.05.2004
 */
package eniac.data.model.unit;



/**
 * @author zoppke
 */
public class ConstantTransmitter1 extends Unit implements EEventListener, PulseInteractor {

	private static final int[] SEND_1P = {1, 3, 5, 7, 9};

	private static final int[] SEND_2P = {2, 3, 4, 5, 6, 7, 8, 9};

	private static final int[] SEND_2AP = {4, 5, 8, 9};

	private static final int[] SEND_4P = {6, 7, 8, 9};

	private boolean _transmitting;

	private long _startTime;

	private SwitchAndFlag _program;