package ckelling.baukasten;

import java.applet.*;
import java.awt.*;
import java.lang.*;
import java.util.*;
/* import java.io.*; */
import java.net.*;


// Versionsgeschichte
// 0.8.1, 07.02.97
// 0.9.0, 02.08.97  erste Version ohne singleStep()

/**
 *	Komponenten_RAM.java
 *
 *	Demonstration der Rechner-Komponente "RAM"
 *
 *	@author		Carsten Kelling
 *	@version	0.9.0, 02.08.97
 */
public class Komponenten_RAM extends Rechner
{
	///// Konstanten Anfang /////

    public final static String VERSIONSTRING = "Komponenten: RAM 0.9.0";

	// Moegliche Werte fuer c_state stehen nun
	// in der Klasse "Rechner"

    public final static int	CONTROLHEIGHT = 30;

	///// Konstanten Ende /////


	private EditableMemory  ram;

	private SimpleBus		abus;
	private SimpleBus		dbus;
	private SimpleBus		resetBus;

	private Komponenten_RAM_Control komponenten_RAM_Control;
	private DescriptionLibrary	descriptionLibrary;
	public	RTFTextArea		helpText;       // Muss leider public sein...
	//private WrapTextArea		helpText;

	public	EditableLabel		address;	// Muss leider public sein...
	private Register16	 		register;

	private int 				randomAddress;
	private int 				randomInput;
	protected boolean 			randomFlag;
	protected boolean 			useRandomNumbers;

	private int					old_c_state;


	public String getAppletInfo()
	{
		return "Demonstration der Rechner-Komponente 'RAM'";
	}

	public String getVersionString()
	{
		return VERSIONSTRING;
	}

