package ckelling.baukasten;

import java.awt.*;
import java.lang.*;
import java.util.*;

// Versionsgeschichte
// 0.9.0, 04.03.97
// 0.9.1, 13.06.97

/**
 *	Abzweigung eines SimpleBus.
 *
 *	Die Länge einer "edge" wird als Abstand von DER Seite des
 * 	Busses gerechnet, bei der sie beginnt (variiert also in
 *	Bezug auf die Startkoordinaten des Busses
 *	um LINEWIDTH).
 *
 *	@author		Carsten Kelling
 *	@version	0.9.1, 13.06.97
 */
public class SimpleEdge
{
	protected int[] x;
	protected int[] y;
	private   int[]	realX, realY;
	protected int[]	acOffset;

	/**
	 * Variablen für temporäre Inhalte - durch deren globale Deklaration muß weniger im Speicher aufgeräumt werden.
	 */
	private int		ax, ay, bx, by = 0;
	private int     ao, ac = 0;
	private boolean	negativeCoord = false;

	private String	label;

	private Graphics	graphics;

	private boolean 	activated;
	private boolean 	hasBeenActivated;
	private boolean 	dataFlowReverse;
	private boolean 	dataFlowTo;
	private int 		activationCounter;
	private boolean		withGate;
	private boolean		withSolderDot;
	private int			lw;  // bekommt Wert von parent_rechner.LINEWIDTH
	private int			halfDot;
	private int			dotSize;

	private SimpleBus	parent;
	private Rechner 	parent_rechner;
	private Connection	neighbour;


	public SimpleEdge(String str, int[] ax, int[] ay,
								  boolean withGate,
								  boolean withSolderDot,
								  SimpleBus par, Rechner par_vnr)
	{
		parent = par;
		parent_rechner = par_vnr;
		neighbour = null;
		activated = false;
		hasBeenActivated = false;
		dataFlowReverse = false;
		dataFlowTo = true;
		activationCounter = 0;
		this.withGate = withGate;
		this.withSolderDot = withSolderDot;

		lw = parent_rechner.LINEWIDTH;
		halfDot = (lw+1) / 2;
		dotSize = 2*halfDot + lw;

		label = str;
		x = ax;
		y = ay;
		acOffset = new int[5];
		acOffset[0] = 0;
		for (int i = 1; i < acOffset.length; i++)
			acOffset[i] = -9999;  // muss noch in fillRect() berechnet werden

		if (label.equals("up"))
		{
			acOffset[1] = 0;
			//acOffset[2] = -1;
			//acOffset[3] = 1 - 1;
			//acOffset[4] = -2 + 1 - 1;
		}
		else if (label.equals("down"))
		{
			acOffset[1] = 0;
			//acOffset[2] = 3;
			//acOffset[3] = -2 + 3;
			//acOffset[4] = -2 + 3;
		}

		realX = new int[5];
		realY = new int[5];
		for (int i = 0; i < realX.length; i++)
		{
			realX[i] = 0; realY[i] = 0;
		}

	} /* end SimpleEdge */


