package ckelling.baukasten;

import java.awt.*;
import java.lang.*;
import java.util.*;

// Versionsgeschichte
// 0.2.1, 03.09.96
// 0.3.0, 01.08.97

/**
 *	Simuliert ein RAM und zeichnet dieses.
 *	Speicherzellen sind editierbar, falls nicht gesperrt.
 *
 *	@author		Carsten Kelling
 *	@version	0.3.0, 01.08.97
 */
public class EditableMemory extends SimpleMemory
{
   	///// Konstanten Anfang /////

    public final static int NOT_AVAILABLE	= -1;
    public final static int WRITE_THROUGH  = 10;
    public final static int WRITE_BACK     = 11;
    public final static int LRU            = 20;
    public final static int RANDOM         = 21;
    public final static int FULL           = 0;
    public final static int DIRECT_MAPPED  = 1;

   	///// Konstanten Ende /////


    protected 	EditableMemory_Resource		bild;
    protected	Label						label;

    protected	boolean		tagMode = false;

    protected	int 		topLine;
    protected	int 		maxLine;
    protected	int 		cellX, cellY;
	public		boolean 	withScrollbar;

 	protected	Color       farben[];
	protected	Colorpoint	displayFarben[];
	private		Image		imageCache;

	protected	int			lineSize = 1;
	private		int			savedLineAddress;
	private		int			savedLineValue[];

	private		boolean		breakpointsAllowed;
	private		boolean[]	breakpoints;
	private 	boolean		opcodesSupported;

	protected	Vector		connectionsAddress;

	public EditableMemory(String title, int x1, int y1, int cellX, int cellY, int memsize, int bitwidth, Rechner par)
	{
	    initialize(title, x1, y1, cellX, cellY, memsize, bitwidth, par);
	}

	protected void initialize(String title, int x1, int y1, int cellX, int cellY, int memsize, int bitwidth, Rechner par)
	{
		parent = par;
		startX = x1; startY = y1;

		memorySize = memsize;
		bitWidth = bitwidth;
		zwei_hoch_bitWidth = (long) Math.pow(2, (double) bitWidth);
		speicher = new int[memorySize];
		displaySpeicher = new String[memorySize];

		breakpointsAllowed = false;
		breakpoints		= new boolean[memorySize];
		opcodesSupported = true;
	    farben			= new Color[memorySize];
	    displayFarben	= new Colorpoint[memorySize];

		initRam();

		topLine = 0;
		maxLine = (memorySize / cellX) - cellY;
		this.cellX = cellX; this.cellY = cellY;

		activated = false;
		hasBeenActivated = false;

		imageCache = null;

		savedLineAddress = -1;
		savedLineValue = new int[lineSize];
		for (int i = 0; i < lineSize; i++)
			savedLineValue[i] = -(i+1);

		showOpcodes = false;
		technicalOpcodes = false;

		address = 0;
        withScrollbar = false;
        if (memorySize > cellX * cellY)
            withScrollbar = true;
        if ( ((cellX == 1) || (cellX == 2) || (cellX == 4) || (cellX == 8)) &&
             ((cellY == 4) || (cellY == 8)) )
        {
            bild = new EditableMemory_Resource(cellX, cellY, this);
			parent.add(bild);
			bild.show();  // ruft move() auf
        }
        else
            Rechner.out("EditableMemory: Erlaubte Werte für Breite in Zellen: 1, 2, 4, 8. Erluabte Werte für Höhe in Zellen: 4, 8.");

        String[] strArray = {title};
        setLabel(strArray);

        connectionsAddress = null;

	} /* end initialize */


	public void initRam(String memoryContents)
	{
		super.initRam(memoryContents);

		for (int i = 0; i < memorySize; i++)
		{
			breakpoints[i] = false;
	        displayFarben[i] = new Colorpoint();
	        farben[i] = displayFarben[i].background = parent.BACKGROUND;
	        displayFarben[i].foreground = parent.MEM_COLOR;
	    }

	    if (memoryContents.equalsIgnoreCase("bubblesort_s"))
	    {
	    	setBreakpoint(0x19, true);
	    	update(0x19);
	    	paint(parent.onScreenGC);
	    }
	} /* end initRam */


