package sim.lib.memory;

import java.awt.*;

import sim.*;
import sim.engine.*;

import sim.lib.wires.Junction;

public abstract class RegisterStructure extends WrapperPainted implements EngineModule {
	/* ==================================================================
	 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 value;
	protected int hex;
	protected int[] store;
	protected String name;
	protected int busSize;
	protected int valueLenght;
	public boolean modDisp;

	public RegisterStructure() {
		super();
		this.setBusSize(8);
	}

	protected abstract Wrapper getCopy();

	public void initializeGridSize() {
		this.setGridSize(10, 7);
	}
	
	public int[] getStore(){
		return this.store;
	}
	
	public void setValue(String val){
		this.value = val.toUpperCase(); //set hex/display value
		this.hex = Integer.parseInt(val, 16); //convert hex string value to decimal integer
		//this.hex = Integer.
		//System.out.println("Value: "+ val);
		String bin_int = Integer.toBinaryString(this.hex);
		System.out.println("bin:"+bin_int);	
		int loop;
		for (loop = 0; loop < this.busSize; loop++) { //set binary value
			if (loop < bin_int.length()) {
				this.store[this.busSize - loop - 1] = Integer.valueOf(bin_int.substring(bin_int.length()-loop-1, bin_int.length()-loop)).intValue();
			} else{
				this.store[this.busSize - loop - 1] = 0;
			}
			System.out.println("index:" + (loop) + " value:" + this.store[loop]);
		}
	}

	public void setBusSize(int size) {
		this.busSize = size;
		int requiredSize, loop;

		String max = Integer.toHexString((int) Math.pow(2, this.busSize) - 1).toUpperCase();
		FontMetrics fm = this.getFontMetrics(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * Grid.SIZE / 4));

		requiredSize = fm.stringWidth(max) / Grid.SIZE + 2;

		if (requiredSize > 5) {
			this.setGridSize(requiredSize + 5, 7);
		} else {
			this.setGridSize(10, 7);
		}

		this.valueLenght = max.length();

		this.value = "0";
		for (loop = 0; this.value.length() < this.valueLenght; loop++) {
			this.value = "0" + this.value;
		}

		this.store = new int[this.busSize];
		for (loop = 0; loop < this.busSize; loop++) {
			this.store[loop] = 0;
		}
	}

	public int getBusSize() {
		return this.busSize;
	}
	
	public String getRegName() {
		return this.name;
	}
	/* ==================================================================
	 Maintanance Part
	 ================================================================= */
	protected Junction input = null;
	protected Junction output = null;
	protected Junction clock = null;
	/* ==================================================================
	 Simulation part
	 ================================================================= */
	protected double delay = 1;
	protected boolean pastClock;
	protected double clkGoesHigh;

	public void reset() {
	}

	public Wrapper getParentWrapper() {
		return this;
	}

	public double getDelay() {
		return this.delay;
	}

	public void changeDelay(double newValue) {
		this.delay = newValue;
	}

	/* ==================================================================
	 Storage Part
	 ================================================================= */
	public String getSpecificParameters() {
		int loop;
		String result = Double.toString(this.delay) + Wrapper.SEPARATOR + this.busSize + Wrapper.SEPARATOR + this.name + Wrapper.SEPARATOR;

		for (loop = 0; loop < this.busSize; loop++) {
			result = result + this.store[this.busSize - 1 - loop];
		}

		result = result + Wrapper.SEPARATOR + this.value + Wrapper.SEPARATOR + this.modDisp + Wrapper.SEPARATOR;

		return result;
	}

	public void loadWrapper(String[] specificParameters) throws SimException {
		int loop;

		if (specificParameters.length == this.getNumberOfSpecificParameters()) {
			try {
				this.delay = Double.valueOf(specificParameters[0]).doubleValue();
				this.setBusSize(Integer.valueOf(specificParameters[1]).intValue());
				this.name = specificParameters[2];

				for (loop = 0; loop < this.busSize; loop++) {
					this.store[this.busSize - 1 - loop] = Integer.valueOf(specificParameters[3].substring(loop, loop + 1)).intValue();
				}

				this.value = specificParameters[4];
				this.modDisp = Boolean.valueOf(specificParameters[5]);
			} catch (NumberFormatException e) {
				throw (new SimException("incorrect parameter type"));
			}
		} else {
			throw (new SimException("incorrect number of parameters"));
		}
	}

	public int getNumberOfSpecificParameters() {
		return 6;
	}
	/* ==================================================================
	 Popup Part
	 ================================================================= */
	private int oldBusSize;
	protected String oldValue = null;
	protected int[] oldStore;

	public boolean hasProperties() {
		return true;
	}

	public Component getPropertyWindow() {
		return (new RegisterProperties(this.busSize, this.delay, this.value, this.name));
	}

	public void respondToChanges(Component property) {
		RegisterProperties rp = (RegisterProperties) property;

		this.delay = rp.getDelay();

		if (this.oldValue == null) {
			this.oldBusSize = this.busSize;
			this.oldValue = this.value;
			this.oldStore = this.store;
		}

		CentralPanel.ACTIVE_GRID.eraseComponent(this);
		this.setBusSize(rp.getBusSize());
		this.value = rp.getContent().toUpperCase();
		this.name = rp.getRegisterName();
		CentralPanel.ACTIVE_GRID.paintComponent(this);

		int loop, index, base;
		String binary;

		try {
			for (loop = 0; loop < this.valueLenght; loop++) {
				base = 4 * loop;

				if (this.value.charAt(this.valueLenght - 1 - loop) == '-') {
					for (index = base; (index - base < 4) && (index < this.busSize); index++) {
						this.store[index] = 2;
					}
				} else {
					binary = Integer.toBinaryString(Integer.valueOf(this.value.substring(this.valueLenght - 1 - loop, this.valueLenght - loop), 16).intValue());

					for (index = binary.length(); index < 4; index++) {
						binary = "0" + binary;
					}

					for (index = 3; (index >= 0) && (base + 3 - index < this.busSize); index--) {
						if (binary.charAt(index) == '1') {
							this.store[base + 3 - index] = 1;
						} else {
							this.store[base + 3 - index] = 0;
						}
					}
				}
			}
		} catch (NumberFormatException e) {
		}
	}

	public void restoreOriginalProperties() {
		if (this.oldValue != null) {
			this.setBusSize(this.oldBusSize);
			this.value = this.oldValue;
			this.store = this.oldStore;
			this.oldValue = null;
			this.oldStore = null;
		}
	}
}
