package ckelling.baukasten;

import java.awt.*;
import java.lang.*;
import java.util.*;

// Versionsgeschichte
// 0.9.0, 04.03.97
// 0.9.1, 06.05.97
// 0.9.2, 13.06.97

/**
 *	Einfacher Bus.
 *
 *  Kann um beliebig viele Abzweigungen erweitert werden, ermittelt bei activate() oder write()
 *  selbsttätig seinen Wert, kann mehrere oder nur einen Punkt laufen lassen.
 *
 *	@author		Carsten Kelling
 *	@version	0.9.2, 13.06.97
 */
public class SimpleBus extends RechnerKomponente
{
	/**
	 * Flag: Keine besonderen Eigenschaften
	 */
	public final static int	NONE				= 0;

	/**
	 * Flag: Den Hauptstrang des Busses (im Gegensatz zu seinen Abzweigungen) schwarz zeichnen
	 */
	public final static int	BLACK_ROOT			= 1;

	/**
	 * Flag: An jeder Abzweigung, die nicht aktiviert ist, erscheint eine "Sperre".
	 * (als Symbol für ein sperrendes Tri-State-Gatter)
	 */
	public final static int	WITH_GATES			= 2;

	/**
     * Flag: An jeder Abzweigung erscheint ein Lötpunkt.
	 */
	public final static int	WITH_SOLDER_DOTS	= 4;

	protected final static int	START  = 8;
	protected final static int	END    = 9;
	protected final static int	EDGE   = 10;
	protected final static int	NOT_ACTIVE   = 11;

	protected final static int	LEFT   = 12;
	protected final static int	RIGHT  = 13;
	protected final static int	TOP    = 14;
	protected final static int	BOTTOM = 15;

	protected final static int  DIRECTION_DEFAULT = 16;
	protected final static int  DIRECTION_LEFT = 32;
	protected final static int  DIRECTION_RIGHT = 64;
	protected final static int  DIRECTION_UP = 128;
	protected final static int  DIRECTION_DOWN = 256;

	public	int 	startX;
	public	int 	startY;
	private int 	sizeX;
	private int 	sizeY;

	private Vector	edges;

	private String	label[];
	private Point	labelCoord[];
	private int		labelWidth[];
	private int		labelHeight;
	private int		labelPosX;
	private int		labelPosY;

	private int		value;

	private int 		dataSourceType;
	private int 		dataDestinationType;
	private String		dataSourceString;
	private String		dataDestinationString[];
	private boolean 	hasBeenActivated;
	private boolean 	dataFlowReverse;
	private boolean		destinationsInDifferentDirections;
	private int 		activationCounter;
	private int 		dotMoveCounter;
	private boolean 	moveDotMode;
	private Color       busColor;
	private boolean		blackRoot;
	private boolean		withGates;
	private boolean		withSolderDots;

	private Point       dataSource;
	private Point       dataSource2;
	private Point       dataDestination;
	private Point       dataDestination2;

	private String		oldSource;
	private String		oldDestination[];

	private Connection  neighbourAtStart;
	private Connection	neighbourAtEnd;

	private int 		lw;  // bekommt Wert von parent.LINEWIDTH
	private int 		sw;  // bekommt Wert von parent.STRIPEWIDTH

	/**
	 * Variablen für temporäre Inhalte - muß durch deren globale Deklaration weniger im Speicher aufgeräumt werden?
	 */
	private int			ax, ay, bx, by = 0;
	private int			size, size2 = 0;
	private int			ac = 0;
	private Graphics	offScreenGC = null;
	
	private Hashtable   imageCache;

	private Rechner 	parent;


	/**
	 * Einen neuen Bus mit Standard-Einstellungen (WITH_GATES) erzeugen.
	 *
	 * @param str Einzeiliger Titel des Busses
	 * @param x1  X-Koordinate
	 * @param y1  Y-Koordinate
	 * @param width Der Betrag von width wird Breite eines horizontalen Busses; wird als 0 angenommen, falls height > 0
	 * @param height Der Betrag von height wird Höhe eines vertikalen Busses; width wird ggf. ignoriert.
	 */
	public SimpleBus(String str, int x1, int y1, int width, int height, Rechner par)
	{
		String strArray[] = {str};
	    initialize(strArray, x1, y1, width, height, WITH_GATES, par);
	} /* end SimpleBus */


	/**
	 * Einen neuen Bus mit bestimmten Einstellungen erzeugen.
	 *
	 * @param str Einzeiliger Titel des Busses
	 * @param x1  X-Koordinate
	 * @param y1  Y-Koordinate
	 * @param width Der Betrag von width wird Breite eines horizontalen Busses; wird als 0 angenommen, falls height > 0
	 * @param height Der Betrag von height wird Höhe eines vertikalen Busses; width wird ggf. ignoriert.
	 * @param flags (NONE bzw. BLACK_ROOT) plus (WITH_GATES bzw. WITH_SOLDER_DOTS)
	 */
	public SimpleBus(String str, int x1, int y1, int width, int height, int flags, Rechner par)
	{
		String strArray[] = {str};
	    initialize(strArray, x1, y1, width, height, flags, par);
	} /* end SimpleBus */


	/**
	 * Einen neuen Bus mit eigenen Einstellungen und mehrzeiligem Titel erzeugen.
	 *
	 * @param strArray Mehrzeiliger Titel des Busses
	 * @param x1  X-Koordinate
	 * @param y1  Y-Koordinate
	 * @param width Der Betrag von width wird Breite eines horizontalen Busses; wird als 0 angenommen, falls height > 0
	 * @param height Der Betrag von height wird Höhe eines vertikalen Busses; width wird ggf. ignoriert.
	 * @param flags (NONE bzw. BLACK_ROOT) plus (WITH_GATES bzw. WITH_SOLDER_DOTS)
	 */
	public SimpleBus(String strArray[], int x1, int y1, int width, int height, int flags, Rechner par)
	{
	    initialize(strArray, x1, y1, width, height, flags, par);
	} /* end SimpleBus */


	private void initialize(String strArray[], int x1, int y1, int width, int height, int flags, Rechner par)
	{
		parent = par;
		lw = parent.LINEWIDTH;
		sw = parent.STRIPEWIDTH;

		value = -1;

		dataSourceType = NOT_ACTIVE;
		dataDestinationType = NOT_ACTIVE;
		dataSourceString = "";
		dataDestinationString = new String[2];
		dataDestinationString[0] = dataDestinationString[1] = "";
		hasBeenActivated = false;
		dataFlowReverse = false;
		destinationsInDifferentDirections = false;
		activationCounter = 0;
		dotMoveCounter = -(parent.LINEWIDTH+1);
		moveDotMode = false;
		dataSource = new Point(-7, -7);
		dataSource2 = new Point(-8, -8);
		dataDestination = new Point(-7, -7);
		dataDestination2 = new Point(-8, -8);
		oldSource = "";
		oldDestination = new String[2];
		oldDestination[0] = oldDestination[1] = "";

		neighbourAtStart = neighbourAtEnd = null;

		startX = x1;
		startY = y1;
		if (height != 0)
		// vertical bus
		{
			sizeX = 0;
			sizeY = Math.abs(height);
			setLabel(strArray, RIGHT, START);
		}
		else
		// horizontal bus
		{
			sizeX = Math.abs(width);
			sizeY = 0;
			setLabel(strArray, START, TOP);
		}

		if ((flags & BLACK_ROOT) == BLACK_ROOT)
		{
			blackRoot = true;
		    busColor = parent.BUS_COLOR;
		}
		else
		{
			blackRoot = false;
		    busColor = parent.EDGE_COLOR;
		}

		withGates = ((flags & WITH_GATES) == WITH_GATES);

		if ((flags & WITH_SOLDER_DOTS) == WITH_SOLDER_DOTS)
		{
			withGates = false;
			withSolderDots = true;
		}
		else
			withSolderDots = false;

		edges = new Vector(0,1); // (Initialgroesse, Zuwachs)

		imageCache = new Hashtable(40);
	} /* end initialize */


