/* Copyright (c) 2010, Carl Burch. License information is located in the
 * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */

package com.cburch.logisim.std.memory;

import java.util.Arrays;

class MemContentsSub {
	private static class ByteContents extends ContentsInterface {
		private byte[] data;

		public ByteContents(int size) {
			data = new byte[size];
		}

		@Override
		void clear() {
			Arrays.fill(data, (byte) 0);
		}

		@Override
		public ByteContents clone() {
			ByteContents ret = (ByteContents) super.clone();
			ret.data = new byte[this.data.length];
			System.arraycopy(this.data, 0, ret.data, 0, this.data.length);
			return ret;
		}

		@Override
		int get(int addr) {
			return addr >= 0 && addr < data.length ? data[addr] : 0;
		}

		//
		// methods for accessing data within memory
		//
		@Override
		int getLength() {
			return data.length;
		}

		@Override
		void load(int start, int[] values, int mask) {
			int n = Math.min(values.length, data.length - start);
			for (int i = 0; i < n; i++) {
				data[start + i] = (byte) (values[i] & mask);
			}
		}

		@Override
		void set(int addr, int value) {
			if (addr >= 0 && addr < data.length) {
				byte oldValue = data[addr];
				if (value != oldValue) {
					data[addr] = (byte) value;
				}
			}
		}
	}

	static abstract class ContentsInterface implements Cloneable {
		abstract void clear();

		@Override
		public ContentsInterface clone() {
			try {
				return (ContentsInterface) super.clone();
			} catch (CloneNotSupportedException e) {
				return this;
			}
		}

		abstract int get(int addr);

		int[] get(int start, int len) {
			int[] ret = new int[len];
			for (int i = 0; i < ret.length; i++)
				ret[i] = get(start + i);
			return ret;
		}

		abstract int getLength();

		boolean isClear() {
			for (int i = 0, n = getLength(); i < n; i++) {
				if (get(i) != 0)
					return false;
			}
			return true;
		}

		abstract void load(int start, int[] values, int mask);

		boolean matches(int[] values, int start, int mask) {
			for (int i = 0; i < values.length; i++) {
				if (get(start + i) != (values[i] & mask))
					return false;
			}
			return true;
		}

		abstract void set(int addr, int value);
	}

	private static class IntContents extends ContentsInterface {
		private int[] data;

		public IntContents(int size) {
			data = new int[size];
		}

		@Override
		void clear() {
			Arrays.fill(data, 0);
		}

		@Override
		public IntContents clone() {
			IntContents ret = (IntContents) super.clone();
			ret.data = new int[this.data.length];
			System.arraycopy(this.data, 0, ret.data, 0, this.data.length);
			return ret;
		}

		@Override
		int get(int addr) {
			return addr >= 0 && addr < data.length ? data[addr] : 0;
		}

		//
		// methods for accessing data within memory
		//
		@Override
		int getLength() {
			return data.length;
		}

		@Override
		void load(int start, int[] values, int mask) {
			int n = Math.min(values.length, data.length - start);
			for (int i = 0; i < n; i++) {
				data[i] = values[i] & mask;
			}
		}

		@Override
		void set(int addr, int value) {
			if (addr >= 0 && addr < data.length) {
				int oldValue = data[addr];
				if (value != oldValue) {
					data[addr] = value;
				}
			}
		}
	}

	private static class ShortContents extends ContentsInterface {
		private short[] data;

		public ShortContents(int size) {
			data = new short[size];
		}

		@Override
		void clear() {
			Arrays.fill(data, (short) 0);
		}

		@Override
		public ShortContents clone() {
			ShortContents ret = (ShortContents) super.clone();
			ret.data = new short[this.data.length];
			System.arraycopy(this.data, 0, ret.data, 0, this.data.length);
			return ret;
		}

		@Override
		int get(int addr) {
			return addr >= 0 && addr < data.length ? data[addr] : 0;
		}

		//
		// methods for accessing data within memory
		//
		@Override
		int getLength() {
			return data.length;
		}

		@Override
		void load(int start, int[] values, int mask) {
			int n = Math.min(values.length, data.length - start);
			for (int i = 0; i < n; i++) {
				data[start + i] = (short) (values[i] & mask);
			}
		}

		@Override
		void set(int addr, int value) {
			if (addr >= 0 && addr < data.length) {
				short oldValue = data[addr];
				if (value != oldValue) {
					data[addr] = (short) value;
				}
			}
		}
	}

	static ContentsInterface createContents(int size, int bits) {
		if (bits <= 8)
			return new ByteContents(size);
		else if (bits <= 16)
			return new ShortContents(size);
		else
			return new IntContents(size);
	}

	private MemContentsSub() {
	}
}
