package ckelling.baukasten;

import java.awt.Point;
import java.util.Vector;


/**
 *	Superklasse für Elemente des Rechnerbaukastens:
 *  ALU, Adder, EditableMemory, TagMemory, SimpleBus, PipelineStageLabel,
 *  Misc, Mux, SimpleLabel, EditableLabel
 *
 *	@author		Carsten Kelling
 *	@version	0.9.0, 15.07.97
 */
public abstract class RechnerKomponente
{
	/**
	 * Der Aktivierungsstatus der Komponente - erscheint sie auf dem
	 * Bildschirm hervorgehoben?
	 */
	protected boolean	activated = false;

	/**
	 *  Wird durch lock/unlock gesetzt.
	 */
	protected boolean	activationLocked = false;

	/**
	 * Alle an diese angeschlossenen Komponenten
	 * vom Type Rechner.IN oder Rechner.INOUT.
	 */
	protected Vector connectionsIn = null;

	/**
	 * Alle an diese angeschlossenen Komponenten
	 * vom Type Rechner.OUT oder Rechner.INOUT.
	 */
	protected Vector connectionsOut = null;


    /**
     * Aus Geschwindigkeitsgründen:
     * Aufrufe von activate(), deactivate() und paintActivated() werden bis zu einem unlock() ignoriert.
     *
     * @see #activate
     * @see #deactivate
     * @see #paintActivated
     */
	public void lock()
	{
		activationLocked = true;
	}

	/**
	 * Hebt die Wirkung von lock() wieder auf.
	 *
	 * @see #lock
	 */
	public void unlock()
	{
		activationLocked = false;
	}

	/**
	 *  Zeichnet die Komponente neu.
	 *
	 *  @see #paintActivated
	 */
	public synchronized abstract void paint(java.awt.Graphics onScreenGC);

	/**
	 *  Zeichnet die Komponente
	 *  immer, wenn sie aktiviert ist und
	 *  genau einmal, wenn sie deaktiviert wurde.
	 *
	 *  @see #activate
	 *  @see #deactivate
	 *  @see #paint
	 */
	public synchronized abstract void paintActivated(java.awt.Graphics onScreenGC);

	/**
	 * Aktiviert die Komponente:
	 * Das Erscheinungsbild ändert sich (um die Aufmerksamkeit des
	 * Betrachters zu erregen; Register werden Rot statt Blau, Busse
	 * lassen Punkte laufen) und
	 * falls vorhanden wird update() (nicht: java.awt.Component.update()) aufgerufen.
	 * Ruft nicht paint() auf.
	 *
	 * @see #deactivate
	 * @see #paintActivated
	 */
	public abstract synchronized void activate();

	/**
	 * Deaktiviert die Komponente:
	 * Das Erscheinungsbild ändert sich (die Komponente wird wieder unscheinbar;
	 * Register werden Blau statt Rot, Busse werden wieder unbewegte Linien).
	 * Ruft nicht paint() auf.
	 *
	 * @see #activate
	 * @see #paintActivated
	 */
	public abstract synchronized void deactivate();

	/**
	 * Ist die Komponente aktiviert?
	 */
	public boolean isActivated()
	{
		return activated;
	}

	/**
	 *  Ändert X- und Y-Koordinate, Breite und Höhe der Komponente.
	 */
	public abstract void reshape(int x, int y, int width, int height);

	/**
	 *  Ändert X- und Y-Koordinate, Breite und Höhe der Komponente;
	 *  Der Punkt (x, y) muß dabei nicht notwendigerweise von der linken, oberen
	 *  Ecke der Komponente eingenommen werden, sondern kann z.B. auch von der Mitte
	 *  ihres rechten Randes oder dem Ausgang (bei einer ALU) eingenommen werden,
	 *  bestimmt durch grabMode.
	 *
	 *  @param grabMode Bestimmt, auf welchen Punkt der Komponente sich (x, y) bezieht.
	 *  @see #getPossibleQualifiers
	 *  @see #setCoordinates
	 *  @see #getCoordinates
	 */
	public abstract void reshape(int x, int y, int width, int height, String grabMode);

