package sim.lib.memory;

import java.awt.*;
import java.net.*;
import java.io.*;

import sim.*;
import sim.engine.*;
import sim.lib.memory.hex.*;

import sim.lib.wires.Junction;

public abstract class MemoryStructure extends WrapperPainted implements EngineModule, HexEditorParent
{
/* ==================================================================
	Creation Part
	================================================================= */
	public Wrapper createWrapper()
	{
		return this.getCopy();
	}
	
	public Wrapper createWrapper(Point gridPosition)
	{
		Wrapper result = this.getCopy();
		result.setGridLocation(gridPosition);
		return result;
	}
	
/* ==================================================================
	GUI part
	================================================================= */
	protected String targetFile = "Untitle.mem";
	protected String addressToDraw = null;
	protected String dataToDraw = null;
	
	public MemoryStructure()
	{
		super();
	}
	
	protected abstract Wrapper getCopy();
	
	public void initializeGridSize()
	{
		this.setGridSize(10, 7);
	}
	
	public void setFile(String name)
	{
		this.targetFile = name;
	}
	
/* ==================================================================
	Maintanance Part
	================================================================= */
	protected Junction notCE = null;
	protected Junction notOE = null;
	protected Junction address = null;
	protected Junction data = null;
	
	protected int busSize = 8;
	protected int addressSpace = 256;
	protected int requiredAddress = 8;
	protected int wordLenght = 2;
	protected int addressLenght = 2;
	
	protected void setBusSize(int size)
	{
		this.busSize = size;
		this.wordLenght = Integer.toHexString((int)Math.pow(2, this.busSize) - 1).length();
	}
	
	protected void setAddressSpace(int size)
	{
		this.addressSpace = size;
		this.requiredAddress = (int)Math.ceil(Math.log(this.addressSpace) / Math.log(2));
		this.addressLenght = Integer.toHexString((int)Math.pow(2, this.requiredAddress) - 1).length();
	}
	
/* ==================================================================
	Simulation part
	================================================================= */
	// read cycle times
	protected double aa = 12;	// address access
	protected double acs = 12;	// chip select access
	protected double clz = 1;	// chip select to output
	protected double oe = 8;	// output enable to output valid
	protected double olz = 1;	// output enable to output
	protected double chz = 4;	// chip deselection to output
	protected double ohz = 4;	// chip disable to output
	protected double oh = 1;	// output hold from address change
	
	
	// simulation variables
	protected double notCE_goesLow;
	protected double notOE_goesLow;
	
	protected double notCE_goesHigh;
	protected double notOE_goesHigh;
	
	protected boolean pastNotCE;
	protected boolean pastNotOE;
	
	protected double startPutting;
	protected double startFloating;
	protected boolean isReading;
	
	protected double addressChange;
	protected boolean addressHandled;
	protected int oldAddress;
	
	protected char memoryContent[][] = null;
	
	public void reset()
	{
		if(this.editor != null)
			this.editor.prepareForEditing();
		
		this.addressToDraw = null;
		this.dataToDraw = null;
		CentralPanel.ACTIVE_GRID.paintComponent(this);
	}
	
	public Wrapper getParentWrapper()
	{
		return this;
	}
	
	protected void initializeMemory()
	{
		int loop, index;
		
		this.memoryContent = new char[this.addressSpace][this.wordLenght];
		
		for(loop = 0; loop < this.addressSpace; loop++)
			for(index = 0; index < this.wordLenght; index++)
				this.memoryContent[loop][index] = '-';
		
		this.reloadMemory();
		
		System.gc();
	}
	
	protected void undefineOutputs(double when, EnginePeer ep)
	{
		for(int loop = 0; loop < this.busSize; loop++)
			ep.clearUncomplitedTransaction(loop, new Signal(false, when, true, ep, loop));
		
		if(this.editor != null)
			this.editor.eliminateHighlights();
	}
	
	protected void floatOutputs(double when, EnginePeer ep)
	{
		for(int loop = 0; loop < this.busSize; loop++)
			ep.clearUncomplitedTransaction(loop, new Signal(when, ep, loop));
		
		if(this.editor != null)
			this.editor.eliminateHighlights();
	}
	
	protected void setSignalsAt(int address, double when, EnginePeer ep)
	{
		int loop;
		
		try
		{
			if(address != -1)
			{
				if(address >= this.addressSpace)
					address = this.addressSpace - 1;
				
				if(this.editor != null)
					this.editor.readFrom(address);
				
				String value = (new Character(this.memoryContent[address][this.wordLenght - 1])).toString();
				
				for(loop = 1; loop < this.wordLenght; loop++)
					value = value + this.memoryContent[address][this.wordLenght - 1 - loop];
				
				this.putData(Integer.valueOf(value, 16).intValue(), when, ep);
			}
		}
		catch(NumberFormatException nfx)
		{
			this.undefineOutputs(when, ep);
		}
	}
	
	protected void putData(int data, double when, EnginePeer ep)
	{
		int loop;
		
		if(data >= 0)
		{
			String value = Integer.toBinaryString(data);
			
			for(loop = value.length(); loop < this.busSize; loop++)
				value = "0" + value;
			
			for(loop = 0; loop < this.busSize; loop++)
				ep.clearUncomplitedTransaction(loop, new Signal(value.charAt(this.busSize - 1 - loop) == '1', when, false, ep, loop));
		}
		else
			this.undefineOutputs(when, ep);
	}
	
/* ==================================================================
	Popup Part
	================================================================= */
	protected HexEditor editor;
	
	public boolean hasProperties()
	{
		return false;
	}
	public Component getPropertyWindow()
	{
		return null;
	}
		
	public void respondToChanges(Component property)
	{
	}
	
	public int getNumberOfMenuItems()
	{
		return 1;
	}
	
	public String getMenuItemName(int index)
	{
		return "Show Content...";
	}
	
	public void respondToMenuItem(String itemName)
	{
		if(this.editor == null)
		{
			this.editor = new HexEditor(this, this.memoryContent, this.targetFile, this.busSize, this.addressSpace, this.getFont(), MainWindow.MENU.STOP.isEnabled());
			this.editor.show();
		}
		else
		{
			this.editor.requestFocus();
		}
	}
	
	public void restoreOriginalProperties()
	{
	}
	
	public void respondToClosing()
	{
		this.editor = null;
	}
	
	public void reloadMemory()
	{
		int loop;
		
		Reader inStream;
		
		if(MainWindow.MASTER == null)
		{
			try
			{
				inStream = new BufferedReader(new FileReader(this.targetFile));
				
				for(loop = 0; loop < this.addressSpace; loop++)
					inStream.read(this.memoryContent[loop], 0, this.wordLenght);
				
				inStream.close();
			}
			catch(IOException e)
			{
				// do nothing
			}
		}
		else
		{
			// handle internet dowloads
			try
			{
				inStream = new BufferedReader(new InputStreamReader((new URL(MainWindow.MASTER.getCodeBase().toString() + this.targetFile)).openStream()));
				
				for(loop = 0; loop < this.addressSpace; loop++)
					inStream.read(this.memoryContent[loop], 0, this.wordLenght);
				
				inStream.close();
			}
			catch(MalformedURLException mue)
			{
				// do nothing
			}
			catch(IOException e)
			{
				// do nothing
			}
		}
	}
	
/* ==================================================================
	Dipose Part
	================================================================= */
	public void removeNotify()
	{
		if(this.editor != null)
		{
			this.editor.setVisible(false);
			this.editor.dispose();
		}
		
		super.removeNotify();
		
		System.gc();
	}
}