package ckelling.baukasten;

import java.awt.*;
import java.lang.*;
import java.util.*;


// Versionsgeschichte
// 0.1.0, 29.06.97
// 0.2.0, 28.07.97

/**
 *  Ein Addierer mit beliebig vielen Eingängen.
 *  Abgeleitet von ALU.java; kann im Gegensatz zu dieser nur Addieren und
 *	sieht anders aus, erzeugt aber auch flags.
 *
 *
 *	@author		Carsten Kelling
 *	@version	0.2.0, 28.07.97
 */
public class Adder extends ALU
{
	//// Konstanten Anfang ////

    public final static int	DEFAULT_WIDTH = 20;  // Standardmaße
    public final static int	DEFAULT_HEIGHT = 40;
    public final static int	CORNER_GAP = 10;

	//// Konstanten Ende ////

	/**
	 *  @param str Titel des Addierers
	 *  @param grabMode Bestimmt, auf welchen Punkt der Komponente sich (x, y) bezieht.
	 *  @see #getPossibleQualifiers
	 */
	public Adder (String str, int x, int y, String grabMode, Rechner par)
	{
		this(str, x, y, DEFAULT_WIDTH, DEFAULT_HEIGHT, grabMode, par);
	}

	/**
	 *  @param str Titel des Addierers
	 *  @param grabMode Bestimmt, auf welchen Punkt der Komponente sich (x, y) bezieht.
	 *  @see #getPossibleQualifiers
	 */
	public Adder (String str, int x, int y, int width, int height, String grabMode, Rechner par)
	{
		super(str, x, y, width, height, grabMode, "v", par);

		parent = par;
		label = str;
		this.orientation = "";

		calculatingMode = Rechner.ADD_MEM;
		operator = new String("+");
		activated = false;
		hasBeenActivated = false;

		op1 = displayOp1 = 0;
		op2 = displayOp2 = 0;
		result = displayResult = 0;

		zeroFlag = false;
		lessFlag = false;

		blinker = false;

		reshape(x, y, width, height, grabMode);
	} /* end Adder */


	/**
	 *  @param newGrabMode Bestimmt, auf welchen Punkt der Komponente sich (x, y) bezieht
	 *  @see #getPossibleQualifiers
	 */
	public void reshape(int x, int y, int width, int height, String newGrabMode)
	{
		grounds = new Point(x, y);
		this.width = width;
		this.height = height;

		setCoordinates(x, y, newGrabMode);
	}

	/**
	 *  Verschiebt die Komponente an den Punkt (x1, y1);
	 *  (x1, y1) 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 (x1, y1) bezieht.
	 *  @see #getPossibleQualifiers
	 *  @see #getCoordinates
	 */
	public void setCoordinates(int x1, int y1, String grabMode)
	{
		this.grabMode = grabMode;

		if ( grabMode.equalsIgnoreCase("upperInput") || grabMode.equalsIgnoreCase("upper") )
		{
			input1 = new Point(x1, y1);
			input2 = new Point(x1, y1 + height - 2*CORNER_GAP);
			upperLeft = new Point(x1, y1 - CORNER_GAP);
			output = new Point(x1 + width, upperLeft.y + height/2);
		}
		else if ( grabMode.equalsIgnoreCase("lowerInput") || grabMode.equalsIgnoreCase("lower") )
		{
			input1 = new Point(x1, y1 - height + 2*CORNER_GAP);
			input2 = new Point(x1, y1);
			upperLeft = new Point(x1, y1 - height + CORNER_GAP);
			output = new Point(x1 + width, upperLeft.y + height/2);
		}
		else if (grabMode.equalsIgnoreCase("top"))
		{
			upperLeft = new Point(x1 - width/2, y1);
			input1 = new Point(upperLeft.x, y1 + CORNER_GAP);
			input2 = new Point(upperLeft.x, y1 + height - CORNER_GAP);
			output = new Point(upperLeft.x + width, upperLeft.y + height/2);
		}
		else if (grabMode.equalsIgnoreCase("bottom"))
		{
			upperLeft = new Point(x1 - width/2, y1 - height);
			input1 = new Point(upperLeft.x, y1 - height + CORNER_GAP);
			input2 = new Point(upperLeft.x, y1 - CORNER_GAP);
			output = new Point(upperLeft.x + width, upperLeft.y + height/2);
		}
		else if (grabMode.equalsIgnoreCase("output"))
		{
			output = new Point(x1, y1);
			upperLeft = new Point(x1 - width, y1 - height/2);
			input1 = new Point(upperLeft.x, upperLeft.y + CORNER_GAP);
			input2 = new Point(upperLeft.x, upperLeft.y + height - CORNER_GAP);
		}
		else  // "leftTop"
		{
			upperLeft = new Point(x1, y1);
			output = new Point(x1 + width, y1 + height/2);
			input1 = new Point(x1, y1 + CORNER_GAP);
			input2 = new Point(x1, y1 + height - CORNER_GAP);
		}

		labelX = upperLeft.x + (width - parent.stringWidth(parent.DIALOGFONT, label))/2;
		labelHeight = parent.getHeight(parent.DIALOGFONT);

	} /* end setCoordinates */


