package edu.mit.streamjit.impl.compiler2;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Range;
import edu.mit.streamjit.util.CollectionUtils;
import edu.mit.streamjit.util.Combinators;
import edu.mit.streamjit.util.Pair;
import java.lang.invoke.MethodHandle;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Represents one core during the compilation.
 * @author Jeffrey Bosboom <jeffreybosboom@gmail.com>
 * @since 10/17/2013
 */
public class Core {
	private final ImmutableMap<Storage, ConcreteStorage> globalStorage;
	private final StorageFactory localStorageFactory;
	private final ImmutableTable<Actor, Integer, IndexFunctionTransformer> inputTransformers, outputTransformers;
	private final Bytecodifier.Function bytecodifier;
	private final List<Pair<ActorGroup, Range<Integer>>> allocations = new ArrayList<>();
	public Core(Set<Storage> storage, ImmutableMap<Storage, ConcreteStorage> globalStorage,
			StorageFactory localStorageFactory,
			ImmutableTable<Actor, Integer, IndexFunctionTransformer> inputTransformers,
			ImmutableTable<Actor, Integer, IndexFunctionTransformer> outputTransformers,
			Bytecodifier.Function bytecodifier) {
		this.globalStorage = globalStorage;
		this.localStorageFactory = localStorageFactory;
		this.inputTransformers = inputTransformers;
		this.outputTransformers = outputTransformers;
		this.bytecodifier = bytecodifier;
	}

	public void allocate(ActorGroup group, Range<Integer> iterations) {
		allocations.add(Pair.make(group, iterations));
	}

	public MethodHandle code() {
		ImmutableMap.Builder<Storage, ConcreteStorage> localStorage = ImmutableMap.builder();
		for (Pair<ActorGroup, Range<Integer>> p : allocations) {
			ActorGroup g = p.first;
			Range<Integer> iterations = p.second;
			for (Storage s : g.internalEdges()) {
				ImmutableSortedSet<Integer> reads = g.reads(s, iterations);
				assert reads.equals(g.writes(s, iterations));
				//StorageFactory doesn't let us specify a capacity or offset.
				localStorage.put(s, new InternalArrayConcreteStorage(s, reads.last() - reads.first() + 1, reads.first()));
			}
		}

		Map<Storage, ConcreteStorage> allStorage = CollectionUtils.union(globalStorage, localStorage.build());
		//TODO: here's where to plug in ActorGroup ordering parameters
		List<MethodHandle> code = new ArrayList<>(allocations.size());
		for (Pair<ActorGroup, Range<Integer>> p : allocations)
			code.add(p.first.specialize(p.second, allStorage, inputTransformers, outputTransformers, bytecodifier));
		return Combinators.semicolon(code);
	}

	/**
	 * Returns true iff this Core is empty (has no allocations) and thus doesn't
	 * need to generate or run code.
	 * @return true iff this core is empty
	 */
	public boolean isEmpty() {
		return allocations.isEmpty();
	}

	@Override
	public String toString() {
		return allocations.toString();
	}
}

@@ -1708,16 +1708,272 @@
 e() %7B%0A%09%09
+//TODO: ActorGroup ordering parameters: accumulate a%0A%09%09//List%3CPair%3CActorGroup, MethodHandle%3E%3E, then sort before semicolon(code).%0A%09%09List%3CMethodHandle%3E code = new ArrayList%3C%3E(allocations.size());%0A%09%09for (Pair%3CActorGroup, Range%3CInteger%3E%3E p : allocations) %7B%0A%09%09%09
 Immutabl
@@ -2053,67 +2053,8 @@
 r();
-%0A%09%09for (Pair%3CActorGroup, Range%3CInteger%3E%3E p : allocations) %7B
 %0A%09%09%09
@@ -2363,16 +2363,70 @@
 .put(s, 
+reads.isEmpty() ? new EmptyConcreteStorage(s) :%0A%09%09%09%09%09%09
 new Inte
@@ -2510,20 +2510,16 @@
 ;%0A%09%09%09%7D%0A%09
-%09%7D%0A%0A
 %09%09Map%3CSt
@@ -2619,195 +2619,8 @@
 ));%0A
-%09%09//TODO: here's where to plug in ActorGroup ordering parameters%0A%09%09List%3CMethodHandle%3E code = new ArrayList%3C%3E(allocations.size());%0A%09%09for (Pair%3CActorGroup, Range%3CInteger%3E%3E p : allocations)%0A
 %09%09%09c
@@ -2723,16 +2723,20 @@
 fier));%0A
+%09%09%7D%0A
 %09%09return

handle empty/duplicate internal storage