	/**
	 *  Ändert X- und Y-Koordinate, Breite und Höhe der Komponente.
	 *  grabMode wird ignoriert.
	 *
	 *  @param grabMode Wird ignoriert
	 *  @see #getPossibleQualifiers
	 *  @see #setCoordinates
	 *  @see #getCoordinates
	 */
	public void reshape(int x, int y, int width, int height, String grabMode)
	{
		reshape (x, y, width, height);
	}

	/**
	 *  Ändert X- und Y-Koordinate, Breite und Höhe der Komponente.
	 *  Von width und height wird zunächst der Betrag gebildet,
	 *  sind beide größer 0 wird width als 0 angenommen.
	 *  Abzweigungen des Busses bleiben in sich unverändert,
	 *  werden aber mitverschoben.
	 *
	 *  @param grabMode Wird ignoriert
	 *  @see #getPossibleQualifiers
	 *  @see #setCoordinates
	 *  @see #getCoordinates
	 */
	public void reshape(int x, int y, int width, int height)
	{
		if (x != startX || y != startY || width != sizeX || height != sizeY)
		{
			if (sizeX != width || sizeY != height)
				imageCache = new Hashtable(40);

			startX = x; startY = y;
			if (height != 0)
			// vertical bus
			{
				sizeX = 0;
				sizeY = Math.abs(height);
			}
			else
			// horizontal bus
			{
				sizeX = Math.abs(width);
				sizeY = 0;
			}

			setLabel(label, labelPosX, labelPosY);
		}
	} /* end reshape */

	/**
	 * @see #reshape
	 */
	public void move(int x, int y)
	{
		reshape(x, y, sizeX, sizeY);
	}

	/**
	 * @see #reshape
	 */
	public void resize(int width, int height)
	{
		reshape(startX, startY, width, height);
	}

	/**
	 * Liefert "true", falls p über dem Hauptstrang des Busses oder
	 * einer seiner Abzweigungen liegt.
	 */
	public boolean intersectsWith(Point p)
	{
		if (intersectsWithRoot(p))
			return true;
		else
			for (int i = 0; i < edges.size(); i++)
			{
				SimpleEdge se = (SimpleEdge) edges.elementAt(i);
				if (se.intersectsWith(p))
					return true;
			}

		return false;
	} /* end intersectsWith */

	/**
	 * Liefert nur "true", falls p über dem Hauptstrang des Busses liegt.
	 */
	private boolean intersectsWithRoot(Point p)
	{
		if (sizeX == 0)
		// vertical bus
		{
			if ( (p.x >= startX) && (p.x <= startX + lw) &&
			     (p.y >= startY) && (p.y <= startY + sizeY) )
				return true;
		}
		else
		// horizontal bus
		{
			if ( (p.x >= startX) && (p.x <= startX + sizeX) &&
			     (p.y >= startY) && (p.y <= startY + lw) )
				return true;
		}

		return false;
	}

	/**
	 * Liefert den Hilfetext, der angezeigt wird, wenn der Mauszeiger für
	 * Rechner.INFOTIP_DELAY ms unbewegt über dem Bus oder einer seiner Abzweigungen steht:
	 * Falls der Bus an dieser Stelle getrieben wird, wird sein Wert in hex./dez. Darstellung
	 * angezeigt, ansonsten "(nicht getrieben)".
	 *
	 * @see #setConnection
	 * @see #intersectsWith
	 */
	public String getInfoTipText(Point p)
	{
		if (isActivated())
		{
			String baseDesc, otherBaseDesc;
			int otherBase;
			if (parent.NUMBERBASE == 16)
			{
				baseDesc = " hex.";
				otherBaseDesc = " dez.";
				otherBase = 10;
			}
			else  // "10"
			{
				baseDesc = " dez.";
				otherBaseDesc = " hex.";
				otherBase = 16;
			}

			long maxValue = getMaxValue();
			String infoTipText = parent.expandToString(value, maxValue) + baseDesc + " / " + parent.expandToString(value, maxValue, otherBase) + otherBaseDesc;

			if (intersectsWithRoot(p))
				return infoTipText;

			if (dataSourceType == EDGE)
			{
				if (getEdge(dataSourceString).intersectsWith(p))
					return infoTipText;
			}

			if (dataDestinationType == EDGE)
			{
				if ( (! dataDestinationString[0].equals("")) && getEdge(dataDestinationString[0]).intersectsWith(p) )
					return infoTipText;
				else if ( (! dataDestinationString[1].equals("")) && getEdge(dataDestinationString[1]).intersectsWith(p) )
					return infoTipText;
			}

			return "(nicht getrieben)";  // diese Abzweigung des Busses wird nicht getrieben
		}
		else  // nicht aktiviert: kein Teil des Busses wird getrieben
			return "(nicht getrieben)";

	} /* end getInfoTipText */


	public void setConnection (Connection neighbour)
	{
		Rechner.out("FEHLENDER PARAMETER: SimpleBus.setConnection benötigt einen String-Parameter!");
	}

	/**
	 * Dient dazu, dem Bus mitzuteilen, welche Komponenten an ihm angeschlossen sind.
	 * Damit wird die Anzeige des aktuellen Wertes bei getInfoTipText() möglich.
	 *
	 * @param str "start", "end" oder Bezeichner einer Abzweigung
	 * @see #setConnection(String str, RechnerKomponentePanel rkp)
	 * @see #setConnection(String str, Editor_Source source)
	 */
	public void setConnection(String str, RechnerKomponente rk, int type)
	{
		setConnection(str, new Connection(rk, type));
	}

	/**
	 * Dient dazu, dem Bus mitzuteilen, welche Komponenten an ihm angeschlossen sind.
	 * Damit wird die Anzeige des aktuellen Wertes bei getInfoTipText() möglich.
	 *
	 * @param str "start", "end" oder Bezeichner einer Abzweigung
	 * @see #setConnection(String str, RechnerKomponente rk)
	 * @see #setConnection(String str, Editor_Source source)
	 */
	public void setConnection(String str, RechnerKomponentePanel rkp, int type)
	{
		setConnection(str, new Connection(rkp, type));
	}

	/**
	 * Dient dazu, dem Bus mitzuteilen, welche Komponenten an ihm angeschlossen sind.
	 * Damit wird die Anzeige des aktuellen Wertes bei getInfoTipText() möglich.
	 *
	 * @param str "start", "end" oder Bezeichner einer Abzweigung
	 * @see #setConnection(String str, RechnerKomponente rk)
	 * @see #setConnection(String str, Editor_Source source)
	 */
	public void setConnection(String str, Editor_Source source, int type)
	{
		setConnection(str, new Connection(source, type));
	}

	/**
	 * Dient dazu, dem Bus mitzuteilen, welche Komponenten an ihm angeschlossen sind.
	 * Damit wird die Anzeige des aktuellen Wertes bei getInfoTipText() möglich.
	 *
	 * @param str "start", "end" oder Bezeichner einer Abzweigung
	 * @see #setConnection(String str, RechnerKomponente rk)
	 * @see #setConnection(String str, RechnerKomponentePanel rkp)
	 */
	public void setConnection(String str, Connection neighbour)
	{
		if (str.equalsIgnoreCase("start"))
			neighbourAtStart = neighbour;
		else if (str.equalsIgnoreCase("end"))
			neighbourAtEnd = neighbour;
		else
		{
			SimpleEdge se = getEdge(str);
			if (se != null)
				se.setConnection(neighbour);
		}
	}


	/**
	 * Setzt den Wert des Busses. Dieser Wert wird, außer durch getInfoTipText(), niemals
	 * angezeigt, kann aber natürlich an andere Komponenten weitergereicht werden.
	 * Achtung! Der Wert des Busses wird ebenfalls durch activate() gesetzt, falls der Bus
	 * durch ein setConnection() "weiß", welche Komponente ihn treibt.
	 *
	 * @param newValue Der neue Wert
	 * @see #activate
	 * @see #deactivate
	 * @see #getValue
	 * @see #setConnection
	 */
	public void setValue(int newValue)
	{
		value = newValue;
	}

	/**
	 * Liefert den Wert des Busses; dieser wurde entweder durch setValue() eingestellt,
	 * oder bei activate() automatisch ermittelt.
	 *
	 * @see #activate
	 * @see #deactivate
	 * @see #setValue
	 * @see #setConnection
	 */
	public int getValue()
	{
		return value;
	}