	public void paint(Graphics g)
	{
		graphics = g;

		// Die folgende temporären Variablen sind global definiert
		ax = parent.startX + x[0]; ay = parent.startY + y[0];
		bx = x[1]; by = y[1];

		if (activated && withGate)
		{
			g.setColor(parent_rechner.BACKGROUND);
			paintGate(g, ax, ay, bx, by);
		}

		g.setColor(parent_rechner.EDGE_COLOR);

		// Das nun folgende Zeichnen
		// einer Linie mit bis zu drei Ecken
		// ist KORREKT!
		// Bitte nicht mehr modifizieren!

		if (bx == 0)
		// vertical edge
		{
			if (by > 0)
				fillRect(ax, ay + lw, lw, by - lw, 1);
			else
				fillRect(ax, ay, lw, by, 1);

			/* local method fillRect is used! */
		}
		else
		// horizontal edge
		{
			if (bx > 0)
				fillRect(ax + lw, ay, bx - lw, lw, 1);
			else
				fillRect(ax, ay, bx, lw, 1);
		}

		if ( (! activated) && withGate)
		{
			g.setColor(parent_rechner.GATE_COLOR);
			paintGate(g, ax, ay, bx, by);
			g.setColor(parent_rechner.EDGE_COLOR);
		}
		else if (withSolderDot)
		{
			g.setColor(parent_rechner.SOLDER_COLOR);
			g.fillOval(ax - halfDot, ay - halfDot, dotSize, dotSize);
			g.setColor(parent_rechner.EDGE_COLOR);
		}

		if (x.length >= 3)
		{
			ax = ax + bx; ay = ay + by;
			bx = x[2]; by = y[2];
			if (bx == 0)
			// vertical edge
			{
				if (by > 0)
					fillRect(ax, ay, lw, by, 2);
				else
					fillRect(ax, ay + lw, lw, by - lw, 2);
			}
			else
			// horizontal edge
			{
				if (bx > 0)
					fillRect(ax, ay, bx, lw, 2);
				else
					fillRect(ax + lw, ay, bx - lw, lw, 2);
			}


			if (x.length >= 4)
			{
				ax = ax + bx; ay = ay + by;
				bx = x[3]; by = y[3];
				if (bx == 0)
				// vertical edge
				{
					if (by > 0)
						fillRect(ax, ay, lw, by, 3);
					else
						fillRect(ax, ay + lw, lw, by - lw, 3);
				}
				else
				// horizontal edge
				{
					if (bx > 0)
						fillRect(ax, ay, bx, lw, 3);
					else
						fillRect(ax + lw, ay, bx - lw, lw, 3);
				}


				if (x.length >= 5)
				{
					ax = ax + bx; ay = ay + by;
					bx = x[4]; by = y[4];
					if (bx == 0)
					// vertical edge
					{
						if (by > 0)
							fillRect(ax, ay, lw, by, 4);
						else
							fillRect(ax, ay + lw, lw, by - lw, 4);
					}
					else
					// horizontal edge
					{
						if (bx > 0)
							fillRect(ax, ay, bx, lw, 4);
						else
							fillRect(ax + lw, ay, bx - lw, lw, 4);
					}
				} // end if (x.length >= 5)
			} // end if (x.length >= 4)
		} // end if (x.length >= 3)

	} /* end paint */


	public void paintActivated(Graphics g)
	{
		if (activated)
			paint(g);
		else if (hasBeenActivated == true)
		{
			hasBeenActivated = false;
			paint(g);
		}
	}