	public void paint(Graphics g)
	{
    	bild.paint(g);
	}

	public void paintActivated(Graphics g)
	{
		if (activated || hasBeenActivated)
		{
			if (activated == false)
				hasBeenActivated = false;

		    bild.paint(g);
		}
	}


	public int getLineSize()
	{
		return lineSize;
	}

	public Dimension getSizeInCells()
	{
		return new Dimension(cellX, cellY);
	}

	public int getBitWidth()
	{
		return bitWidth;
	}

	public int getMemorySize()
	{
		return memorySize;
	}

	public int getWriteMode()
	{
		return NOT_AVAILABLE;
	}

	public void setLineSize(int l)
	{
		lineSize = l;
	}

	public void setEditable(boolean b)
	{
		bild.setEditable(b);
	}

	public boolean isEditable()
	{
		return bild.isEditable();
	}

	public void allowBreakpoints(boolean b)
	{
		breakpointsAllowed = b;
	}

	public boolean breakpointsAllowed()
	{
		return breakpointsAllowed;
	}

	public void setBreakpoint(int index, boolean b)
	{
		if (breakpointsAllowed)
		{
			breakpoints[index] = b;
			if (b == true)
				farben[index] = parent.MEM_COLOR_BREAKPOINT;
			else
				farben[index] = parent.BACKGROUND;
		}
	}


	public boolean hasBreakpoint()
	{
		for (int i = 0; i < breakpoints.length; i++)
			if (breakpoints[i] == true)
				return true;

		return false;
	}


	public boolean isBreakpoint(int index)
	{
		return breakpoints[index];
	}


    public EditableMemory_Resource getRessource()
    {
        return bild;
    }


	public Point getCoordinates(String str)
	{
	    return bild.getCoordinates(str);
	}

	public String[] getPossibleQualifiers()
	{
		String[] q = {"left", "right", "top", "bottom", "leftTop", "rightTop", "leftBottom", "rightBottom"};
		return q;
	}


	public void activate()
	{
		activate(parent.MEM_COLOR_ACTIVATED, parent.MEM_COLOR_ACTIVATED_LINE);
	}

	public void activate(Color restColor)
	{
		activate(parent.MEM_COLOR_ACTIVATED, restColor);
	}

	public void activate(Color selectColor, Color restColor)
	{
		if (! activationLocked)
		{
			activated = true;
			hasBeenActivated = true;

			updateText();                              // die gesamte Cache-Line in Text
			updateForeground(selectColor, restColor);  // und Farben
			updateBackground();                        // aktualisieren

			if (withScrollbar)
			{
			    if (bild.atWhichCell(address) == -1)
			    // Adresse ist momentan nicht zu sehen
			        bild.makeVisible(address);
			}
			bild.setAddress(address);
		}
	}

	public void activateCompared()
	{
		if (! activationLocked)
		{
			activated = true;
			hasBeenActivated = true;

			if (showOpcodes)
			    displaySpeicher[address] = toOpcode(speicher[address], zwei_hoch_bitWidth);
			else
			    displaySpeicher[address] = parent.expandToString(speicher[address], zwei_hoch_bitWidth);

	   		displayFarben[address].foreground = parent.MEM_COLOR_COMPARED;
	   		displayFarben[address].background = farben[address];
	   	}
	}

	public void deactivate()
	{
		if (! activationLocked)
		{
			activated = false;
			for (int i = 0; i < memorySize; i++)
				displayFarben[i].foreground = parent.MEM_COLOR;
		}
	}