	/**
	 * Liefert, falls bekannt, getMaxValue() der gerade den Bus
	 * treibenden Komponente, ansonsten Rechner.DEFAULT_MAXVALUE.
	 *
	 * @see #setConnection
	 */
	public long getMaxValue()
	{
		if (isActivated())
		{
			if ( (dataSourceType == START) && (neighbourAtStart != null) )
				return neighbourAtStart.getMaxValue();
			else if ( (dataSourceType == END) && (neighbourAtEnd != null) )
				return neighbourAtEnd.getMaxValue();
			else if (dataSourceType == EDGE)
				return getEdge(dataSourceString).getMaxValue();
			else
				return Rechner.DEFAULT_MAXVALUE;
		}
		else
			return Rechner.DEFAULT_MAXVALUE;
	}

	public void setLabel(String[] strArray)
	{
		setLabel(strArray, labelPosX, labelPosY);
	} /* end setLabel */

	/**
	 * Legt den Titel des Busses fest (pro Zeile ein String).
	 *
	 * @see #getLabel
	 */
	private void setLabel(String[] strArray, int labelPosX, int labelPosY)
	{
		this.labelPosX = labelPosX;
		this.labelPosY = labelPosY;

		if (strArray != null)
			label = strArray;
		else
		{
			label = new String[1];
			label[0] = "";
		}

		setLabelCoordinates(labelPosX, labelPosY);
	} /* end setLabel */

	private void setLabelCoordinates(int labelPosX, int labelPosY)
	{
		labelCoord = new Point[label.length];
		labelWidth = new int[label.length];
		labelHeight = parent.getHeight(parent.DIALOGFONT);

		int lineGap = labelHeight;
		Point coord;

		for (int i = 0; i < label.length; i++)
		{
			labelCoord[i] = new Point(0, 0);
			labelWidth[i] = parent.stringWidth(parent.DIALOGFONT, label[i]);

			if (sizeX == 0)
			// vertical bus
			{
				if (labelPosY == START)
					labelCoord[i].move(startX + lw + 5, startY + 10 + labelHeight + i*lineGap);
				else
					labelCoord[i].move(startX + lw + 5, startY + sizeY - 10 - (label.length-1 - i)*lineGap);

				if (labelPosX == LEFT)
					labelCoord[i].translate(-labelWidth[i] - lw - 10, 0);
			}
			else
			// horizontal bus
			{
				if (labelPosX == START)
					labelCoord[i].move(startX + 10, 					    startY - 5 - (label.length-1 - i)*lineGap);
				else
					labelCoord[i].move(startX + sizeX - labelWidth[i] - 10, startY - 5 - (label.length-1 - i)*lineGap);

				if (labelPosY == BOTTOM)
					labelCoord[i].translate(0, label.length*lineGap + lw + 10);
			}
		}

	} /* end setLabelCoordinates */


	/**
	 * Zeichnet den Bus und alle seine Abzweigungen neu.
	 */
	public synchronized void paint (Graphics g)
	{
		paint(g, true);
	}

	/**
	 * Zeichnet den Bus und auf Wunsch alle seine Abzweigungen neu; auf jeden Fall werden alle
	 * aktivierten Abzweigungen neu gezeichnet.
	 *
	 * @param paintAllEdges Falls "false", werden nur aktivierte Abzweigungen neu gezeichnet, sonst alle.
	 */
	public synchronized void paint (Graphics g, boolean paintAllEdges)
	{
		// always paint the whole bus in Rechner.BUS_COLOR/EDGE_COLOR
		remove(g);

		// redraw label
		paintLabel(g);

		paintDotted(g, paintAllEdges);
	} /* end paint */

	/**
	 * Zeichnet den aktivierten Teil des Hauptstranges des Busses gepunktet bzw. läßt,
	 * als Symbolisierung der relevanten Taktflanke, einen Punkt laufen.
	 * Zeichnet alle oder nur die aktivierten Abzweigungen neu; aktivierte Abzweigungen erscheinen
	 * selbsttätig gepunktet.
	 *
	 * @param paintAllEdges Falls "false", werden nur aktivierte Abzweigungen neu gezeichnet, sonst alle.
	 */
	private void paintDotted(Graphics g, boolean paintAllEdges)
	{
		if (dataSourceType != NOT_ACTIVE)
		// (ein Teil des) Bus(ses) ist aktiviert.
		{
			if (moveDotMode == false)
			// gepunktete Linie veranschaulicht
			// den Datenfluss
			{
				int dataFlowAtStart = DIRECTION_DEFAULT;
				int dataFlowAtEnd = DIRECTION_DEFAULT;

				if (dataSourceType == EDGE)
				{
					SimpleEdge se = getEdge(dataSourceString);
					
					if (sizeX == 0)  // vertical bus
					{
						//if (dataDestination.y < dataSource.y)
						//	negativeCoord = true;
							
						if (se.x[1] >= 0)
							dataFlowAtStart = DIRECTION_LEFT;
						else
							dataFlowAtStart = DIRECTION_RIGHT;
					}
					else  // horizontal bus
					{
						//if (dataDestination.x < dataSource.x)
						//	negativeCoord = true;
							
						if (se.y[1] >= 0)
							dataFlowAtStart = DIRECTION_UP;
						else
							dataFlowAtStart = DIRECTION_DOWN;
					}
				}
				if (dataDestinationType == EDGE)
				{
					SimpleEdge se = getEdge(dataDestinationString[0]);
					if (sizeX == 0)  // vertical bus
					{
						if (se.x[1] >= 0)
							dataFlowAtEnd = DIRECTION_LEFT;
						else
							dataFlowAtEnd = DIRECTION_RIGHT;
					}
					else  // horizontal bus
					{
						if (se.y[1] >= 0)
							dataFlowAtEnd = DIRECTION_UP;
						else
							dataFlowAtEnd = DIRECTION_DOWN;
					}
					
				}

				if ((dataFlowAtStart != DIRECTION_DEFAULT) && (dataFlowAtEnd != DIRECTION_DEFAULT))
				{
					int aac;
					if (dataFlowReverse == false)
						aac = activationCounter;
					else
						aac = (sw - 1) - activationCounter;
						
					Rectangle rect = new Rectangle(dataSource.x, dataSource.y, dataDestination.x, dataDestination.y);
					Image[] images = (Image[]) imageCache.get(rect);
					
					if ((images == null) || (images[aac] == null))
					{
						paintDottedLine(g, dataSource, dataDestination, activationCounter, dataFlowReverse, dataFlowAtStart, dataFlowReverse, 1);
						// specialFlags ist 1: Nicht zeichnen (aber in den Cache aufnehmen)
						
						// Gerade gezeichnete Linie aus dem Cache holen
						images = (Image[]) imageCache.get(rect);
					
						Image firstImage = parent.createImage(lw, lw);
						Graphics firstImageGC = firstImage.getGraphics();
					
						int aax, aay;
						if (dataFlowReverse)
						{
							if (sizeX == 0)  // vertical bus
							{
								aax = 0;
								aay = dataDestination.y - lw;
							}
							else  // horizontal bus
							{
								aax = dataDestination.x - lw;
								aay = 0;
							}
						}
						else
						{
							aax = 0; aay = 0;
						}
						
						if (dataFlowReverse)
							images[aac].getGraphics().copyArea(aax, aay, lw, lw, -aax, -aay);
						firstImageGC.drawImage(images[aac], 0, 0, parent);
						
						//debug g.drawImage(firstImage, lw, lw, parent);
						//debug g.drawImage(images[aac], lw, 2*lw, parent);
							
						paintDottedLine(g, dataSource, dataDestination, activationCounter, dataFlowReverse, dataFlowAtEnd, !dataFlowReverse, 3);
						// specialFlags ist 3: Nicht zeichnen, Inhalt des Cache nicht verwenden
						
						// Gerade gezeichnete Linie aus dem Cache holen
						images = (Image[]) imageCache.get(rect);
						
						//debug g.drawImage(images[aac], lw, 3*lw, parent);

						images[aac].getGraphics().drawImage(firstImage, aax, aay, parent);
					}
					
			   	    g.drawImage(images[aac], dataSource.x, dataSource.y, parent);
				}
				else if (dataFlowAtStart != DIRECTION_DEFAULT)
					paintDottedLine(g, dataSource, dataDestination, activationCounter, dataFlowReverse, dataFlowAtStart, dataFlowReverse);
				else if (dataFlowAtEnd != DIRECTION_DEFAULT)
					paintDottedLine(g, dataSource, dataDestination, activationCounter, dataFlowReverse, dataFlowAtEnd, !dataFlowReverse);
				else
					paintDottedLine(g, dataSource, dataDestination, activationCounter, dataFlowReverse);
				//debug g.setColor(Color.green);
				//debug g.drawRect(dataSource.x+1, dataSource.y+1, dataDestination.x, dataDestination.y);

				if (destinationsInDifferentDirections)
				{
					paintDottedLine(g, dataSource2, dataDestination2, activationCounter, !dataFlowReverse);
					//debug g.setColor(Color.red);
					//debug g.drawRect(dataSource2.x, dataSource2.y, dataDestination2.x, dataDestination2.y);
				}
			}
			else
			// ein sich bewegender Punkt als
			// Symbol der relevanten Taktflanke
				moveDot(false);
		}

		paintAllEdges(g, paintAllEdges);

	} /* end paintDotted */