	private void fillRect(int ax, int ay, int bx, int by, int edgeNumber)
	{
		dataFlowReverse = false;
		realX[edgeNumber] = bx;
		realY[edgeNumber] = by;

		if (bx < 0)
		{
			bx = -bx;
			ax = ax - bx;
			dataFlowReverse = true;
		}

		if (by < 0)
		{
			by = -by;
			ay = ay - by;
			dataFlowReverse = true;
		}

		if (activated)
		{
            negativeCoord = dataFlowReverse;
		    dataFlowReverse = (dataFlowReverse == dataFlowTo);

		    if (edgeNumber <= 1)
		    {
				if (acOffset[1] != -9999)
					ao = acOffset[1];
				else  // Offset muss berechnet werden
				{
					//ao = acOffset[0];

					int summand1 = -1;
					int summand2 = 0;
					boolean previousHadNegativeCoord = true;

					if (x[1] == 0)
					// vertical edge
					{
						if (negativeCoord)
							summand1 = by;
					}
					else  // horizontal edge
					{
						if (negativeCoord)
							summand1 = bx;
					}

					if (summand1 != -1)  // negativeCoord
					{
						//ao = ao - summand1 + lw;  // geprüft; falsch: (-s1+s2) (-s1-s2)
						ao = ao - summand1;  // geprüft; falsch: (-s1+s2) (-s1-s2)
					}
					else
					{
						//ao = ao + 1;  // geprüft; falsch: (lw - s2) (lw-s2+1)
						ao = ao + 1 - lw;  // geprüft; falsch: (lw - s2) (lw-s2+1)
				    }

					while (ao < 0)
						ao += parent_rechner.STRIPEWIDTH;
					ao = ao % parent_rechner.STRIPEWIDTH;
					//debug System.out.println(label + ", " + edgeNumber + ": " + ao + " (" + acOffset[edgeNumber-1] + ", " + summand1 + ", " + previousHadNegativeCoord + ")");
					//debug System.out.println(y[edgeNumber-1]);
					acOffset[1] = ao;
				}

				if (dataFlowTo)
					ac = (activationCounter + ao) % parent_rechner.STRIPEWIDTH;
				else
					ac = (activationCounter + (parent_rechner.STRIPEWIDTH - ao)) % parent_rechner.STRIPEWIDTH;

			    parent.paintDottedLine(graphics, ax, ay, bx, by, ac, dataFlowReverse);
			}
			else
			{
				if (acOffset[edgeNumber] != -9999)
					ao = acOffset[edgeNumber];
				else  // Offset muss berechnet werden
				{
					ao = acOffset[edgeNumber - 1];

					int summand1 = -1;
					int summand2 = 0;
					boolean previousHadNegativeCoord = false;

					if (x[edgeNumber] == 0)
					// vertical edge
					{
						if (negativeCoord)
							summand1 = by;
					}
					else  // horizontal edge
					{
						if (negativeCoord)
							summand1 = bx;
					}

					if (x[edgeNumber - 1] == 0)
					// previous edge was vertical edge
					{
						summand2 = realY[edgeNumber - 1];
						if (y[edgeNumber - 1] < 0)  // dataFlowReverse
							previousHadNegativeCoord = true;
					}
					else  // previous edge was horizontal edge
					{
						summand2 = realX[edgeNumber - 1];
						if (x[edgeNumber - 1] < 0)  // dataFlowReverse
							previousHadNegativeCoord = true;
					}

					if (summand1 != -1)  // negativeCoord
					{
						if (previousHadNegativeCoord)
							ao = ao - summand1 + lw;  // geprüft; falsch: (-s1+s2) (-s1-s2)
							                          //                  (s1) (s1+lw) (s1+s2) (s1-s2)
						else
							ao = ao - summand2 - summand1 + lw - 1;  // geprüft; falsch: (lw) (lw-s2) (-s1-s2) (-s1-s2+lw)
							                                     // (-s1+s2-lw) (-s1+s2+lw) (s1+s2+lw) (-s1-s2-lw)
							                                     // (s1+s2-lw) (s1-s2+lw)
					}
					else
					{
						if (previousHadNegativeCoord)
							ao = ao + 1;  // geprüft; falsch: (lw - s2) (lw-s2+1)
						else
							ao = ao - summand2;  // geprüft
				    }

					while (ao < 0)
						ao += parent_rechner.STRIPEWIDTH;
					ao = ao % parent_rechner.STRIPEWIDTH;
					//debug System.out.println(label + ", " + edgeNumber + ": " + ao + " (" + acOffset[edgeNumber-1] + ", " + summand1 + ", " + previousHadNegativeCoord + ")");
					//debug System.out.println(y[edgeNumber-1]);
					acOffset[edgeNumber] = ao;
				}

				if (dataFlowTo)
					ac = (activationCounter + ao) % parent_rechner.STRIPEWIDTH;
				else
					ac = (activationCounter + (parent_rechner.STRIPEWIDTH - ao)) % parent_rechner.STRIPEWIDTH;

				if (x[edgeNumber - 1] == 0)
				// previous edge was vertical edge
				{
					if (y[edgeNumber - 1] > 0)
					    parent.paintDottedLine(graphics, ax, ay, bx, by, ac, dataFlowReverse, SimpleBus.DIRECTION_DOWN, negativeCoord);
					else
					    parent.paintDottedLine(graphics, ax, ay, bx, by, ac, dataFlowReverse, SimpleBus.DIRECTION_UP, negativeCoord);
				}
				else  // previous edge was horizontal edge
				{
					if (x[edgeNumber - 1] > 0)
					    parent.paintDottedLine(graphics, ax, ay, bx, by, ac, dataFlowReverse, SimpleBus.DIRECTION_RIGHT, negativeCoord);
					else
					    parent.paintDottedLine(graphics, ax, ay, bx, by, ac, dataFlowReverse, SimpleBus.DIRECTION_LEFT, negativeCoord);
				}
			}  // end (edgeNumber >= 1)
		}
		else  // !activated
			graphics.fillRect(ax, ay, bx, by);
	} /* end fillRect */