	public void read()
	{
		Connection address = null;

		if (connectionsAddress == null)
		{
			String intro;

			if (label.getText() == "")
				intro = "Für eine Instanz ohne Titel";
			else
				intro = "Für die Instanz \"" + label.getText() + "\"";

			if (connectionsAddress == null)
				Rechner.out("FEHLER in EditableMemory.write(): " + intro + " von " + getClass().getName() + " sind keine Adreßeingänge definiert!");
			return;
		}

		for (int i = 0; i < connectionsAddress.size(); i++)
			if ( ((Connection) connectionsAddress.elementAt(i)).isActivated() )
			{
				if (address != null)  // mehr als ein aktivierter Eingang
				{
					String intro;

					if (label.getText() == "")
						intro = "Bei einer Instanz ohne Titel";
					else
						intro = "Bei der Instanz \"" + label.getText() + "\"";

					Rechner.out("FEHLER in EditableMemory.write(): " + intro + " von " + getClass().getName() + " ist mehr als ein Adreßeingang aktiviert!");
					return;
				}
				else
					address = (Connection) connectionsAddress.elementAt(i);
			}

		if (address == null)
		{
			String intro;

			if (label.getText() == "")
				intro = "Bei einer Instanz ohne Titel";
			else
				intro = "Bei der Instanz \"" + label.getText() + "\"";

			if (address == null)
				Rechner.out("FEHLER in EditableMemory.write(): " + intro + " von " + getClass().getName() + " ist kein Adreßingang aktiviert!");
			return;
		}

		setAddress(address.getValue());
		activate();
	} /* end read */

	public void write()
	{
		Connection input = null;
		Connection address = null;

		if ( (connectionsIn == null) || (connectionsAddress == null) )
		{
			String intro;

			if (label.getText() == "")
				intro = "Für eine Instanz ohne Titel";
			else
				intro = "Für die Instanz \"" + label.getText() + "\"";

			if (connectionsIn == null)
				Rechner.out("FEHLER in EditableMemory.write(): " + intro + " von " + getClass().getName() + " sind keine Eingänge definiert!");

			if (connectionsAddress == null)
				Rechner.out("FEHLER in EditableMemory.write(): " + intro + " von " + getClass().getName() + " sind keine Adreßeingänge definiert!");
			return;
		}

		for (int i = 0; i < connectionsIn.size(); i++)
			if ( ((Connection) connectionsIn.elementAt(i)).isActivated() )
			{
				if (input != null)  // mehr als ein aktivierter Eingang
				{
					String intro;

					if (label.getText() == "")
						intro = "Bei einer Instanz ohne Titel";
					else
						intro = "Bei der Instanz \"" + label.getText() + "\"";

					Rechner.out("FEHLER in EditableMemory.write(): " + intro + " von " + getClass().getName() + " ist mehr als ein Eingang aktiviert!");
					return;
				}
				else
					input = (Connection) connectionsIn.elementAt(i);
			}

		for (int i = 0; i < connectionsAddress.size(); i++)
			if ( ((Connection) connectionsAddress.elementAt(i)).isActivated() )
			{
				if (address != null)  // mehr als ein aktivierter Eingang
				{
					String intro;

					if (label.getText() == "")
						intro = "Bei einer Instanz ohne Titel";
					else
						intro = "Bei der Instanz \"" + label.getText() + "\"";

					Rechner.out("FEHLER in EditableMemory.write(): " + intro + " von " + getClass().getName() + " ist mehr als ein Adreßeingang aktiviert!");
					return;
				}
				else
					address = (Connection) connectionsAddress.elementAt(i);
			}

		if ( (input == null) || (address == null) )
		{
			String intro;

			if (label.getText() == "")
				intro = "Bei einer Instanz ohne Titel";
			else
				intro = "Bei der Instanz \"" + label.getText() + "\"";

			if (input == null)
				Rechner.out("FEHLER in EditableMemory.write(): " + intro + " von " + getClass().getName() + " ist kein Eingang aktiviert!");

			if (address == null)
				Rechner.out("FEHLER in EditableMemory.write(): " + intro + " von " + getClass().getName() + " ist kein Adreßingang aktiviert!");
			return;
		}

		setAddress(address.getValue());
		setValue(input.getValue());
		activate();
	} /* end write */