	public void init()
	{
		System.out.println(VERSIONSTRING);
		System.out.println("Initialisierung - Anfang");

		Point coord;

		initialize(new Dimension(510, 400), new Dimension(510, 380)); // defined in class Rechner

		PROGRAM = "3address";  // Parameter in HTML-Datei ignorieren
		useRandomNumbers = true;
		c_state = n_state = old_c_state = READ;

		ram = new EditableMemory("Programm- und Datenspeicher", 10, 30, 8, 8,    MEMORYSIZE, BITWIDTH, (Rechner) this);
		// Format:              (Titel,                         x,  y,  Zellen x, Speichergroesse,      Verweis auf dieses Objekt)
		//                                                                 Zellen y,         Speicherbreite,
		ram.initRam(PROGRAM);
		ram.setOpcodesSupported(false);  // InfoTip zeigt keinen Opcode, sondern nur Wert zu anderer Basis

		// ram = new SimpleMemory("Programm- und Datenspeicher", 10, 10, MEMORYSIZE, BITWIDTH, (Rechner) this);
		// Format: (<Bezeichner>, x, y, Speichergroesse, Bitbreite, Verweis auf Komponenten_RAM)


	//// Busse ////
		coord = ram.getCoordinates("right");
		abus = new SimpleBus("Adreßbus", coord.x, coord.y, 100, 0, (Rechner) this);
		/**
		   Format: (<Bezeichner>, x1, y1, width, height, Referenz auf dieses Objekt um Farb-Konstanten benutzen zu können)
		   Hauptstrang des Busses verläuft von
		   (x1, y1) nach (x1 + width, y1 + height).
		*/
		abus.setLabelPosition("end", "top");
		/**
			Fuer horizontale Busse: (start/end, top/bottom); Standard: (start, top)
			Fuer vertikale Busse:	(left/right, start/end); Standard: (right, start)
		*/

		abus.addEdge("address", 30, 0, 0, 30, 10, 0);
		/**
			Format: (<Bezeichner>, x3, y3, x4, y4)
			Eine Abzweigung vom Bus beginnt bei (x1+x3, y1+y3)
			und verläuft x4 Pixel nach rechts sowie y4 nach unten.
		*/


		coord = ram.getCoordinates("bottom");
		dbus = new SimpleBus("Datenbus", coord.x - 60, coord.y, 0, 85, (Rechner) this);

		resetBus = new SimpleBus("Reset", coord.x + 60 - LINEWIDTH, coord.y, 0, 60, (Rechner) this);

		coord = dbus.getCoordinates("end");
		Point coord2 = dbus.getCoordinates("start");
		coord.translate(-coord2.x, -coord2.y - LINEWIDTH);
		dbus.addEdge("register", 0, coord.y, -30, 0);


	//// Label ////
		coord = abus.getCoordinates("address", "end");
		address = new EditableLabel(coord.x, coord.y, "left", (Rechner) this);
		add(address); address.show();


	//// Register ////
		coord = dbus.getCoordinates("register", "end");
		register = new Register16("Register", coord.x, coord.y, "right", (Rechner) this);
		register.setEditable(true);
		add(register);
		register.show();

		setComponentsEditableOrNot();

		//alu = new ALU("ALU", coord.x, coord.y, "right", (Rechner) this);


	//// Connections ////
		// Den Bussen wird mitgeteilt, welche Komponenten an welchen Kanten angeschlossen sind;
		// bei einem bus.activate() kann der Bus dann den passenden Wert annehmen
		// und im InfoTip zeigen.

		abus.setConnection("start", ram, OUT);
		abus.setConnection("address", address, IN);

		dbus.setConnection("start", ram, INOUT);
		dbus.setConnection("register", register, INOUT);


		coord = resetBus.getCoordinates("end");
		coord.translate(0, CONTROLHEIGHT + LINEWIDTH);
		helpText = new RTFTextArea(SMALLFONT, 4, 70);
		//helpText = new RTFTextArea(new Font("TimesRoman", Font.PLAIN, 12), 4, 70);
		//helpText = new WrapTextArea(SMALLFONT, 4, 70);

		helpText.setText("Hier werden Hilfetexte erscheinen.");
		helpText.move(0, coord.y + 15);
		helpText.resize(WIDTH, HEIGHT - coord.y - 15);
		descriptionLibrary = new DescriptionLibrary();

		setLayout (new BorderLayout() );
		add("South", helpText);

		komponenten_RAM_Control = new Komponenten_RAM_Control(this);
		komponenten_RAM_Control.setTitle("RAM steuern");
		//add (komponenten_RAM_Control);
		//komponenten_RAM_Control.pack();
		double faktor = SMALLFONTSIZE / (double) NORMALFONTSIZE;
		komponenten_RAM_Control.resize((int) (375*faktor), (int) (160*faktor));

		Rectangle bounds = bounds();


		System.out.println("Initialisierung - Ende");

		show();
		komponenten_RAM_Control.show();
		helpText.show();

		randomAddress = randomInput = 0;
		randomFlag = true;

		versionStringX = WIDTH - stringWidth(DIALOGFONT, VERSIONSTRING) - 5;
		versionStringY = helpText.bounds().y - DIALOGFONTHEIGHT;

		paint(onScreenGC);
} /* end init */


	public void stop()
	{
		super.stop();  // Alle Threads anhalten und beseitigen

		komponenten_RAM_Control.hide();
	}

	public void start()
	{
		super.start();  // Alle Threads erzeugen und starten

		Rectangle bounds = bounds();
		Rectangle bbounds = komponenten_RAM_Control.bounds();
		int x = Math.min(bounds.x + bounds.width, screenSize.width - bbounds.width);
		komponenten_RAM_Control.move(x, bounds.y);
		komponenten_RAM_Control.show();
	}


	/**
		this method is called when repaint of the panel is requested
	*/