	/**
	 * Löscht die Fläche des Hauptstranges des Busses.
	 */
	public void remove (Graphics g)
	{
		g.setColor(busColor);

		if (sizeX == 0)
		// vertical bus
			g.fillRect(startX, startY, sizeX + lw, sizeY	 );
		else
		// horizontal bus
			g.fillRect(startX, startY, sizeX	 , sizeY + lw);
	}


	/**
	 * Zeichnet alle oder nur die aktivierten Abzweigungen neu; aktivierte Abzweigungen erscheinen
	 * selbsttätig gepunktet.
	 *
	 * @param paintAllEdges Falls "false", werden nur aktivierte Abzweigungen neu gezeichnet, sonst alle.
	 */
	public void paintAllEdges (Graphics g, boolean paintAllEdges)
	{
		if (! paintAllEdges)
		{
			// repaint activated edges only
			for (int i = 0; i < edges.size(); i++)
			{
				SimpleEdge edg = (SimpleEdge) edges.elementAt(i);
				edg.paintActivated(g);
			}
		}
		else
		{
			// repaint all edges
			for (int i = 0; i < edges.size(); i++)
			{
				SimpleEdge edg = (SimpleEdge) edges.elementAt(i);
				edg.paint(g);
			}
		}
	} /* end paintAllEdges */


	/**
	 * Zeichnet ein gepunktetes Rechteck, welches bei (ax, ay) beginnt und sich um bx nach rechts und
	 * by nach unten ausdehnt (bx und by müssen positiv sein).
	 */
    public void paintDottedLine (Graphics g, int ax, int ay, int bx, int by, int activationCounter, boolean dataFlowReverse)
    {
        Point ds = new Point(ax, ay);
        Point dd = new Point(bx, by);
        paintDottedLine(g, ds, dd, activationCounter, dataFlowReverse, DIRECTION_DEFAULT, dataFlowReverse);
    }

    public void paintDottedLine (Graphics g, int ax, int ay, int bx, int by, int activationCounter, boolean dataFlowReverse, int dataFlowAtStart, boolean negativeCoord)
    {
        Point ds = new Point(ax, ay);
        Point dd = new Point(bx, by);
        paintDottedLine(g, ds, dd, activationCounter, dataFlowReverse, dataFlowAtStart, negativeCoord);
    }

	public void paintDottedLine (Graphics g, Point dataSource, Point dataDestination, int activationCounter, boolean dataFlowReverse)
	{
        paintDottedLine(g, dataSource, dataDestination, activationCounter, dataFlowReverse, DIRECTION_DEFAULT, dataFlowReverse);
	}

    public void paintDottedLine (Graphics g, Point dataSource, Point dataDestination, int activationCounter, boolean dataFlowReverse, int dataFlowAtStart, boolean negativeCoord)
    {
    	paintDottedLine(g, dataSource, dataDestination, activationCounter, dataFlowReverse, dataFlowAtStart, negativeCoord, 0);
    }
    
    public void paintDottedLine (Graphics g, Point dataSource, Point dataDestination, int activationCounter, boolean dataFlowReverse, int dataFlowAtStart, boolean negativeCoord, int specialFlags)
	{
		offScreenGC = g;

		if (dataFlowReverse == false)
			ac = activationCounter;
		else
			ac = (sw - 1) - activationCounter;
			//ac = (sw) - activationCounter;

		Rectangle rect = new Rectangle(dataSource.x, dataSource.y, dataDestination.x, dataDestination.y);
		Image[] images = (Image[]) imageCache.get(rect);

		if ((images == null) || (images[ac] == null) || ((specialFlags & 3) == 3))
		{
		    if (images == null)
		        images = new Image[sw + 1];

			//debug if (dataDestination.x <= 0 || dataDestination.y <= 0)
			//debug 	System.out.println("--- SimpleBus.paintDottedLine: " + dataDestination.toString());
		    images[ac] = parent.createImage(dataDestination.x, dataDestination.y);
		    g = images[ac].getGraphics();

    		g.setColor(parent.BACKGROUND);
    		g.fillRect(0, 0, dataDestination.x, dataDestination.y);

    		g.setColor(parent.BUS_COLOR);

    		ax = 0;
    		ay = 0;

    		if (dataDestination.x <= lw)
    		// vertical line
    		{
    			by = dataDestination.y - ac;

    			g.fillRect(ax, ay, lw, ac - (sw - lw));

    			ay = ay + ac;
    			for (int i = 0; i < (by / sw); i++)
    			{
    				g.fillRect(ax, ay, lw, lw);
    				ay = ay + sw;
    			}
    			g.fillRect(ax, ay, lw, Math.min(lw, dataDestination.y - ay));

                size = functionOne(dataDestination.y, ac);
                size2 = functionTwo(dataDestination.y, ac);             

                
 				if ( (dataFlowAtStart != DIRECTION_DEFAULT) &&
                     ( ((! negativeCoord) && (size2 > 0)) ||
                       (negativeCoord && (size > 0)) ) )
 				{
                    ay = negativeCoord ? dataDestination.y - lw : 0;

                    if (negativeCoord)
					{
 						g.setColor(parent.BACKGROUND);
		 				g.fillRect(0, ay, lw, lw);

						g.setColor(parent.BUS_COLOR);

						if (dataFlowAtStart == DIRECTION_LEFT)
		    				g.fillRect(lw - size, ay, size, lw);
						else if (dataFlowAtStart == DIRECTION_RIGHT)
	    					g.fillRect(0, ay, size, lw);
	    			}
	    			else
	    			{
						g.setColor(parent.BACKGROUND);
	 					g.fillRect(0, ay, lw, lw);

						g.setColor(parent.BUS_COLOR);

						if (dataFlowAtStart == DIRECTION_LEFT)
		    				g.fillRect(lw - size2, ay, size2, lw);
						else if (dataFlowAtStart == DIRECTION_RIGHT)
	    					g.fillRect(0, ay, size2, lw);
	    			}
	    		}
    		}
    		else
    		// horizontal line
    		{
    			bx = dataDestination.x - ac;

   				g.fillRect(ax, ay, ac - (sw - lw), lw);

    			ax = ax + ac;
    			for (int i = 0; i < (bx / sw); i++)
    			{
    				g.fillRect(ax, ay, lw, lw);
    				ax = ax + sw;
    			}
    			g.fillRect(ax, ay, Math.min(lw, dataDestination.x - ax), lw);

                size = functionOne(dataDestination.x, ac);
                size2 = functionTwo(dataDestination.x, ac);
                
 				if ( (dataFlowAtStart != DIRECTION_DEFAULT) &&
                     ( ((! negativeCoord) && (size2 > 0)) ||
                       (negativeCoord && (size > 0)) ) )
 				{
                    ax = negativeCoord ? dataDestination.x - lw : 0;

                    if (negativeCoord)
					{
 						g.setColor(parent.BACKGROUND);
		 				g.fillRect(ax, 0, lw, lw);

						g.setColor(parent.BUS_COLOR);

						if (dataFlowAtStart == DIRECTION_UP)
		    				g.fillRect(ax, lw - size, lw, size);
						else if (dataFlowAtStart == DIRECTION_DOWN)
    						g.fillRect(ax, 0, lw, size);
    				}
    				else
    				{
 						g.setColor(parent.BACKGROUND);
 						g.fillRect(ax, 0, lw, lw);

						g.setColor(parent.BUS_COLOR);

						if (dataFlowAtStart == DIRECTION_UP)
						// 9 - (0, 1, lw, 2)
						// 8 - (0, 2, lw, 1)
						// 7 - (0, 3, lw, 0)
	    					g.fillRect(ax, lw - size2, lw, size2);
						else if (dataFlowAtStart == DIRECTION_DOWN)
						// 9 - (0, 0, lw, 2)
						// 8 - (0, 0, lw, 1)
						// 7 - (0, 0, lw, 0)
    						g.fillRect(ax, 0, lw, size2);
    				}
    			}
    		}

    		imageCache.put(rect, images);
    	}

		if (! ((specialFlags & 1) == 1))
	   	    offScreenGC.drawImage(images[ac], dataSource.x, dataSource.y, parent);
	} /* end paintDottedLine */