	/**
	 * Ohne Funktion.
	 */
	public void setOrientation(String o) {}

	public void paint (Graphics g)
	{
		g.setColor(parent.BACKGROUND);
		g.fillRect(upperLeft.x, upperLeft.y, width, height);

		// Zeichnen des Umrisses
		if (activated)
			g.setColor(parent.ALU_COLOR_ACTIVATED);
		else
			g.setColor(parent.ALU_COLOR);

		g.drawRect(upperLeft.x, upperLeft.y, width, height);
		g.drawRect(upperLeft.x + 1, upperLeft.y + 1, width - 2, height - 2);

		String displayFlags;  // Ausgabe der Flags (noch in Schwarz)
		if (displayZeroFlag == true)
		{
			if (displayLessFlag == true)
				displayFlags = new String("?");
			else
				displayFlags = new String("=0");
		}
		else
		{
			if (displayLessFlag == true)
				displayFlags = new String("<0");
			else
				displayFlags = new String(">0");
		}

		g.drawString(displayFlags, output.x - parent.stringWidth(parent.DIALOGFONT, displayFlags) - 2, output.y - labelHeight - 4);

		if ( (parent.blackAndWhite == true) && (blinker == true) )
			g.setColor(parent.BACKGROUND);
		else if (activated == true)
			g.setColor(parent.ALU_COLOR_ACTIVATED);

		//String operand1Expanded = parent.expandToString(displayOp1, ZWEI_HOCH_BITwidth);
		//String operand2Expanded = parent.expandToString(displayOp2, ZWEI_HOCH_BITwidth);

		//int labelwidth = parent.stringWidth(parent.DIALOGFONT, operand1Expanded);
		//g.drawString(operand1Expanded, upperLeft.x + (BASE.x-labelwidth)/2 + 4	  		, input1.y + labelHeight);
		//labelwidth = parent.stringWidth(parent.DIALOGFONT, operand2Expanded);
		//g.drawString(operand2Expanded, upperLeft.x + 2*BASE.x + (BASE.x-labelwidth)/2 - 4, input1.y + labelHeight);

		g.drawString(operator, upperLeft.x + width/2 - 3, upperLeft.y + height/2 + labelHeight);

		//StringBuffer erg = new StringBuffer("= ");
		//erg.append(parent.expandToString(displayResult, ZWEI_HOCH_BITwidth));
		//g.drawString(erg.toString(), upperLeft.x + BASE.x, output.y - 4);

		// Ausgabe des Titels ("Adder" etc.) in Schwarz
		g.setColor(Color.black);
		g.drawString(label, labelX, upperLeft.y - 4);

	} /* end paint */