	public synchronized void paint (Graphics onScreenGC)
	{
		if (offScreenGC == (Graphics) null)
			out("Oh Gott! paint(Graphics) wurde vor init() aufgerufen!");

		Rectangle size = bounds();

		// Flaeche des Applets loeschen
		offScreenGC.setColor (BACKGROUND);
		offScreenGC.fillRect(0, 0, size.width, size.height);

		// Komponenten neu zeichnen
		paintComponents(offScreenGC);

		offScreenGC.setColor(Color.black);

		String stw = new String("Steuerwerk");
		int labelHeight = getHeight(DIALOGFONT);
		int labelWidth = stringWidth(DIALOGFONT, stw);

		Point coord = resetBus.getCoordinates("end");
		int gap = 5;
		coord.translate(-labelWidth/2 - gap, 0);
		offScreenGC.drawRect(coord.x,   coord.y,	labelWidth + 2*gap,	 	CONTROLHEIGHT);
		offScreenGC.drawRect(coord.x+1, coord.y+1,	labelWidth + 2*gap-2,	CONTROLHEIGHT-2);
		offScreenGC.drawString(stw, coord.x + gap, coord.y + (CONTROLHEIGHT+labelHeight)/2 - 3);

		offScreenGC.drawString(VERSIONSTRING, versionStringX, versionStringY);

		onScreenGC.drawImage(offScreenImage, 0, 0, this);

		ram.paint(onScreenGC);
		helpText.repaint();
		//komponenten_RAM_Control.paint(onScreenGC);
	} /* end paint */

	public synchronized void paintComponents (Graphics g)
	{
		abus.		paint(g);
		dbus.		paint(g);
		resetBus.	paint(g);

		address.	paint(g);
		register.	paint(g);

		//komponenten_RAM_Control.paint(g);
	} /* end paintComponents */

	public synchronized void paintActivated (Graphics onScreenGC)
	{
		abus.		paintActivated(offScreenGC);
		dbus.		paintActivated(offScreenGC);
		resetBus.	paintActivated(offScreenGC);

		address.	paintActivated(offScreenGC);
		register.	paintActivated(offScreenGC);

		onScreenGC.drawImage(offScreenImage, 0, 0, this);

		ram.		paintActivated(onScreenGC);

	} /* end paintActivated */

	public void deactivateAll()
	{
		ram.		deactivate();

		abus.		deactivate();
		dbus.		deactivate();
		resetBus.	deactivate();

		address.	deactivate();
		register.	deactivate();
	} /* end deactivateAll */


	public synchronized void scrollAll()
	{
		abus.		scroll();
		dbus.		scroll();
		resetBus.	scroll();

		abus.		paintActivated(offScreenGC);
		dbus.		paintActivated(offScreenGC);
		resetBus.	paintActivated(offScreenGC);

		onScreenGC.drawImage(offScreenImage, 0, 0, this);
	} /* end scrollAll */


	/**
	 *  Alle Komponenten, die zwischen ihrem eigentlichem Wert (value)
	 *  und dem gerade sichtbaren Wert (displayValue) unterscheiden,
	 *  führen ein "displayValue = value" durch.
	 *  Wird stets von deactivateAll() gefolgt.
	 */
	public void updateAll()
	{
		ram.updateAll();

		address.	activate();
		register.	activate();

		//alu.activate();
	} /* end updateAll */


	public void downdateAll()
	// Sehr witzige Bezeichnung:
	// Die EditableLabel, die zwischen ihrem eigentlichem Wert (value)
	// und dem gerade sichtbaren Wert (displayValue) unterscheiden UND
	// in denen etwas eingegeben worden sein kann,
	// führen ein value = displayValue durch.
	{
		address.	setValue(address.getDisplayValue());

	} /* end downdateAll */