	/**
	 *  Verschiebt die Komponente an den Punkt (x, y);
	 *  (x, y) muß dabei nicht notwendigerweise von der linken, oberen
	 *  Ecke der Komponente eingenommen werden, sondern kann z.B. auch von der Mitte
	 *  ihres rechten Randes oder dem Ausgang (bei einer ALU) eingenommen werden.
	 *
	 *  @param grabMode Bestimmt, auf welchen Punkt der Komponente sich (x, y) bezieht.
	 *  @see #getPossibleQualifiers
	 *  @see #getCoordinates
	 */
	public abstract void setCoordinates(int x, int y, String grabMode);

	/**
	 *  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).
	 *
	 *  @param grabMode Beschreibung des gemeinten Punktes
	 *  @see #getPossibleQualifiers
	 *  @see #setCoordinates
	 */
	public abstract java.awt.Point getCoordinates(String grabMode);

	/**
	 * Setzt den/einen internen Wert der Komponente. Komponenten, die, wie ein RAM, mehrere Werte beinhalten,
	 * muß vorher mit setAddress() o.ä. mitgeteilt werden, welcher Wert gesetzt werden soll.
	 * Achtung! setValue(), auch gefolgt von paint(), ändert nicht die Bildschirmdarstellung der Komponente!
	 * Zur Anzeige des neuen Wertes ist stets ein activate() notwendig!
	 *
	 * @param newValue Der neue Wert
	 * @see #activate
	 * @see #deactivate
	 * @see #getValue
	 */
	public abstract void setValue(int newValue);

	/**
	 * Liefert den/einen internen Wert der Komponente. Komponenten, die, wie ein RAM, mehrere Werte beinhalten,
	 * muß vorher mit setAddress() o.ä. mitgeteilt werden, welcher Wert gemeint ist.
	 *
	 * @see #setValue
	 */
	public abstract int getValue();

	/**
	 * Liefert 2^(Bitbreite der Komponente).
	 */
	public abstract long getMaxValue();


	/**
	 * Eine "Abkürzungsmethode", um die Simulationsmethoden
	 * getValue() und setValue() und die Demonstrationsmethode
	 * activate() zusammenzufassen. Standard: read() = activate().
	 *
	 * @see #activate
	 * @see #getValue
	 * @see #setConnection
	 * @see #setValue
	 * @see #write
	 */
	public void read()
	{
		activate();
	}

	/**
	 * Eine "Abkürzungsmethode", um die Simulationsmethoden
	 * getValue() und setValue() und die Demonstrationsmethode
	 * activate() zusammenzufassen. Standard: Ermitteln, ob genau
	 * ein Eingang der Komponente aktiviert ist. Falls ja, übernehmen
	 * von dessen Wert und aktivieren, falls nein, Ausgabe einer
	 * Fehlermeldung.
	 *
	 * @see #activate
	 * @see #getValue
	 * @see #setConnection
	 * @see #setValue
	 * @see #write
	 */
	public void write()
	{
		Connection input = getActivatedInput();
		if (input != null)
		{
			setValue(input.getValue());
			activate();
		}
	}

	/**
	 * Falls genau ein Eingang der Komponente aktiviert ist,
	 * wird dieser zurückgeliefert, sonst null.
	 *
	 * @see #write
	 */
	public Connection getActivatedInput()
	{
		Connection input = null;
		String intro;

		if (connectionsIn == null)
		{
			String str = Rechner.arrayToString(getLabel());
			if (str == "")
				intro = "Für eine Instanz ohne Titel";
			else
				intro = "Für die Instanz \"" + str + "\"";

			Rechner.out("FEHLER in RechnerKomponente.write(): " + intro + " von " + getClass().getName() + " sind keine Eingänge definiert!");
			return null;
		}

		for (int i = 0; i < connectionsIn.size(); i++)
			if ( ((Connection) connectionsIn.elementAt(i)).isActivated() )
			{
				if (input != null)  // mehr als ein aktivierter Eingang
				{
					String str = Rechner.arrayToString(getLabel());
					if (str == "")
						intro = "Bei einer Instanz ohne Titel";
					else
						intro = "Bei der  Instanz \"" + str + "\"";

					Rechner.out("FEHLER in RechnerKomponente.write(): " + intro + " von " + getClass().getName() + " ist mehr als ein Eingang aktiviert!");
					return null;
				}
				else
					input = (Connection) connectionsIn.elementAt(i);
			}

		if (input == null)
		{
			String str = Rechner.arrayToString(getLabel());
			if (str == "")
				intro = "Bei einer Instanz ohne Titel";
			else
				intro = "Bei der Instanz \"" + str + "\"";

			Rechner.out("FEHLER in RechnerKomponente.write(): " + intro + " von " + getClass().getName() + " ist kein Eingang aktiviert!");
			return null;
		}

		return input;
	}