	private int functionOne(int len, int ac)
	{
		//int[] gueltig = new int[lw];

		for (int i = 0; i < lw; i++)
		{
			//gueltig[i] = (i + 2*(sw - lw) + len) % sw;
			//int gueltig = (i + sw + 2*lw + len + 1) % sw;
			int gueltig = (i + sw + len - lw + 1) % sw;

			if (gueltig == ac)
				return lw - i - 1;  // 0 bis (lw-1)
		}

		return -1;
	}

	private int functionTwo(int len, int ac)
	{
		//int[] gueltig = new int[lw];

		for (int i = 0; i < lw; i++)
		{
			//int gueltig = (i + sw + lw + 1) % sw;  // guter Ansatz...
			int gueltig = (i + sw - lw) % sw;

			if (gueltig == ac)
				return i;
				//return lw - i - 1;  // 0 bis (lw-1)
		}

		return -1;
	}


	public void paintLabel (Graphics g)
	{
		g.setColor(parent.BACKGROUND);
		for (int i = 0; i < label.length; i++)
		{
			if ( ! label[i].equals(""))
				g.fillRect(labelCoord[i].x, labelCoord[i].y - labelHeight, labelWidth[i], labelHeight);
		}

		g.setColor(Color.black);
		for (int i = 0; i < label.length; i++)
			g.drawString(label[i], labelCoord[i].x, labelCoord[i].y);
	} /* end paintLabel */


	public synchronized void paintActivated(Graphics g)
	{
		if (hasBeenActivated == true)
		{
			hasBeenActivated = false;
			remove(g);
			paintAllEdges(g, false);
		}
		
		if (dataSourceType != NOT_ACTIVE)
		{
			paintDotted(g, false);
		}
	} /* end paintActivated */


	public void addEdge(String str, int x1, int y1, int x2, int y2)
	// the simplest edge that looks like a line
	{
		int[] ax = {x1, x2};
		int[] ay = {y1, y2};
		addEdge(str, ax, ay);
	}
	public void addEdge(String str, int x1, int y1, int x2, int y2, int x3, int y3)
	// edge with a single corner
	{
		int[] ax = {x1, x2, x3};
		int[] ay = {y1, y2, y3};
		addEdge(str, ax, ay);
	}
	public void addEdge(String str, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4)
	// edge with two corners - all you "really" need
	{
		int[] ax = {x1, x2, x3, x4};
		int[] ay = {y1, y2, y3, y4};
		addEdge(str, ax, ay);
	}
	public void addEdge(String str, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int x5, int y5)
	// edge with three corners - for complex routing
	{
		int[] ax = {x1, x2, x3, x4, x5};
		int[] ay = {y1, y2, y3, y4, y5};
		addEdge(str, ax, ay);
	}
	private void addEdge(String str, int[] ax, int[] ay)
	{
		SimpleEdge edg = new SimpleEdge(str, ax, ay, withGates, withSolderDots, this, parent);
		edges.addElement(edg);
	}

	public void removeEdge(String str)
	{
		//debug System.out.println("SimpleBus.removeEdge(): " + edges.size() + " Kanten vorher");
		SimpleEdge se = getEdge(str);
		if (se != null)
		{
			edges.removeElement((Object) se);
			invalidateImageCache();
		}
		//debug System.out.println("SimpleBus.removeEdge(): " + edges.size() + " Kanten nachher");
	}

	public void reshapeEdge(String str, boolean isHorizontalBus, int c1, int c2, int c3, int c4, int c5)
	{
		SimpleEdge se = getEdge(str);
		if (se != null)
		{
			se.reshape(isHorizontalBus, c1, c2, c3, c4, c5);
			invalidateImageCache();
		}
	}


	/**
	 *  Liefert die Koordinaten eines bestimmten Punktes der Komponente,
	 *  z.B. die der linken oberen Ecke, der Mitte des rechten Randes oder
	 *  des Ausganges (bei einer ALU).
	 *  Mögliche Werte für grabMode:
	 *  "start" == "topLeft" == "left" (horz. Bus) == "top" (vert. Bus),
	 *  "end" == "bottomRight" == "right" (horz. Bus) == "bottom" (vert. Bus),
	 *  Bezeichner aller Abzweigungen.
	 *
	 *  @param grabMode Beschreibung des gemeinten Punktes
	 *  @see #getPossibleQualifiers
	 *  @see #setCoordinates
	 */
	public Point getCoordinates(String grabMode)
	{
		Point p;

		if ( grabMode.equalsIgnoreCase("start") || grabMode.equalsIgnoreCase("topLeft") ||
 		     ((sizeX == 0) && grabMode.equalsIgnoreCase("top")) ||  // vertical bus
		     ((sizeY == 0) && grabMode.equalsIgnoreCase("left")) )  // horizontal bus
			if (sizeX == 0)  // vertical bus
				p = new Point(startX, startY - 1);
			else  // horizontal bus
				p = new Point(startX - 1, startY);
		else if ( grabMode.equalsIgnoreCase("end") || grabMode.equalsIgnoreCase("bottomRight") ||
		          ((sizeX == 0) && grabMode.equalsIgnoreCase("bottom")) ||  // vertical bus
		          ((sizeY == 0) && grabMode.equalsIgnoreCase("right")) )    // horizontal bus
			p = new Point(startX + sizeX, startY + sizeY);
		else  // Koordinaten einer SimpleEdge
			p = getCoordinates(grabMode, "end");

		return p;
	} /* end getCoordinates (1 parameter) for SimpleBus */

	/**
	 * Liefert die Koordinaten einer Abzweigung des Busses, entweder von dort, wo sie am Bus beginnt, oder
	 * von dort, wo sie endet.
	 *
	 * @param edgeName Der Name der Abzweigung
	 * @param qualifier "start" oder "end"
	 */
	public Point getCoordinates(String edgeName, String qualifier)
	{
		SimpleEdge edg = getEdge(edgeName);

		if (edg != null)
		{
			Point p = edg.getCoordinates(qualifier);
			p.translate(startX, startY);
			return p;
		}
		else
			return new Point(0, 0);
	} /* end getCoordinates (2 parameters) for SimpleBus */

	/**
	 * Der Parameter grabMode wird immer als "leftTop" angenommen.
	 */
	public void setCoordinates(int x, int y, String grabMode)
	{
		reshape(x, y, sizeX, sizeY);
	}

	/**
	 *  Liefert mögliche Werte für den Parameter grabMode von getCoordinates(1 Parameter)
	 *  (bei setCoordinates wird immer "leftTop" angenommen): "start", "end" und Namen
	 *  aller Abzweigungen. Folgende Werte für grabMode sind zusätzlich erlaubt:
	 *  "start" == "topLeft" == "left" (horz. Bus) == "top" (vert. Bus),
	 *  "end" == "bottomRight" == "right" (horz. Bus) == "bottom" (vert. Bus).
	 */
	public String[] getPossibleQualifiers()
	{
		String[] q = new String[2 + edges.size()];
		q[0] = "start"; q[1] = "end";

		for (int i = 0; i < edges.size(); i++)
			q[2+i] = ((SimpleEdge) edges.elementAt(i)).getLabel();

		return q;
	}