	public void showInfoTip(Point p)
	{
		if (infoTipLabel == null)
			return;

		String itt = "";
		//Color bg = MEDIUM_GREY;
		Color bg = Color.yellow;

		if (abus.intersectsWith(p))
			itt = "Adreßbus: " + abus.getInfoTipText(p);
		else if (dbus.intersectsWith(p))
			itt = "Datenbus: " + dbus.getInfoTipText(p);
		else if (resetBus.intersectsWith(p))
			itt = "Resetleitung: " + resetBus.getInfoTipText(p);
		else if (register.intersectsWith(p))
			itt = "Register : " + register.getInfoTipText(p);
		else if (address.intersectsWith(p))
			itt = "Adresse: " + address.getInfoTipText(p);
		else if (ram.intersectsWith(p))
		{
			String ritt = ram.getInfoTipText(p);
			if (! ritt.equals(""))
				itt = "Programm- und Datenspeicher: " + ritt;
		}

		//if (itt.equals(""))
		//	itt = "Zeigen Sie auf eine Komponente";
		//else
		//	bg = Color.yellow;

		int stringWidth = stringWidth(SMALLFONT, itt);
		int gap = 16;
		if (p.x + gap + stringWidth > bounds().width)
			p = new Point(p.x - stringWidth - (3*gap)/2, p.y);
		if (p.y + SMALLFONTHEIGHT > bounds().height)
			p = new Point(p.x, p.y - SMALLFONTHEIGHT);

		symantec.itools.awt.InfoTipManager.draw(p.x + gap, p.y, itt, SMALLFONTMETRICS, bg, Color.black);
		Rectangle b = infoTipPanel.bounds();
		infoTipLabel.setText(itt);
		infoTipLabel.reshape(0, 0, b.width, b.height);
		infoTipLabel.setBackground(bg);

	} /* end showInfoTip */


	public synchronized void invalidateAllImageCaches()
	{
		ram.		invalidateImageCache();

		abus.		invalidateImageCache();
		dbus.		invalidateImageCache();
		resetBus.	invalidateImageCache();

	} /* end invalidateAllImageCaches() */


	public void showAllOpcodes(boolean b)
	{
		showOpcodes = b;
		ram.	showOpcodes(b);

		paint(onScreenGC);
	}


	public synchronized void timerWokeUp(String title)
	{
		if (title.equalsIgnoreCase("blinktimer"))
		{
			blinker = !blinker;

			ram.		blink(blinker);

			address.	blink(blinker);
			register.	blink(blinker);

		    onScreenGC.drawImage(offScreenImage, 0, 0, this);
		}
		// else if (title.equalsIgnoreCase("dotMover")) ...
		else
			super.timerWokeUp(title);

	} /* end timerWokeUp */


	public void stopSimulation()
	{
		demonstrationReady = true;
		c_state = n_state = READ;

		address.setValue(0);
		register.setValue(0);
		//alu.calculate(ADD_MEM, 0, 0, true);
		ram.initRam(PROGRAM);

		helpText.setText(descriptionLibrary.helpText_Komponenten_RAM(c_state, 1) );

		updateAll();
		deactivateAll();
		paint(onScreenGC);
	} /* end stopSimulation */

	public void goUntilBreakpoint()
	// ">> BP"
	{
	}

	public void singleInstruction(boolean doRepaint)
	// ">>"
	{
	} /* end singleInstruction */

	public void singleInstructionBack(boolean doRepaint)
	// "<<"
	{
	} /* end singleInstructionBack */

	public synchronized void bufferComputer()
	{
		Vector v = new Vector(4);

		v.addElement(ram.getAllValues());

		v.addElement(new Integer(address.getValue()));
		v.addElement(new Integer(register.getValue()));

		simBuffer[simBufferTop] = v;

		simBufferTop = (simBufferTop+1) % SIM_BUFFER_SIZE;
		if (simBufferTop == simBufferBottom)
			simBufferBottom = (simBufferBottom+1) % SIM_BUFFER_SIZE;

	} /* end bufferComputer */

	public synchronized void setComputer()
	{
		if (simBufferTop == simBufferBottom)
			System.out.println("'<' oder '<<': Keine weiteren Arbeitsschritte mehr gespeichert!");
		else
		{
			simBufferTop--;
			if (simBufferTop == -1)
				simBufferTop = SIM_BUFFER_SIZE - 1;
			//debug System.out.println("setComputer - simBufferTop = " + simBufferTop);

			Vector v = simBuffer[simBufferTop];

			ram.setValue((int[]) v.elementAt(0));

			address.	setValue( ((Integer) v.elementAt(1)).intValue() );
			register.	setValue( ((Integer) v.elementAt(2)).intValue() );
		}
	} /* end setComputer */