	/**
	 * Macht der Komponente bekannt, daß an sie eine andere Komponente
	 * angeschlossen ist und diese aus ihrer Sicht
	 * ein Eingang/ein Ausgang/beides ist.
	 */
	public void setConnection(RechnerKomponente rk, int type)
	{
		setConnection(new Connection(rk, type));
	}

	/**
	 * Macht der Komponente bekannt, daß an sie eine andere Komponente
	 * angeschlossen ist und diese aus ihrer Sicht
	 * ein Eingang/ein Ausgang/beides ist.
	 */
	public void setConnection(RechnerKomponentePanel rkp, int type)
	{
		setConnection(new Connection(rkp, type));
	}

	/**
	 * Macht der Komponente bekannt, daß an sie eine andere Komponente
	 * angeschlossen ist und diese aus ihrer Sicht
	 * ein Eingang/ein Ausgang/beides ist.
	 */
	public void setConnection(Editor_Source source, int type)
	{
		setConnection(new Connection(source, type));
	}

	/**
	 * Macht der Komponente bekannt, daß an sie eine andere Komponente
	 * angeschlossen ist und diese aus ihrer Sicht
	 * ein Eingang/ein Ausgang/beides ist.
	 *
	 * @param type Rechner.IN, OUT oder INOUT
	 */
	public void setConnection(Connection neighbour)
	{
		int type = neighbour.getType();

		if (type == Rechner.IN)
			connectionsIn.addElement(neighbour);
		else if (type == Rechner.OUT)
			connectionsOut.addElement(neighbour);
		else if (type == Rechner.INOUT)
		{
			connectionsIn.addElement(neighbour);
			connectionsOut.addElement(neighbour);
		}
		else
			Rechner.out("FEHLER in RechnerKomponente.setConnection(): Unbekannter Typ \"" + type + "\" - erlaubt: Rechner.IN, OUT, INOUT.");
	}


	/**
	 * Liefert die Zeilen des Titels der Komponente; einige Komponenten haben nur einzeilige Titel.
	 *
	 * @see #setLabel(String[] newLabel)
	 */
	public abstract String[]		getLabel();

	/**
	 * Legt den Titel der Komponente fest (pro Zeile ein String); einige Komponenten ignorieren alle Zeilen bis auf die erste.
	 *
	 * @see #getLabel
	 * @see #setLabel(String newLabel)
	 */
	public abstract void			setLabel(String[] newLabel);

	/**
	 * Legt den Titel der Komponente fest (nur eine Zeile).
	 *
	 * @see #getLabel
	 * @see #setLabel(String[] newLabel)
	 */
	public void setLabel(String newLabel)
	{
		String[] strArray = {newLabel};
		setLabel(strArray);
	}

	/**
	 * Liefert alle möglichen Werte für den Parameter "grabMode" bei
	 * setCoordinates() und getCoordinates():
	 * Stellen, an denen diese Art von Komponente "angefaßt" werden kann.
	 *
	 * @see #setCoordinates
	 * @see #getCoordinates
	 */
	public abstract String[]       getPossibleQualifiers();

	/**
	 * Liefert "true", falls p innerhalb der Grenzen dieser Komponente liegt.
	 */
	public abstract boolean intersectsWith(Point p);

	/**
	 * Liefert den Hilfetext, der angezeigt wird, wenn der Mauszeiger für
	 * Rechner.INFOTIP_DELAY ms unbewegt über der Komponente steht.
	 */
	public abstract String getInfoTipText(Point p);

}