	private void paintGate(Graphics g, int ax, int ay, int bx, int by)
	{
		if (bx == 0)
		// vertical edge
		{
			if (by > 0)
				g.fillRect(ax - 2, ay + lw, lw + 4, lw);
			else
				g.fillRect(ax - 2, ay - lw, lw + 4, lw);
		}
		else
		// horizontal edge
		{
			if (bx > 0)
				g.fillRect(ax + lw, ay - 2, lw, lw + 4);
			else
				g.fillRect(ax - lw, ay - 2, lw, lw + 4);
		}
	} /* end paintGate */


	public void reshape(boolean parentIsHorizontalBus, int c1, int c2, int c3, int c4, int c5)
	{
		int length = 2;
		if (c3 != -1)
		{
			length = 3;
			if (c4 != -1)
			{
				length = 4;
				if (c5 != -1)
					length = 5;
			}
		}
		x = new int[length]; y = new int[length];

		if (parentIsHorizontalBus)
		{
			x[0] = c1; y[0] = 0;
			x[1] = 0;  y[1] = c2;
			if (c3 != -1)
			{
				x[2] = c3; y[2] = 0;
				if (c4 != -1)
				{
					x[3] = 0; y[3] = c4;
					if (c5 != -1)
						x[4] = c5; y[4] = 0;
				}
			}
		}
		else  // vertical bus
		{
			x[0] = 0 ; y[0] = c1;
			x[1] = c2;  y[1] = 0;
			x[2] = y[2] = x[3] = y[3] = x[4] = y[4] = -1;
			if (c3 != -1)
			{
				x[2] = 0; y[2] = c3;
				if (c4 != -1)
				{
					x[3] = c4; y[3] = 0;
					if (c5 != -1)
						x[4] = 0; y[4] = c5;
				}
			}
		}

		for (int i = 1; i < acOffset.length; i++)
			acOffset[i] = -9999;  // muss noch in fillRect() berechnet werden
	} /* end reshape */


	public Point getCoordinates(String str)
	{
		Point p = new Point(x[0], y[0]);

		if (str.equalsIgnoreCase("end"))
		{
			p.translate(x[1], y[1]);
			if (x.length >= 3)
				p.translate(x[2], y[2]);
			if (x.length >= 4)
				p.translate(x[3], y[3]);
			if (x.length >= 5)
				p.translate(x[4], y[4]);
		}
		else if (! str.equalsIgnoreCase("start"))
			parent_rechner.out("FEHLER: Abzweigungen von Bussen kennen keine solchen Koordinaten: '" + str + "'.");
		return p;
	} /* end getCoordinates for SimpleEdge */


