/*
 *  Copyright (c) 2012-2019, Jan Bernitt
 *	
 *  Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0
 */
package se.jbee.inject.container;

import static se.jbee.inject.container.Cast.resourceTypeFor;

import se.jbee.inject.Dependency;
import se.jbee.inject.Generator;
import se.jbee.inject.Hint;
import se.jbee.inject.Injector;
import se.jbee.inject.Instance;
import se.jbee.inject.Resource;
import se.jbee.inject.UnresolvableDependency;

/**
 * Similar to a call-site each {@linkplain InjectionSite} represents the
 * resolution of arguments from a specific site or path that is represented by a
 * {@link Dependency}. This is used in cases where otherwise a dependency would
 * be resolved over and over again for the same {@link Dependency}. For example
 * when injecting a factory {@link java.lang.reflect.Method} or
 * {@link java.lang.reflect.Constructor} with arguments.
 */
public final class InjectionSite {

	public final Dependency<?> site;

	private final Hint<?>[] hints;
	private final Generator<?>[] generators;
	private final Object[] preResolvedArgs;
	private final int[] lazyArgIndexes;
	private final int lazyArgCount;

	public InjectionSite(Injector injector, Dependency<?> site,
			Hint<?>[] hints) {
		this.site = site;
		this.hints = hints;
		this.generators = new Generator<?>[hints.length];
		this.preResolvedArgs = new Object[hints.length];
		this.lazyArgIndexes = new int[hints.length];
		this.lazyArgCount = preResolveArgs(injector);
	}

	public Object[] args(Injector injector) throws UnresolvableDependency {
		if (lazyArgCount == 0)
			return preResolvedArgs;
		// in this case we have to copy to become thread-safe!
		Object[] args = preResolvedArgs.clone();
		for (int j = 0; j < lazyArgCount; j++) {
			int i = lazyArgIndexes[j];
			Hint<?> hint = hints[i];
			args[i] = generators[i] == null
				? injector.resolve(site.instanced(hint.relativeRef))
				: yield(generators[i], site.instanced(hint.relativeRef));
		}
		return args;
	}

	private int preResolveArgs(Injector injector) {
		int lazyArgCount = 0;
		for (int i = 0; i < generators.length; i++) {
			Hint<?> hint = hints[i];
			if (hint.type().rawType == Injector.class) {
				preResolvedArgs[i] = injector;
			} else if (hint.isConstant()) {
				preResolvedArgs[i] = hint.value;
			} else if (hint.type().arrayDimensions() == 1) {
				lazyArgIndexes[lazyArgCount++] = i;
			} else if (hint.absoluteRef != null) {
				preResolvedArgs[i] = injector.resolve(hint.absoluteRef);
			} else { // relative ref
				Instance<?> ref = hint.relativeRef;
				Dependency<? extends Resource<?>> resourceDep = site.typed(
						resourceTypeFor(ref.type)).named(ref.name);
				Resource<?> resource = injector.resolve(resourceDep);
				if (resource.scoping.isStableByDesign()) {
					//TODO and not has type variable involved
					preResolvedArgs[i] = yield(resource,
							site.instanced(hint.relativeRef));
				} else {
					lazyArgIndexes[lazyArgCount++] = i;
					generators[i] = resource;
				}
			}
		}
		return lazyArgCount;
	}

	@SuppressWarnings("unchecked")
	private static <I> I yield(Generator<I> gen, Dependency<?> dep) {
		return gen.generate((Dependency<? super I>) dep);
	}
}
@@ -1926,21 +1926,24 @@
 )%0A%09%09%09%09: 
-yield
+generate
 (generat
@@ -2867,21 +2867,24 @@
 gs%5Bi%5D = 
-yield
+generate
 (resourc
@@ -3113,13 +3113,16 @@
 %3E I 
-yield
+generate
 (Gen
@@ -3213,8 +3213,9 @@
 p);%0A%09%7D%0A%7D
+%0A

Rename yield to generate