package org.genericsystem.defaults.tools;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.stream.Stream;

import org.genericsystem.api.core.FiltersBuilder;
import org.genericsystem.api.core.IndexFilter;
import org.genericsystem.defaults.DefaultGeneric;

/**
 * @author Nicolas Feybesse
 *
 * @param <T>
 */
public class InheritanceComputer<T extends DefaultGeneric<T>> extends HashSet<T> {

	private static final long serialVersionUID = 1877502935577170921L;

	private final Map<T, Collection<T>> inheritingsCache = new HashMap<>();

	private final T base;
	private final T origin;
	private final int level;

	public InheritanceComputer(T base, T origin, int level) {
		this.base = base;
		this.origin = origin;
		this.level = level;
	}

	public Stream<T> inheritanceStream() {
		return getInheringsStream(base).filter(holder -> !contains(holder) && !holder.equals(origin) && holder.getLevel() == level);
	}

	private Stream<T> getInheringsStream(T superVertex) {
		// Collection<T> result = inheritingsCache.get(superVertex);
		// if (result == null)
		// inheritingsCache.put(superVertex, result = buildInheritings(superVertex).inheritanceStream().collect(Collectors.toList()));
		// return result.stream();
		return new Inheritings(superVertex).inheritanceStream();
	}

	protected Inheritings buildInheritings(T superVertex) {
		return new Inheritings(superVertex);
	}

	protected class Inheritings {

		protected final T localBase;

		protected Inheritings(T localBase) {
			this.localBase = localBase;
		}

		private Stream<T> inheritanceStream() {
			return fromAboveStream().flatMap(holder -> getStream(holder)).distinct();
		}

		private boolean hasIntermediateSuperOrIsMeta() {
			return localBase.isMeta() || localBase.getSupers().stream().filter(next -> localBase.getMeta().equals(next.getMeta())).count() != 0;
		}

		private Stream<T> metaAndSupersStream() {
			return Stream.concat(hasIntermediateSuperOrIsMeta() ? Stream.empty() : Stream.of(localBase.getMeta()), localBase.getSupers().stream()).distinct();
		}

		private Stream<T> fromAboveStream() {
			return localBase.isRoot() ? Stream.of(origin) : metaAndSupersStream().flatMap(InheritanceComputer.this::getInheringsStream).distinct();
		}

		private Stream<T> getStream(final T holder) {
			if (compositesBySuper(holder).count() != 0)
				add(holder);
			Stream<T> indexStream = Stream.concat(holder.getLevel() < level ? compositesByMeta(holder) : Stream.empty(), compositesBySuper(holder));
			return Stream.concat(Stream.of(holder), indexStream.flatMap(x -> getStream(x)).distinct());
		}

		protected Stream<T> compositesByMeta(T holder) {
			return localBase.getDependencies().filter(Arrays.asList(new IndexFilter(FiltersBuilder.COMPOSITES, localBase), new IndexFilter(FiltersBuilder.HAS_META, holder))).stream();
		}

		protected Stream<T> compositesBySuper(T holder) {
			return localBase.getDependencies().filter(Arrays.asList(new IndexFilter(FiltersBuilder.COMPOSITES, localBase), new IndexFilter(FiltersBuilder.HAS_SUPER, holder))).stream();
		}

	}

}

@@ -160,24 +160,82 @@
 a.util.Map;%0A
+import java.util.Set;%0Aimport java.util.stream.Collectors;%0A
 import java.
@@ -521,27 +521,8 @@
 %3CT%3E%3E
- extends HashSet%3CT%3E
  %7B%0A%0A
@@ -662,16 +662,68 @@
 Map%3C%3E();
+%0A%09private final Set%3CT%3E overridden = new HashSet%3C%3E();
 %0A%0A%09priva
@@ -1012,16 +1012,27 @@
 der -%3E !
+overridden.
 contains
@@ -1159,19 +1159,16 @@
 ex) %7B%0A%09%09
-// 
 Collecti
@@ -1219,19 +1219,16 @@
 tex);%0A%09%09
-// 
 if (resu
@@ -1241,19 +1241,17 @@
 null)%0A%09%09
-// 
+%09
 inheriti
@@ -1372,11 +1372,8 @@
 ;%0A%09%09
-// 
 retu
@@ -1392,16 +1392,20 @@
 ream();%0A
+%09%09//
 %09%09return
@@ -2496,16 +2496,27 @@
  0)%0A%09%09%09%09
+overridden.
 add(hold

make InheritanceComputer not extend HashSet