	public String[] getLabel()
	{
		return label;
	}


	/**
	 *  Abkürzung für activate("start", "end");
	 *
	 *  @see #activate(String src, String dst)
	 */
	public synchronized void activate()
	{
		activate("start", "end");
	}

	/**
	 * Auf dem Bus laufen von der durch <i>src</i> bezeichneten Stelle Punkte
	 * zu der durch <i>dst</i> bezeichneten. <i>src</i> und <i>dst</i> können
	 * entweder "start", "end" oder der Bezeichner einer Abzweigung des
	 * Busses sein.
	 * Wurde setMoveDotMode(true) (ein Punkt als Symbol der relevanten
	 * Taktflanke soll sich bewegen) aufgerufen, wird der Hauptstrang
	 * des Busses zunächst nur in Rechner.MOVEDOT_COLOR_BACKGROUND
	 * eingefärbt, Abzweigungen werden wie üblich aktiviert (siehe moveDot).
	 * Sofern dem Bus durch setConnection() mitgeteilt wurde, welche
	 * Komponente an der durch <i>src</i> bezeichneten Stelle
	 * angeschlossen ist, wird deren Wert übernommen.
	 *
	 * @see deactivate
	 * @see moveDot
	 * @see setConnection
	 */
	public synchronized void activate(String src, String dst)
	{
		if (! activationLocked)
		{
			hasBeenActivated = true;
			destinationsInDifferentDirections = false;
			dataSourceString = src;
			dataDestinationString[0] = dst;
			dataDestinationString[1] = "";

			int ax, ay, bx, by;

			if (src.equalsIgnoreCase("start"))
			{
				dataSourceType = START;
				ax = startX;
				ay = startY;
				if ((neighbourAtStart != null) && ((neighbourAtStart.getType() & Rechner.IN) == Rechner.IN) )
					value = neighbourAtStart.getValue();
			}
			else if (src.equalsIgnoreCase("end"))
			{
				dataSourceType = END;
				ax = startX + sizeX;
				ay = startY + sizeY;
				if ((neighbourAtEnd != null) && ((neighbourAtEnd.getType() & Rechner.IN) == Rechner.IN) )
					value = neighbourAtEnd.getValue();
			}
			else
			{
				activateFrom(src);
				dataSourceType = EDGE;
				Point p = getCoordinates(src, "start");
				ax = p.x;
				ay = p.y;
				SimpleEdge se = getEdge(src);
				if (se != null && (se.getConnection() != null) && ((se.getConnection().getType() & Rechner.IN) == Rechner.IN))
					value = se.getConnection().getValue();
			}

			if (dst.equalsIgnoreCase("start"))
			{
				dataDestinationType = START;
				bx = startX;
				by = startY;
			}
			else if (dst.equalsIgnoreCase("end"))
			{
				dataDestinationType = END;
				bx = startX + sizeX;
				by = startY + sizeY;
			}
			else
			{
				activateTo(dst);
				dataDestinationType = EDGE;
				Point p = getCoordinates(dst, "start");
				bx = p.x;
				by = p.y;
			}

			if (((dataSourceType	  == START) || (dataSourceType		== EDGE)) &&
				((dataDestinationType == START) || (dataDestinationType == EDGE)))
			{
				if (sizeX == 0)
				// vertical bus
					if (ay > by)
						ay = ay + lw;
					else
						by = by + lw;
				else
				// horizontal bus
					if (ax > bx)
						ax = ax + lw;
					else
						bx = bx + lw;
			}

			dataFlowReverse = false;

			if (bx < ax)
			{
				int cx = ax; ax = bx; bx = cx;
				dataFlowReverse = true;
			}
			if (by < ay)
			{
				int cy = ay; ay = by; by = cy;
				dataFlowReverse = true;
			}
			dataSource.x      = ax;      dataSource.y      = ay;
			dataDestination.x = bx - ax; dataDestination.y = by - ay;
			if (dataDestination.x == 0)
				dataDestination.x = parent.LINEWIDTH;
			if (dataDestination.y == 0)
				dataDestination.y = parent.LINEWIDTH;
		}
	} /* end activate (one destination) */

	/**
	 * Auf dem Bus laufen von der durch <i>src</i> bezeichneten Stelle Punkte
	 * zu der durch <i>dst1</i> und zu der durch <i>dst2</i> bezeichneten.
	 * <i>src</i>, <i>dst1</i> und <i>dst2</i> können entweder "start",
	 * "end" oder der Bezeichner einer Abzweigung des Busses sein.
	 * Durch zusätzliche Aufrufe von activateTo() können auch Punkte zu
	 * mehr als zwei Zielen laufen.
	 * Wurde setMoveDotMode(true) (ein Punkt als Symbol der relevanten
	 * Taktflanke soll sich bewegen) aufgerufen, wird der Hauptstrang
	 * des Busses zunächst nur in Rechner.MOVEDOT_COLOR_BACKGROUND
	 * eingefärbt, Abzweigungen werden wie üblich aktiviert (siehe moveDot).
	 * Sofern dem Bus durch setConnection() mitgeteilt wurde, welche
	 * Komponente an der durch <i>src</i> bezeichneten Stelle
	 * angeschlossen ist, wird deren Wert übernommen.
	 *
	 * @see activateTo
	 * @see deactivate
	 * @see moveDot
	 * @see setConnection
	 */
	public void activate(String src, String dst1, String dst2)
	{
		if (! activationLocked)
		{
		    activate(src, dst2);
		    boolean dataFlowReverse2 = dataFlowReverse;
		    dataSource2 = new Point(dataSource.x, dataSource.y);
		    dataDestination2 = new Point(dataDestination.x, dataDestination.y);

		    activate(src, dst1);
		    if (dataFlowReverse == dataFlowReverse2)
		    // Beide Datensenken liegen von der Quelle aus gesehen in derselben Richtung.
		    {
		    	destinationsInDifferentDirections = false;
		    	if ((dataDestination2.x > dataDestination.x) ||
		    	    (dataDestination2.y > dataDestination.y))
		    		activate(src, dst2);
		    		// Bis zur weiter entfernten Senke aktivieren.
		    }
		    else
		    // Die Datensenken liegen in unterschiedlichen Richtungen.
		    {
		    	destinationsInDifferentDirections = true;

				/**
		    	if (!dataFlowReverse)
		    	{
		    		dataSource2 = dataSource;
		    		dataSource2.translate(-dataDestination2.x, -dataDestination2.y);
		    	}
		    	else
		    	{
		    		dataSource2 = dataSource;
		    		dataSource2.translate(dataDestination.x, dataDestination.y);
		    	}
		    	*/
		    }

			dataSourceString = src;
		    dataDestinationString[0] = dst1;
		    dataDestinationString[1] = dst2;
	    	activateTo(dst2);
	    }
	} /* end activate (two destinations) */

	/**
	 * Die durch <i>dst</i> bezeichnete Abzweigung wird so aktiviert,
	 * daß auf ihr Punkte vom Hauptstrang des Busses weg laufen.
	 */
	public void activateTo(String dst)
	{
		getEdge(dst).activateTo();
	} /* end activateTo */

	/**
	 * Die durch <i>dst</i> bezeichnete Abzweigung wird so aktiviert,
	 * daß auf ihr Punkte zum Hauptstrang des Busses hin laufen.
	 */
	public void activateFrom(String dst)
	{
		getEdge(dst).activateFrom();
	} /* end activateTo */