	/**
	 * Macht dem RAM bekannt, daß an sie eine andere Komponente
	 * angeschlossen ist. Diese kann aus der Sicht des RAMs
	 * ein Dateneingang, ein Datenausgang, beides oder ein Adreßeingang sein.
	 *
	 * @param type Rechner.IN, OUT, INOUT oder ADDRESS_IN
	 */
	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 if (type == Rechner.ADDRESS_IN)
			connectionsAddress.addElement(neighbour);
		else
			Rechner.out("FEHLER in EditableMemory.setConnection(): Unbekannter Typ \"" + type + "\" - erlaubt: Rechner.IN, OUT, INOUT, ADDRESS_IN.");
	} /* end setConnection */


	/**
	 *  Der in der aktuellen Speicherzelle (durch setAddress() bestimmt)
	 *  angezeigte Wert wird auf den Stand des eventuell
	 *  durch setValue() geänderten korrespondierenden internen Wertes gebracht.
	 *  Die Hintergrundfarben der aktuellen "line" werden aktualisiert.
	 *  Hat nichts mit java.awt.Component.update() zu tun.
	 *
	 *  @see #deactivate
	 *  @see #activate
	 */
	public void update()
	{
		updateText();
		updateBackground();
	}

	public void update(int updateAddress)
	{
		int oldAddress = address;
		setAddress(updateAddress);
		update();
		setAddress(oldAddress);
	}

	public void updateText()
	{
		if ((lineSize == 1) || (tagMode))  // Der Tag-Speicher hat immer eine 'line size' von 1
			if (showOpcodes)
			    displaySpeicher[address] = toOpcode(speicher[address], zwei_hoch_bitWidth);
			else
			    displaySpeicher[address] = parent.expandToString(speicher[address], zwei_hoch_bitWidth);
		else
		{
			int lineAddress = address & (memorySize - lineSize);
			for (int i = 0; i < lineSize; i++)
			{
				if (showOpcodes)
				    displaySpeicher[lineAddress + i] = toOpcode(speicher[lineAddress + i], zwei_hoch_bitWidth);
				else
				    displaySpeicher[lineAddress + i] = parent.expandToString(speicher[lineAddress + i], zwei_hoch_bitWidth);
			}
		}
	} /* end updateText */


	public void updateForeground(Color selectColor, Color restColor)
	{
		if ((lineSize == 1) || (tagMode)) // Der Tag-Speicher hat immer eine 'line size' von 1
	   		displayFarben[address].foreground = selectColor;
		else
		{
			int lineAddress = address & (memorySize - lineSize);
			for (int i = 0; i < lineSize; i++)
			{
				if (lineAddress+i == address)
			   		displayFarben[lineAddress + i].foreground = selectColor;
			   	else
			   		displayFarben[lineAddress + i].foreground = restColor;
			}
		}
	} /* end updateForeground */


	public void updateBackground()
	{
		if ((lineSize == 1) || (tagMode)) // Der Tag-Speicher hat immer eine 'line size' von 1
	        displayFarben[address].background = farben[address];
		else
		{
			int lineAddress = address & (memorySize - lineSize);
			for (int i = 0; i < lineSize; i++)
		        displayFarben[lineAddress + i].background = farben[lineAddress + i];
		}
	} /* end updateBackground */


	public void updateAll()
	{
		for (int i = 0; i < memorySize; i++)
		{
			if (showOpcodes)
				displaySpeicher[i] = toOpcode(speicher[i], zwei_hoch_bitWidth);
			else
				displaySpeicher[i] = parent.expandToString(speicher[i], zwei_hoch_bitWidth);

			displayFarben[i].background = farben[i];
			//displayFarben[i].foreground = parent.MEM_COLOR;
		}
	}


	public synchronized void blink(boolean blinker)
	{
		if (activated == true)
		{
		    bild.blink(blinker);
		}
	}

	public void saveLine()
	{
		savedLineAddress = address & (memorySize - lineSize);
		savedLineValue = new int[lineSize];
		for (int i = 0; i < lineSize; i++)
			savedLineValue[i] = speicher[savedLineAddress + i];
	}

	public void restoreLine()
	{
		for (int i = 0; i < lineSize; i++)
			speicher[savedLineAddress + i] = savedLineValue[i];
	}


	public void showOpcodes(boolean b)
	{
		if (! opcodesSupported)
			return;

		if (b != showOpcodes)
		{
			showOpcodes = b;
			updateAll();
		}
	}

	public boolean opcodesAreShowing()
	{
		return showOpcodes && opcodesSupported;
	}

	public void setOpcodesSupported(boolean b)
	{
		opcodesSupported = b;
		if (! opcodesSupported)
			showOpcodes = false;
	}

	public boolean getOpcodesSupported()
	{
		return opcodesSupported;
	}


	/**
	 * Die Parameter width und height werden ignoriert, die Breite oder Höhe
	 * eines EditableMemory läßt sich nur bei der Initialisierung über die
	 * Parameter cellX und cellY (Breite/Höhe in ZELLEN) einstellen.
	 */
	public void reshape(int x, int y, int width, int height)
	{
		if (x != startX || y != startY)
		{
			startX = x; startY = y;
			bild.move(x, y);
		}
	}

	/**
	 * Der Parameter grabMode wird immer als "leftTop" angenommen.
	 * Die Parameter width und height werden ignoriert, die Breite oder Höhe
	 * eines EditableMemory läßt sich nur bei der Initialisierung über die
	 * Parameter cellX und cellY (Breite/Höhe in ZELLEN) einstellen.
	 */
	public void reshape(int x, int y, int width, int height, String grabMode)
	{
		reshape(x, y, width, height);
	}

	/**
	 * Der Parameter grabMode wird immer als "leftTop" angenommen.
	 */
	public void setCoordinates(int x, int y, String grabMode)
	{
		reshape(x, y, -1, -1);
	}

	public boolean intersectsWith(Point where)
	{
		Point p = new Point(where.x - startX, where.y - startY);
		// Der Original-Point muss unveraendert bleiben!
		return bild.intersectsWith(p);
	}

	public String getInfoTipText(Point where)
	{
		Point p = new Point(where.x - startX, where.y - startY);
		// Der Original-Point muss unveraendert bleiben!
		return bild.getInfoTipText(p);
	}

    /**
     * Entfernt die graphische Repräsentation des EditableMemory.
     */
    public synchronized void remove()
    {
    	parent.remove(bild);
    	parent.remove(label);
    }

    public void setLabel(String[] strArray)
    {
    	if (strArray != null)
    	{
    		if (label != null)
    			parent.remove(label);

			if (strArray[0] != "")
			{
		        label = new Label(strArray[0], Label.LEFT);
    		    label.setFont(parent.DIALOGFONT);
    		    parent.add(label);
	    	    int sw = parent.stringWidth(parent.DIALOGFONT, strArray[0]);
    		    Point coord = getCoordinates("leftTop");
    		    label.reshape(startX, startY - parent.DIALOGFONTHEIGHT - 2, sw, parent.DIALOGFONTHEIGHT);
    	    	label.show();
    	    }
        }
    }

    public String[] getLabel()
    {
    	if (label != null)
    	{
	    	String[] strArray = {label.getText()};
    		return strArray;
    	}
    	else
    	{
    		String[] strArray = {""};
    		return strArray;
    	}
    }

	protected boolean	isValid(int index) {return false;}
	protected boolean	isDirty(int index) {return false;}
	protected void		setValid(int index, boolean b) {}
	protected void		setDirty(int index, boolean b) {}

} /* end EditableMemory */

