package ckelling.baukasten;

import java.awt.*;
import java.util.Vector;


/**
 *	Superklasse für Elemente des Rechnerbaukastens:
 *  Register16, Register8, Register16Split, PipelineRegister
 *
 *	@author		Carsten Kelling
 *	@version	0.9.0, 15.07.97
 */
public abstract class RechnerKomponentePanel extends Panel
{
	/**
	 * Der Aktivierungsstatus der Komponente - erscheint sie auf dem
	 * Bildschirm hervorgehoben?
	 */
	protected boolean	activated = false;

	/**
	 *  Wird durch lock/unlock gesetzt.
	 */
	protected boolean	activationLocked = false;

	/**
	 *  Der Titel der Komponente; abgeleitete Klassen
	 *  von RechnerKomponentePanel haben bisher nur einzeilige Titel.
	 */
	protected String	title;

	protected Vector connectionsIn = null;
	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
	 *  immer, wenn sie aktiviert ist und
	 *  genau einmal, wenn sie deaktiviert wurde.
	 *
	 *  @see #activate
	 *  @see #deactivate
	 */
	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
	 * update() wird aufgerufen.
	 * Ruft nicht paint() auf.
	 *
	 * @see #deactivate
	 * @see #update
	 * @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 #update
	 * @see #paintActivated
	 */
	public abstract synchronized void deactivate();

	/**
	 * Ist die Komponente aktiviert?
	 */
	public boolean isActivated()
	{
		return activated;
	}


	/**
	 *  Alle Komponenten unterscheiden zwischen dem/den gespeicherten Wert(en) value
	 *  und dem/den tatsächlich sichtbaren Wert(en) displayValue; update() führt ein
	 *  "displayValue = value" durch.
	 *  Wird von activate() aufgerufen.
	 *  Hat nichts mit java.awt.Component.update() zu tun.
	 *
	 *  @see #deactivate
	 *  @see #activate
	 */
	public abstract void update();

	/**
	 *  Ä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)
		{
			if (title == "")
				intro = "Für eine Instanz ohne Titel";
			else
				intro = "Für die Instanz \"" + title + "\"";

			Rechner.out("FEHLER in RechnerKomponentePanel.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
				{
					if (title == "")
						intro = "Bei einer Instanz ohne Titel";
					else
						intro = "Bei der  Instanz \"" + title + "\"";

					Rechner.out("FEHLER in RechnerKomponentePanel.write(): " + intro + " von " + getClass().getName() + " ist mehr als ein Eingang aktiviert!");
					return null;
				}
				else
					input = (Connection) connectionsIn.elementAt(i);
			}

		if (input == null)
		{
			if (title == "")
				intro = "Bei einer Instanz ohne Titel";
			else
				intro = "Bei der Instanz \"" + title + "\"";

			Rechner.out("FEHLER in RechnerKomponentePanel.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 RechnerKomponentePanel.setConnection(): Unbekannter Typ \"" + type + "\" - erlaubt: Rechner.IN, OUT, INOUT.");
	} /* end setConnection */


	/**
	 * Legt den einzeiligen Titel der Komponente fest.
	 *
	 * @see #getLabel
	 */
	public void setLabel(String newLabel)
	{
		title = newLabel;
	}

	/**
	 * Liefert den einzeiligen Titel der Komponente.
	 *
	 * @see #setLabel
	 */
	public String getLabel()
	{
		return title;
	}

	/**
	 *  Dient an dieser Stelle dazu, das Standardverhalten von java.awt.Component.update() zu ändern,
	 *  nach dem der Hintergrund der Komponente gelöscht wird.
	 *  Muß in jeder Komponente mit folgender Funktionalität gefüllt werden:
	 *  Alle Komponenten unterscheiden zwischen dem/den gespeicherten Wert(en) value
	 *  und dem/den tatsächlich sichtbaren Wert(en) displayValue; update() führt ein
	 *  "displayValue = value" durch.
	 *  Wird von activate() aufgerufen.
	 *
	 *  @see #deactivate
	 *  @see #activate
	 */
	public void update(Graphics g) {}

	/**
	 *  Ruft direkt paint(g), ohne "Umweg" über java.awt.Component.update(), auf.
	 */
	public void repaint(Graphics g)
	{
		paint(g);
	}

	/**
	 * 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);

}