	public synchronized void deactivate()
	{
		if (! activationLocked)
		{
			hasBeenActivated = true;
			
			for (int i = 0; i < edges.size(); i++)
			{
				SimpleEdge edg = (SimpleEdge) edges.elementAt(i);
				edg.deactivate();
			}
			dataSourceType = NOT_ACTIVE;
			dataDestinationType = NOT_ACTIVE;
			dataSource.x = dataSource.y = dataDestination.x = dataDestination.y = -7;
			dataSource2.x = dataSource2.y = dataDestination2.x = dataDestination2.y = -8;
			dataSourceString = "";
			dataDestinationString[0] = dataDestinationString[1] = "";
			activationCounter = 0;
			destinationsInDifferentDirections = false;

			setMoveDotMode(false);
			dotMoveCounter = -(parent.LINEWIDTH+1);
		}
	} /* end deactivate */

	public void write()
	{
		write("start", "end");
	}

	public void write(String src, String dst)
	{
		Connection csrc = null;
		Connection cdst = null;

		if (src.equalsIgnoreCase("start"))
			csrc = neighbourAtStart;
		else if (src.equalsIgnoreCase("end"))
			csrc = neighbourAtEnd;
		else
		{
			SimpleEdge se = getEdge(src);
			if (se != null)
				csrc = se.getConnection();
		}

		if (dst.equalsIgnoreCase("start"))
			cdst = neighbourAtStart;
		else if (dst.equalsIgnoreCase("end"))
			cdst = neighbourAtStart;
		else
		{
			SimpleEdge se = getEdge(dst);
			if (se != null)
				cdst = se.getConnection();
		}

		boolean errorSrc = true;
		if (csrc == null)
			Rechner.out("FEHLER in SimpleBus.write(): Keine Komponente ist an der Stelle \"" + src + "\" angeschlossen!");
		else if ((csrc.getType() & Rechner.IN) != Rechner.IN)
			Rechner.out("FEHLER in SimpleBus.write(): Die an der Stelle \"" + src + "\" angeschlossene Komponente ist kein Eingang!");
		else
			errorSrc = false;

		boolean errorDst = true;
		if (cdst == null)
			Rechner.out("FEHLER in SimpleBus.write(): Keine Komponente ist an der Stelle \"" + dst + "\" angeschlossen!");
		else if ((cdst.getType() & Rechner.OUT) != Rechner.OUT)
			Rechner.out("FEHLER in SimpleBus.write(): Die an der Stelle \"" + src + "\" angeschlossene Komponente ist kein Ausgang!");
		else
			errorDst = false;

		if (errorSrc || errorDst)
			return;
		else
			activate(src, dst);  // führt ein getValue() durch
	} /* end write (one destination) */

	public void write(String src, String dst1, String dst2)
	{
		Connection csrc = null;
		Connection cdst1 = null;
		Connection cdst2 = null;

		if (src.equalsIgnoreCase("start"))
			csrc = neighbourAtStart;
		else if (src.equalsIgnoreCase("end"))
			csrc = neighbourAtEnd;
		else
		{
			SimpleEdge se = getEdge(src);
			if (se != null)
				csrc = se.getConnection();
		}

		if (dst1.equalsIgnoreCase("start"))
			cdst1 = neighbourAtStart;
		else if (dst1.equalsIgnoreCase("end"))
			cdst1 = neighbourAtStart;
		else
		{
			SimpleEdge se = getEdge(dst1);
			if (se != null)
				cdst1 = se.getConnection();
		}

		if (dst2.equalsIgnoreCase("start"))
			cdst2 = neighbourAtStart;
		else if (dst2.equalsIgnoreCase("end"))
			cdst2 = neighbourAtStart;
		else
		{
			SimpleEdge se = getEdge(dst2);
			if (se != null)
				cdst2 = se.getConnection();
		}

		boolean errorSrc = true;
		if (csrc == null)
			Rechner.out("FEHLER in SimpleBus.write(): Keine Komponente ist an der Stelle \"" + src + "\" angeschlossen!");
		else if ((csrc.getType() & Rechner.IN) != Rechner.IN)
			Rechner.out("FEHLER in SimpleBus.write(): Die an der Stelle \"" + src + "\" angeschlossene Komponente ist kein Eingang!");
		else
			errorSrc = false;

		boolean errorDst1 = true;
		if (cdst1 == null)
			Rechner.out("FEHLER in SimpleBus.write(): Keine Komponente ist an der Stelle \"" + dst1 + "\" angeschlossen!");
		else if ((cdst1.getType() & Rechner.OUT) != Rechner.OUT)
			Rechner.out("FEHLER in SimpleBus.write(): Die an der Stelle \"" + src + "\" angeschlossene Komponente ist kein Ausgang!");
		else
			errorDst1 = false;

		boolean errorDst2 = true;
		if (cdst2 == null)
			Rechner.out("FEHLER in SimpleBus.write(): Keine Komponente ist an der Stelle \"" + dst2 + "\" angeschlossen!");
		else if ((cdst2.getType() & Rechner.OUT) != Rechner.OUT)
			Rechner.out("FEHLER in SimpleBus.write(): Die an der Stelle \"" + src + "\" angeschlossene Komponente ist kein Ausgang!");
		else
			errorDst2 = false;

		if (errorSrc || errorDst1 || errorDst2)
			return;
		else
			activate(src, dst1, dst2);  // führt ein getValue() durch
	} /* end write (two destinations) */

	public boolean isActivated()
	{
		return (dataSourceType != NOT_ACTIVE);
	}


	public void scroll()
	{
		activationCounter++;
		if (activationCounter >= sw)
			activationCounter = 0;

		if (dataSourceType != NOT_ACTIVE)
		{
			for (int i = 0; i < edges.size(); i++)
			{
				int ao;
				
				SimpleEdge se = (SimpleEdge) edges.elementAt(i);
				
				if (sizeX == 0)  // vertical bus
				{
					if (destinationsInDifferentDirections && (dataSource.y > se.y[0]))
						ao = (dataSource2.y - startY - se.y[0]);
					else
						ao = (dataSource.y - startY - se.y[0]);
				}
				else  // horizontal bus
				{
					if (destinationsInDifferentDirections && (dataSource.x > se.x[0]))
						ao = (dataSource2.x - startX - se.x[0]);
					else
						ao = (dataSource.x - startX - se.x[0]);
				}

				if ((! dataFlowReverse) && se.isActivatedTo())
					ao -= 1;
				if (dataFlowReverse && se.isActivatedFrom())
					ao -= 1;
				if (dataFlowReverse && (!destinationsInDifferentDirections))
					ao = -ao;
					
				while (ao < 0)
					ao += sw;
					
				se.acOffset[0] = (activationCounter + ao) % sw;
				se.scroll(se.acOffset[0]);
			}
		}
	} /* end scroll */

	public void setLabelPosition(String posX, String posY)
	{
		if (sizeX == 0)
		// vertical bus
		{

			if (posX.equalsIgnoreCase("left"))
				labelPosX = LEFT;
			else
				labelPosX = RIGHT;

			if (posY.equalsIgnoreCase("end"))
				labelPosY = END;
			else
				labelPosY = START;
		}
		else
		// horizontal bus
		{
			if (posX.equalsIgnoreCase("end"))
				labelPosX = END;
			else
				labelPosX = START;

			if (posY.equalsIgnoreCase("bottom"))
				labelPosY = BOTTOM;
			else
				labelPosY = TOP;
		}

		setLabelCoordinates(labelPosX, labelPosY);
	} /* end setLabelPosition */

	public String[] getLabelPosition()
	{
		String[] labelPos = new String[2];
		labelPos[0] = new String("");
		labelPos[1] = new String("");

		if (labelPosX == START)
			labelPos[0] = "start";
		else if (labelPosX == END)
			labelPos[0] = "end";
		else if (labelPosX == LEFT)
			labelPos[0] = "left";
		else if (labelPosX == RIGHT)
			labelPos[0] = "right";

		if (labelPosY == START)
			labelPos[1] = "start";
		else if (labelPosY == END)
			labelPos[1] = "end";
		else if (labelPosY == TOP)
			labelPos[1] = "top";
		else if (labelPosY == BOTTOM)
			labelPos[1] = "bottom";

		return labelPos;
	} /* end getLabelPosition */


	public void setMoveDotMode (boolean b)
	{
		moveDotMode = b;
	}

	public boolean getMoveDotMode ()
	{
		return moveDotMode;
	}

    public boolean moveDot()
    {
        return moveDot(true);
    }