	public void demonstrate()
	{
		demonstrate(true);
	}

	public void demonstrate(boolean doRepaint)
	// ">"
	{
		if (demonstrationReady == true)
		{
			if (doRepaint == true)
			{
				deactivateAll();
				helpText.setText(descriptionLibrary.helpText_Komponenten_RAM(c_state, 99) );
				paint(onScreenGC);
			}

			old_c_state = c_state;  // bei Reset benoetigt
			c_state = n_state;

			demonstrationReady = false;
			demonstrationStep = 1;
			setComponentsEditableOrNot();
			//debug System.out.println("- ready");
		}
		else
		{
			//debug System.out.println("- " + demonstrationStep);
			switch (c_state)
			{
				case READ:
					switch (demonstrationStep)
					{
						case 1:
								bufferComputer();
								if (randomFlag == true)
									randomAddress = (int) (Math.random() * ram.getMemorySize());
								else
									randomFlag = true;
								if (useRandomNumbers == true)
									address.setValue(randomAddress);
							if ((komponenten_RAM_Control.actionRead.getState() == false) && (komponenten_RAM_Control.actionWrite.getState() == false))
							// seltener Fehler: Weder Schreiben noch Lesen ist aktiviert.
								komponenten_RAM_Control.actionRead.setState(true);
							address.activate();
							break;
						case 2:
							//if ((dotMoveThread != null) && (dotMoveThread.isAlive() == true))
							//	dotMoveThread.stop();
							abus.activate("address", "start");
							break;
						case 3:
							//clockBus.setMoveDotMode(true);
							//clockBus.activate("end", "start");

							//if ((dotMoveThread == (TimerThread) null) || (dotMoveThread.isAlive() == false))
							//{
							//	dotMoveThread = new TimerThread(Rechner.DOTMOVETIME, "dotmover", this);
							//	dotMoveThread.start();
							//}
								ram.setAddress(address.getValue());

							ram.activate();
							break;
						case 4:
							dbus.activate("start", "register");
							//clockBus.deactivate();
							break;
						case 5:
							//if ((dotMoveThread != null) && (dotMoveThread.isAlive() == true))
							//	dotMoveThread.stop();
								register.setValue(ram.getValue());
								if (komponenten_RAM_Control.actionWrite.getState() == true)
									n_state = WRITE;
								else
									n_state = READ;
							register.activate();
							demonstrationReady = true;
					}
					break;
				case WRITE:
					switch (demonstrationStep)
					{
						case 1:
								bufferComputer();
								if (randomFlag == true)
								{
									randomAddress = (int) (Math.random() * ram.getMemorySize());
									randomInput   = (int) (Math.random() * Math.pow(2, (double) BITWIDTH-1));
								}
								else
									randomFlag = true;
								if (useRandomNumbers == true)
								{
									address.  setValue(randomAddress);
									register. setValue(randomInput);
								}
							if ((komponenten_RAM_Control.actionRead.getState() == false) && (komponenten_RAM_Control.actionWrite.getState() == false))
							// seltener Fehler: Weder Schreiben noch Lesen ist aktiviert.
								komponenten_RAM_Control.actionWrite.setState(true);
							address.activate();
							break;
						case 2:
							abus.activate("address", "start");
							break;
						case 3:
							register.activate();
							break;
						case 4:
							dbus.activate("register", "start");
							break;
						case 5:
								ram.setAddress(address.getValue());
								ram.setValue(register.getValue());
								if (komponenten_RAM_Control.actionRead.getState() == true)
									n_state = READ;
								else
									n_state = WRITE;
							ram.activate();
							demonstrationReady = true;
					}
					break;
				case RESET:
					switch (demonstrationStep)
					{
						case 1:
								bufferComputer();
								resetBus.setValue(1);  // für den InfoTip
							resetBus.activate("end", "start");
							break;
						case 2:
								ram.initRam(PROGRAM);
								if (komponenten_RAM_Control.actionRead.getState() == true)
									n_state = READ;
								else if (komponenten_RAM_Control.actionWrite.getState() == true)
									n_state = WRITE;
								else
									n_state = old_c_state;
							//ram.updateAll();  // wird schon durch initRam() durchgefuehrt
							paint(onScreenGC);
							demonstrationReady = true;
					}
					break;
				case UNKNOWN_COMMAND:
				default:
					out("     --- Unbekannte Vorgehensweise! ---");
					c_state = UNKNOWN_COMMAND;
					n_state = READ;
					demonstrationReady = true;
					break;
			}
			if (doRepaint == true)
				helpText.setText(descriptionLibrary.helpText_Komponenten_RAM(c_state, demonstrationStep) );

			demonstrationStep++;

			if (doRepaint == true)
				setComponentsEditableOrNot();

		}
	} /* end demonstrate */

