package edu.mit.streamjit.impl.compiler2;

import edu.mit.streamjit.impl.blob.Blob.Token;

/**
 * A StorageSlot represents a slot in a storage: whether it's live or not, and
 * if it is, where it should go when we drain.
 * @author Jeffrey Bosboom <jeffreybosboom@gmail.com>
 * @since 11/29/2013
 */
public abstract class StorageSlot {
	public static StorageSlot live(Token token, int index) {
		assert token != null;
		assert index >= 0 : index;
		return new LiveStorageSlot(token, index);
	}
	public static StorageSlot hole() {
		return HoleStorageSlot.INSTANCE;
	}

	public abstract boolean isLive();
	public abstract boolean isHole();
	public abstract boolean isDrainable();
	public abstract StorageSlot duplify();
	public abstract Token token();
	public abstract int index();
	@Override
	public abstract String toString();

	private static final class HoleStorageSlot extends StorageSlot {
		private static final HoleStorageSlot INSTANCE = new HoleStorageSlot();
		private HoleStorageSlot() {}
		@Override
		public boolean isLive() {
			return false;
		}
		@Override
		public boolean isHole() {
			return true;
		}
		@Override
		public boolean isDrainable() {
			return false;
		}
		@Override
		public StorageSlot duplify() {
			return this; //a hole duplicate is just a hole
		}
		@Override
		public Token token() {
			throw new AssertionError("called token() on a hole");
		}
		@Override
		public int index() {
			throw new AssertionError("called index() on a hole");
		}
		@Override
		public String toString() {
			return "(hole)";
		}
	}

	private static class LiveStorageSlot extends StorageSlot {
		private final Token token;
		private final int index;
		/**
		 * If we've duplified, the duplicate slot we created.  Caching this
		 * prevents a duplicate explosion.
		 */
		private StorageSlot duplicate;
		protected LiveStorageSlot(Token token, int index) {
			this.token = token;
			this.index = index;
		}
		@Override
		public boolean isLive() {
			return true;
		}
		@Override
		public boolean isHole() {
			return false;
		}
		@Override
		public boolean isDrainable() {
			return true;
		}
		@Override
		public StorageSlot duplify() {
			if (duplicate == null)
				duplicate = new DuplicateStorageSlot(token(), index());
			return duplicate;
		}
		@Override
		public Token token() {
			return token;
		}
		@Override
		public int index() {
			return index;
		}
		@Override
		public String toString() {
			return String.format("%s[%d]", token(), index());
		}
	}

	private static final class DuplicateStorageSlot extends LiveStorageSlot {
		private DuplicateStorageSlot(Token token, int index) {
			super(token, index);
		}
		@Override
		public boolean isDrainable() {
			return false;
		}
		@Override
		public StorageSlot duplify() {
			return this;
		}
		@Override
		public String toString() {
			return String.format("dup:%s[%d]", token(), index());
		}
	}

	protected StorageSlot() {}
}

@@ -1661,160 +1661,8 @@
 ex;%0A
-%09%09/**%0A%09%09 * If we've duplified, the duplicate slot we created.  Caching this%0A%09%09 * prevents a duplicate explosion.%0A%09%09 */%0A%09%09private StorageSlot duplicate;%0A
 %09%09pr
@@ -1999,110 +1999,44 @@
 %0A%09%09%09
-if (duplicate == null)%0A%09%09%09%09duplicate = new DuplicateStorageSlot(token(), index());%0A%09%09%09return duplicate
+return DuplicateStorageSlot.INSTANCE
 ;%0A%09%09
@@ -2310,20 +2310,16 @@
 extends 
-Live
 StorageS
@@ -2334,16 +2334,29 @@
 private 
+static final 
 Duplicat
@@ -2371,188 +2371,534 @@
 Slot
-(Token token, int index) %7B%0A%09%09%09super(token, index);%0A%09%09%7D%0A%09%09@Override%0A%09%09public boolean isDrainable() %7B%0A%09%09%09return false;%0A%09%09%7D%0A%09%09@Override%0A%09%09public StorageSlot duplify() %7B%0A%09%09%09return this
+ INSTANCE = new DuplicateStorageSlot();%0A%09%09private DuplicateStorageSlot() %7B%7D%0A%09%09@Override%0A%09%09public boolean isLive() %7B%0A%09%09%09return true;%0A%09%09%7D%0A%09%09@Override%0A%09%09public boolean isHole() %7B%0A%09%09%09return false;%0A%09%09%7D%0A%09%09@Override%0A%09%09public boolean isDrainable() %7B%0A%09%09%09return false;%0A%09%09%7D%0A%09%09@Override%0A%09%09public StorageSlot duplify() %7B%0A%09%09%09return this;%0A%09%09%7D%0A%09%09@Override%0A%09%09public Token token() %7B%0A%09%09%09throw new AssertionError(%22called token() on a duplicate%22);%0A%09%09%7D%0A%09%09@Override%0A%09%09public int index() %7B%0A%09%09%09throw new AssertionError(%22called index() on a duplicate%22)
 ;%0A%09%09
@@ -2969,37 +2969,14 @@
 at(%22
+(
 dup
-:%25s%5B%25d%5D%22, token(), index(
 )
+%22
 );%0A%09

make DuplicateStorageSlot a singleton