    public boolean moveDot (boolean moveItFarther)
	{
        if (moveItFarther == true)
			//dotMoveCounter++;
			dotMoveCounter = dotMoveCounter + 2;

		int dmc;
		if (dataFlowReverse == false)
			dmc = dotMoveCounter;
		else if (sizeX == 0)
		// vertical bus
			dmc = dataDestination.y - dotMoveCounter - lw;
		else
			dmc = dataDestination.x - dotMoveCounter - lw;


		Graphics g = parent.offScreenGC;

		g.setColor(parent.MOVEDOT_COLOR_BACKGROUND);
		g.fillRect(dataSource.x, dataSource.y, dataDestination.x, dataDestination.y);

		g.setColor(parent.MOVEDOT_COLOR_FOREGROUND);

		if (sizeX == 0)
		// vertical bus
		{
			if		((dataFlowReverse == false) && (dmc > dataDestination.y))
				return true;
			else if ((dataFlowReverse == true)	&& (dmc < 0))
				return true;
			else
			{
				int hinausgeschossen = (dmc + lw) - dataDestination.y;

				if (dmc < 0)
				// dot starts its way
					g.fillRect(dataSource.x, dataSource.y, lw, lw + dmc);

				else if (hinausgeschossen > 0)
				// dot ends its way
					g.fillRect(dataSource.x, dataSource.y + dmc, lw, lw - hinausgeschossen);

				else
					g.fillRect(dataSource.x, dataSource.y + dmc, lw, lw);
			}
		}
		else
		// horizontal bus
		{
			if		((dataFlowReverse == false) && (dmc > dataDestination.x))
				return true;
			else if ((dataFlowReverse == true)	&& (dmc < 0))
				return true;
			else
			{
				int hinausgeschossen = (dmc + lw) - dataDestination.x;

				if (dmc < 0)
				// dot starts its way
					g.fillRect(dataSource.x, dataSource.y, lw + dmc, lw);

				else if (hinausgeschossen > 0)
				// dot ends its way
					g.fillRect(dataSource.x + dmc, dataSource.y, lw - hinausgeschossen, lw);

				else
					g.fillRect(dataSource.x + dmc, dataSource.y, lw, lw);
			}
		}

		return false;

	} /* end moveDot */


	public void invalidateImageCache()
	{
	    imageCache = new Hashtable(40);
	} /* end invalidateImageCache */
	
	
	public void saveActivationState()
	{
		if (dataSourceType != NOT_ACTIVE)
			oldSource = dataSourceString;
		else
			oldSource = "";
		oldDestination = dataDestinationString;
	}

	public String[] getActivationState()
	{
		saveActivationState();
		String oldState[] = new String[3];
		oldState[0] = oldSource;
		oldState[1] = oldDestination[0];
		oldState[2] = oldDestination[1];
		return oldState;
	}

	public void restoreActivationState()
	{
		if (! oldSource.equals(""))
		{
			if (oldDestination[1].equals(""))
				activate(oldSource, oldDestination[0]);
			else
				activate(oldSource, oldDestination[0], oldDestination[1]);
			paintActivated(parent.onScreenGC);
		}
	}

	public void restoreActivationState(String oldState[])
	{
		oldSource = oldState[0];
		oldDestination[0] = oldState[1];
		oldDestination[1] = oldState[2];
		restoreActivationState();
	}


	public String getSourceString()
	{
		if (dataSourceType != NOT_ACTIVE)
			return dataSourceString;
		else
			return "";
	}


	public SimpleEdge getEdge(String str)
	{
		SimpleEdge se = (SimpleEdge) null;

		for (int i = 0; i < edges.size(); i++)
		{
			SimpleEdge edg = (SimpleEdge) edges.elementAt(i);
			String edgeLabel = edg.getLabel();
			if (edgeLabel.equalsIgnoreCase(str))
			{
				se = edg;
				break;
			}
		}
		if (se == null)
			if (label[0].equals(""))
				parent.out("FEHLER: Eine Abzweigung '" + str + "' gibt es am Bus mit Startpunkt (" + startX + ", " + startY + ") nicht!");
			else
			{
				String oneLineLabel = "";
				for (int i = 0; i < label.length; i++)
					oneLineLabel = oneLineLabel + label[i] + " ";
				parent.out("FEHLER: Eine Abzweigung '" + str + "' gibt es am Bus '" + oneLineLabel + "' nicht!");
			}
		return se;
	} /* end getEdge */

	public boolean existsEdge(String str)
	{
		SimpleEdge se = (SimpleEdge) null;

		for (int i = 0; i < edges.size(); i++)
		{
			SimpleEdge edg = (SimpleEdge) edges.elementAt(i);
			String edgeLabel = edg.getLabel();
			if (edgeLabel.equalsIgnoreCase(str))
				return true;
		}

		return false;

	} /* end getEdge */

	public int getNumberOfEdges()
	{
		return edges.size();
	}

	public int getNumberOfConnections()
	{
		int noc = 0;
		if (neighbourAtStart != null)
			noc++;
		if (neighbourAtEnd != null)
			noc++;

		for (int i = 0; i < edges.size(); i++)
			if ( ((SimpleEdge) edges.elementAt(i)).getConnection() != null )
				noc++;

		return noc;
	}


	public void setFlag(String str, int f)
	{
		SimpleEdge se = getEdge(str);
		if (se != null)
		{
			se.setGate((f & WITH_GATES) == WITH_GATES);
			se.setSolderDot((f & WITH_SOLDER_DOTS) == WITH_SOLDER_DOTS);
		}
	}

	public void setFlags(int f)
	{
		setFlags(f, f);
	}

	public void setFlags(int f, int flagMask)
	{
		if ((f & BLACK_ROOT) == BLACK_ROOT)
		{
			blackRoot = true;
		    busColor = parent.BUS_COLOR;
		}
		else
		{
			blackRoot = false;
		    busColor = parent.EDGE_COLOR;
		}

		if ((flagMask & WITH_SOLDER_DOTS) == WITH_SOLDER_DOTS)
		{
			withSolderDots = ((f & WITH_SOLDER_DOTS) == WITH_SOLDER_DOTS);
			if (withSolderDots)
				withGates = false;
		}

		if ((flagMask & WITH_GATES) == WITH_GATES)
			withGates = ((f & WITH_GATES) == WITH_GATES) & !withSolderDots;


		SimpleEdge se;
		for (int i = 0; i < edges.size(); i++)
		{
			se = (SimpleEdge) edges.elementAt(i);
			if ((flagMask & WITH_GATES) == WITH_GATES)
				se.setGate(withGates);
			if ((flagMask & WITH_SOLDER_DOTS) == WITH_SOLDER_DOTS)
				se.setSolderDot(withSolderDots);
		}
	} /* end setFlags */

	public int getFlags()  // of bus
	{
		int flags = NONE;

		if (blackRoot)
			flags += BLACK_ROOT;
		if (withGates)
			flags += WITH_GATES;
		if (withSolderDots)
			flags += WITH_SOLDER_DOTS;

		return flags;
	} /* end getFlags of us */

	public int getFlags(String str)  // for edge
	{
		SimpleEdge se = getEdge(str);
		if (se != null)
			return se.getFlags();
		else
			return NONE;
	} /* end getFlags for edge */

	public int getUniqueFlags()
	{
		int busFlags = getFlags();
		int sumOfFlags = BLACK_ROOT + WITH_SOLDER_DOTS + WITH_GATES;
		int uFlags = sumOfFlags;

		for (int i = 0; i < edges.size(); i++)
		{
			SimpleEdge se = (SimpleEdge) edges.elementAt(i);

			boolean seWithSolderDot = ((se.getFlags() & WITH_SOLDER_DOTS) == WITH_SOLDER_DOTS);
			boolean seWithGate      = ((se.getFlags() & WITH_GATES) == WITH_GATES);

			if (seWithSolderDot != withSolderDots)
				uFlags = uFlags & (sumOfFlags - WITH_SOLDER_DOTS);

			if (seWithGate != withGates)
				uFlags = uFlags & (sumOfFlags - WITH_GATES);
		}

		return uFlags;
	} /* end getUniqueFlags */


} /* end SimpleBus */