	public boolean intersectsWith(Point p)
	{
		int ax = parent.startX + x[0]; int ay = parent.startY + y[0];
		int bx = x[1]; int by = y[1];
		int left, right, top, bottom;
		int t = 0;

		if (bx == 0)
		// vertical edge
		{
			if (by > 0)
			{
				left = ax; right = left + lw;
				top = ay + lw; bottom = top + by - lw;
			}
			else
			{
				left = ax; right = left + lw;
				top = ay; bottom = top + by;
			}
		}
		else
		// horizontal edge
		{
			if (bx > 0)
			{
				left = ax + lw; right = left + bx - lw;
				top = ay; bottom = top + lw;
			}
			else
			{
				left = ax; right = left + bx;
				top = ay; bottom = top + lw;
			}
		}

		if (left > right) { t = left; left = right; right = t; }
		if (top > bottom) { t = top; top = bottom; bottom = t; }

		if ( (left <= p.x) && (right >= p.x) && (top <= p.y) && (bottom >= p.y) )
			return true;

		if (x.length >= 3)
		{
			ax = ax + bx; ay = ay + by;
			bx = x[2]; by = y[2];
			if (bx == 0)
			// vertical edge
			{
				if (by > 0)
				{
					left = ax; right = left + lw;
					top = ay; bottom = top + by;
				}
				else
				{
					left = ax; right = left + lw;
					top = ay + lw; bottom = top + by - lw;
				}
			}
			else
			// horizontal edge
			{
				if (bx > 0)
				{
					left = ax; right = left + bx;
					top = ay; bottom = top + lw;
				}
				else
				{
					left = ax + lw; right = left + bx - lw;
					top = ay; bottom = top + lw;
				}
			}

			if (left > right) { t = left; left = right; right = t; }
			if (top > bottom) { t = top; top = bottom; bottom = t; }

			if ( (left <= p.x) && (right >= p.x) && (top <= p.y) && (bottom >= p.y) )
				return true;

			if (x.length >= 4)
			{
				ax = ax + bx; ay = ay + by;
				bx = x[3]; by = y[3];
				if (bx == 0)
				// vertical edge
				{
					if (by > 0)
					{
						left = ax; right = left + lw;
						top = ay; bottom = top + by;
					}
					else
					{
						left = ax; right = left + lw;
						top = ay + lw; bottom = top + by - lw;
					}
				}
				else
				// horizontal edge
				{
					if (bx > 0)
					{
						left = ax; right = left + bx;
						top = ay; bottom = top + lw;
					}
					else
					{
						left = ax + lw; right = left + bx - lw;
						top = ay; bottom = top + lw;
					}
				}

				if (left > right) { t = left; left = right; right = t; }
				if (top > bottom) { t = top; top = bottom; bottom = t; }

				if ( (left <= p.x) && (right >= p.x) && (top <= p.y) && (bottom >= p.y) )
					return true;

				if (x.length >= 5)
				{
					ax = ax + bx; ay = ay + by;
					bx = x[4]; by = y[4];
					if (bx == 0)
					// vertical edge
					{
						if (by > 0)
						{
							left = ax; right = left + lw;
							top = ay; bottom = top + by;
						}
						else
						{
							left = ax; right = left + lw;
							top = ay + lw; bottom = top + by - lw;
						}
					}
					else
					// horizontal edge
					{
						if (bx > 0)
						{
							left = ax; right = left + bx;
							top = ay; bottom = top + lw;
						}
						else
						{
							left = ax + lw; right = left + bx - lw;
							top = ay; bottom = top + lw;
						}
					}

					if (left > right) { t = left; left = right; right = t; }
					if (top > bottom) { t = top; top = bottom; bottom = t; }

					if ( (left <= p.x) && (right >= p.x) && (top <= p.y) && (bottom >= p.y) )
						return true;
				} // end if (x.length >= 5)
			} // end if (x.length >= 4)
		} // end if (x.length >= 3)

		return false;

	} /* end intersectsWith */


	public String getLabel()
	{
		return label;
	}

	public void activateTo()
	{
		activated = true;
		hasBeenActivated = true;
		dataFlowTo = true;
	}

	public void activateFrom()
	{
		activated = true;
		hasBeenActivated = true;
		dataFlowTo = false;
	}

	public void deactivate()
	{
		activated = false;
	}

	public boolean isActivated()
	{
		return activated;
	}

	public boolean isActivatedTo()
	{
		return activated && dataFlowTo;
	}

	public boolean isActivatedFrom()
	{
		return activated && (! dataFlowTo);
	}

	public void scroll(int activationCounter)
	{
		//activationCounter++;
		//if (activationCounter == parent_rechner.STRIPEWIDTH)
		//	activationCounter = 0;
		this.activationCounter = activationCounter;
	} /* end scroll */

	public void setGate(boolean b)
	{
		withGate = b;
		if (b)
			withSolderDot = false;
	}

	public void setSolderDot(boolean b)
	{
		if (b)
			withGate = false;
		withSolderDot = b;
	}

	public int getFlags()
	{
		int flags = 0;

		if (withGate)
			flags += SimpleBus.WITH_GATES;
		if (withSolderDot)
			flags += SimpleBus.WITH_SOLDER_DOTS;

		return flags;
	}

	public void setConnection(Connection c)
	{
		neighbour = c;
	}

	public Connection getConnection()
	{
		return neighbour;
	}

	public long getMaxValue()
	{
		if (neighbour != null)
			return neighbour.getMaxValue();
		else
			return Rechner.DEFAULT_MAXVALUE;
	}

} /* end SimpleEdge */