	/**
	 *  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 str Beschreibung des gemeinten Punktes
	 *  @see #getPossibleQualifiers
	 *  @see #setCoordinates
	 */
	public Point getCoordinates(String str)
	{
		if ( str.equalsIgnoreCase("upperInput") || str.equalsIgnoreCase("upper") )
			return new Point(input1.x, input1.y);
		else if ( str.equalsIgnoreCase("lowerInput") || str.equalsIgnoreCase("lower") )
			return new Point(input2.x, input2.y);
		else if (str.equalsIgnoreCase("output"))
			return new Point(output.x, output.y);
		else if (str.equalsIgnoreCase("top"))
			return new Point(upperLeft.x + width/2, upperLeft.y);
		else if (str.equalsIgnoreCase("bottom"))
			return new Point(upperLeft.x + width/2, upperLeft.y + height);
		else  // "leftTop"
			return new Point(upperLeft.x, upperLeft.y);

	} /* end getCoordinates for Adder */


	/**
	 * Führt eine Rechenoperation mit zwei Operanden durch,
	 * aktualisiert auf Wunsch die flags und liefert das Ergebnis zurück.
	 *
	 * @param cm  Durchzuführende Operation: muß "Rechner.ADD_MEM" sein!
	 */
	public int calculate(int cm, int operand1, int operand2, boolean updateFlags)
	{
		if (cm != Rechner.ADD_MEM)
		{
			Rechner.out("FEHLER bei Aufruf von Adder.calculate(): Erster Parameter muß \"Rechner.ADD_MEM\" sein!");
			return -1;
		}
		else
			return super.calculate(Rechner.ADD_MEM, operand1, operand2, updateFlags);
	} /* end calculate (2 operands) */

	/**
	 * Berechnet operand1 + 0,
	 * aktualisiert auf Wunsch die flags und liefert das Ergebnis zurück.
	 *
	 * @param cm  Durchzuführende Operation: muß "Rechner.ADD_MEM" sein!
	 */
	public int calculate(int cm, int operand1, boolean updateFlags)
	{
		return calculate(cm, operand1, 0, updateFlags);
	} /* end calculate (1 operand) */

	/**
	 * Addiert zwei Werte, aktualisiert auf Wunsch die flags und liefert das Ergebnis zurück.
	 */
	public int add(int operand1, int operand2, boolean updateFlags)
	{
		return calculate(Rechner.ADD_MEM, operand1, operand2, updateFlags);
	}

	/**
	 * Addiert zwei Werte, aktualisiert die flags und liefert das Ergebnis zurück.
	 */
	public int add(int operand1, int operand2)
	{
		return calculate(Rechner.ADD_MEM, operand1, operand2, true);
	}

	/**
	 * Addiert beliebig viele Werte, aktualisiert auf Wunsch die flags und liefert das Ergebnis zurück.
	 */
	public int add(int[] operands, boolean updateFlags)
	{
		if ( (operands == null) || (operands.length < 1) )
			return calculate(Rechner.ADD_MEM, 0, 0, updateFlags);
		else if (operands.length == 1)
			return calculate(Rechner.ADD_MEM, operands[0], updateFlags);
		else if (operands.length == 2)
			return calculate(Rechner.ADD_MEM, operands[0], operands[1], updateFlags);
		else
		{
			int result = calculate(Rechner.ADD_MEM, operands[0], operands[1], updateFlags);
			for (int i = 2; i < operands.length; i++)
				result = calculate(Rechner.ADD_MEM, operands[i], result, updateFlags);
			return result;
		}
	} /* end calculate (n operands) */


	/**
	 * Legt den Titel der Komponente fest; nur newLabel[0] wird benutzt.
	 */
	public void setLabel(String[] newLabel)
	{
		if (newLabel != null)
		{
			label = newLabel[0];
			labelX = upperLeft.x + (width - parent.stringWidth(parent.DIALOGFONT, label))/2;
			labelHeight = parent.getHeight(parent.DIALOGFONT);
		}
	}

	/**
	 * 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 String[] getPossibleQualifiers()
	{
		String[] q = {"upperInput", "lowerInput", "output", "top", "bottom", "topLeft"};
		return q;
	}

	/**
	 * Liefert "true", falls p innerhalb der Begrenzungen dieser Komponente liegt.
	 */
	public boolean intersectsWith(Point p)
	{
		return ( (upperLeft.x <= p.x) && (upperLeft.x + width >= p.x) && (upperLeft.y <= p.y) && (upperLeft.y + height >= p.y) );
	}

} /* end Adder */

