package weka.classifiers.lazy.AM.lattice;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import weka.classifiers.lazy.AM.data.Subcontext;
import weka.classifiers.lazy.AM.data.SubcontextList;
import weka.classifiers.lazy.AM.data.Supracontext;
import weka.classifiers.lazy.AM.label.Label;

public class SparseLattice implements Lattice {
	// List<Concept> lattice = new ArrayList<>();

	public SparseLattice(SubcontextList subList) {
		Concept bottom = new Concept(subList.getLabeler().getMinimum());
		// lattice.add(bottom);
		for (Subcontext sub : subList) {
			// TODO next: this should be addIntent(sub, bottom, null). But then
			// things aren't quite organized correctly. Fix this!
			addIntent(sub.getLabel(), new Concept(sub), null);
		}
	}

	Concept addIntent(Label intent, Concept inputConcept, Lattice lattice) {
		Concept generatorConcept = getMaximalConcept(intent, inputConcept);
		if (generatorConcept.getIntent().equals(intent)) {
			// concept with given label is already present, so just add the sub
			generatorConcept.combineExtent(inputConcept);
			// markIfHetero(generatorConcept);
			return generatorConcept;
		}
		Set<Concept> newParents = new HashSet<>();
		for (Concept candidate : generatorConcept.getParents()) {
			if (!candidate.getIntent().isAncestorOf(intent))
				// this possible parent turned out not to be a parent, so
				// generate a parent by intersecting it with the new label, and
				// save the new parent
				candidate = addIntent(intent.intersect(candidate.getIntent()),
						candidate, lattice);
			boolean addParent = true;
			for (Concept parent : newParents) {
				if (parent.getIntent().isAncestorOf(candidate.getIntent())) {
					addParent = false;
					break;
				} else if (candidate.getIntent().isAncestorOf(
						parent.getIntent()))
					newParents.remove(parent);// assert(newParents.contains(parent))
												// may be instructive here
			}
			if (addParent)
				newParents.add(candidate);
		}
		Concept newConcept = new Concept(intent, generatorConcept.getExtent());
		// Lattice.add(newConcept);
		for (Concept parent : newParents) {
			generatorConcept.removeParent(parent);
			newConcept.addParent(parent);
		}
		generatorConcept.addParent(newConcept);
		return newConcept;
	}

	Concept getMaximalConcept(Label intent, Concept generatorConcept) {
		boolean parentIsMaximal = true;
		while (parentIsMaximal) {
			parentIsMaximal = false;
			for (Concept parent : generatorConcept.getParents()) {
				if (parent.getIntent().isAncestorOf(intent)) {
					generatorConcept = parent;
					parentIsMaximal = true;
					break;
				}
			}
		}
		return generatorConcept;
	}

	@Override
	public Set<Supracontext> getSupracontexts() {
		// TODO how should we get the supracontexts?
		return null;
	}

	private class Concept {
		Set<Subcontext> extent;
		Label intent;
		Set<Concept> parents;

		// TODO: track heterogeneity
		// boolean isHetero;
		// double outcome;

		public Concept(Label intent) {
			this.intent = intent;
			extent = new HashSet<Subcontext>();
			parents = new HashSet<Concept>();
		}

		public Concept(Subcontext sub) {
			intent = sub.getLabel();
			extent = new HashSet<Subcontext>();
			extent.add(sub);
			parents = new HashSet<Concept>();
		}

		public Concept(Label intent, Set<Subcontext> extent) {
			this.intent = intent;
			this.extent = extent;
			parents = new HashSet<Concept>();
		}

		public Set<Subcontext> getExtent() {
			return Collections.unmodifiableSet(extent);
		}

		public void addToExtent(Subcontext sub) {
			assert (!extent.contains(sub));
			extent.add(sub);
			// markIfHetero
		}

		public void combineExtent(Concept other) {
			extent.addAll(other.extent);
			// markIfHetero
		}

		// public int getOutcome(){}

		public Label getIntent() {
			return intent;
		}

		public Set<Concept> getParents() {
			return Collections.unmodifiableSet(parents);
		}

		public void addParent(Concept newParent) {
			parents.add(newParent);
		}

		public void removeParent(Concept oldParent) {
			assert (parents.contains(oldParent));
			parents.remove(oldParent);
		}

		// public boolean isHeterogeneous() {
		// return isHetero;
		// }

		@Override
		public String toString() {
			return intent + "(" + extent + ")->[" + parents + "]";
		}
	}

}

@@ -727,16 +727,21 @@
 dIntent(
+sub, 
 sub.getL
@@ -752,24 +752,14 @@
 (), 
-new Concept(sub)
+bottom
 , nu
@@ -790,16 +790,32 @@
 dIntent(
+Subcontext sub, 
 Label in
@@ -841,17 +841,20 @@
 Concept,
- 
+%0A%09%09%09
 Lattice 
@@ -1517,16 +1517,27 @@
 dIntent(
+sub,%0A%09%09%09%09%09%09
 intent.i
@@ -1568,23 +1568,17 @@
 tent()),
-%0A%09%09%09%09%09%09
+ 
 candidat
@@ -1579,17 +1579,23 @@
 ndidate,
- 
+%0A%09%09%09%09%09%09
 lattice)
@@ -3380,22 +3380,37 @@
 xtent = 
+new HashSet%3C%3E(
 extent
+)
 ;%0A%09%09%09par

fix calling addall on unmodifiable collection