	public synchronized void demonstrateBack()
	{
		if (demonstrationStep > 1)
		{
			randomFlag = false;

			setComputer();

			//debug System.out.println("--demonstrateBack()");
			updateAll();
			deactivateAll();
			//paintComponents(offScreenGC);

			int ds = demonstrationStep - 1;
			demonstrationStep = 1;
			demonstrationReady = false;

			while (demonstrationStep < ds-1)
			{
				demonstrate(false);
				// do not perform any drawing
			}
			if (ds > 1)
			    demonstrate(true);
			else
			{
				setComponentsEditableOrNot();
				if (c_state != n_state)
				{
					c_state = n_state;
					randomFlag = true;
				}
    			helpText.setText("\nBitte drücken Sie '>'.");
    		}

			paintActivated(onScreenGC);
		}
	} /* end demonstrateBack */


	/**
	 * Stellt fuer das RAM und die beiden editierbaren Register ein, ob diese
	 * momentan editierbar sind oder nicht.
	 * Caveat: Diese Methode wird erst nach 'demonstrationStep++' in demonstrate()
	 * aufgerufen. Dadurch wird (demonstrationStep == 1) von "(demonstrationStep == 0)"
	 * (bevor das erste Mal '>' gedrueckt wurde) unterscheidbar.
	 */
	public void setComponentsEditableOrNot()
	{
		if ( (demonstrationStep == 1) || (demonstrationStep > 2) )
			ram.setEditable(true);
		else
			ram.setEditable(false);

		switch (c_state)
		{
			case READ:
			case READ + 10:
				if (useRandomNumbers)
				{
					address.setEditable(false);
					register.setEditable(true);
				}
				else
				{
					if ( (demonstrationStep == 1) || (demonstrationStep > 5) )
						address.setEditable(true);
					else
						address.setEditable(false);
					register.setEditable(true);
				}
				break;
			case WRITE:
			case WRITE + 10:
				if (useRandomNumbers)
				{
					address.setEditable(false);
					register.setEditable(false);
				}
				else
				{
					if (demonstrationStep == 1)
						address.setEditable(true);
					else
						address.setEditable(false);

					if (demonstrationStep <= 3)
						register.setEditable(true);
					else
						register.setEditable(false);
				}
				break;
			case RESET:
				address.setEditable(true);
				register.setEditable(true);
		}
	} /* end setComponentsEditableOrNot */


	public int getDemonstrationStep()
	{
		return demonstrationStep;
	}

	public void initCacheComponents(int cSize, int tSize, int assoc) {}
	public void removeCacheRessources() {}

	public void showWindow(String name) {}
	public void hideWindow(String name) {}

	//{{DECLARE_CONTROLS
	//}}
} /* end Komponenten_RAM */
