package org.spoofax.interpreter.library.index;

import org.spoofax.interpreter.terms.IStrategoAppl;
import org.spoofax.interpreter.terms.IStrategoList;
import org.spoofax.interpreter.terms.IStrategoTerm;
import org.spoofax.interpreter.terms.ITermFactory;
import org.spoofax.jsglr.client.imploder.ImploderAttachment;
import org.spoofax.terms.attachments.TermAttachmentStripper;

public class IndexEntryFactory {
	private final ITermFactory factory;
	private final TermAttachmentStripper stripper;

	public IndexEntryFactory(ITermFactory termFactory) {
		this.factory = termFactory;
		this.stripper = new TermAttachmentStripper(termFactory);
	}

	public ITermFactory getTermFactory() {
		return factory;
	}

	public IndexEntry create(IStrategoTerm key, IStrategoTerm value, IStrategoTerm source) {
		ImploderAttachment origin = getImploderAttachment(value);

		// TODO: what is the performance of attachment stripping operations?
		key = stripper.strip(key);
		value = stripper.strip(value);
		if(origin != null)
			value.putAttachment(origin);

		final IndexEntry entry = new IndexEntry(key, value, source, origin);

		return entry;
	}

	public IndexEntry create(IStrategoTerm key, IStrategoTerm source) {
		ImploderAttachment origin = getImploderAttachment(key);

		// TODO: what is the performance of attachment stripping operations?
		key = stripper.strip(key);
		if(origin != null)
			key.putAttachment(origin);

		final IndexEntry entry = new IndexEntry(key, source, origin);

		return entry;
	}

	private ImploderAttachment getImploderAttachment(IStrategoTerm term) {
		final IStrategoTerm termWithImploder = ImploderAttachment.getImploderOrigin(term);
		if(termWithImploder == null)
			return null;
		return ImploderAttachment.get(termWithImploder);
	}

	public IStrategoTerm toPair(IndexEntry entry) {
		return factory.makeTuple(entry.key, entry.value);
	}

	public IStrategoTerm toTerm(IndexEntry entry) {
		if(entry.origin != null)
			return factory.makeTuple(entry.key, entry.value, entry.source,
				ImploderAttachment.TYPE.toTerm(factory, entry.origin));
		else
			return factory.makeTuple(entry.key, entry.value, entry.source);
	}

	public IStrategoList toKeyTerms(Iterable<IndexEntry> entries) {
		IStrategoList list = factory.makeList();
		for(IndexEntry entry : entries) {
			list = factory.makeListCons(entry.key, list);
		}
		return list;
	}

	public IStrategoList toValueTerms(Iterable<IndexEntry> entries) {
		IStrategoList list = factory.makeList();
		for(IndexEntry entry : entries) {
			list = factory.makeListCons(entry.value, list);
		}
		return list;
	}

	public IStrategoList toPairTerms(Iterable<IndexEntry> entries) {
		IStrategoList list = factory.makeList();
		for(IndexEntry entry : entries) {
			list = factory.makeListCons(toPair(entry), list);
		}
		return list;
	}

	public IndexEntry fromTerm(IStrategoTerm term) {
		final IStrategoTerm key = term.getSubterm(0);
		final IStrategoTerm value = term.getSubterm(1);
		final IStrategoTerm source = term.getSubterm(2);
		if(term.getSubtermCount() == 3)
			return new IndexEntry(key, value, source, null);
		final ImploderAttachment origin = ImploderAttachment.TYPE.fromTerm((IStrategoAppl) term.getSubterm(3));
		return new IndexEntry(key, value, source, origin);
	}
}

@@ -983,24 +983,25 @@
 value);%0A%09%09if
+ 
 (origin != n
@@ -1008,40 +1008,148 @@
 ull)
+ %7B
 %0A%09%09%09
-value.putAttachment(origin);
+ImploderAttachment.putImploderAttachment(value, false,%0A%09%09%09%09%09origin.getSort(), origin.getLeftToken(),%0A%09%09%09%09%09origin.getRightToken());%0A%09%09%7D
 %0A%0A%09%09
@@ -1470,16 +1470,17 @@
 y);%0A%09%09if
+ 
 (origin 
@@ -1491,38 +1491,146 @@
 ull)
+ %7B
 %0A%09%09%09
-key.putAttachment(origin);
+ImploderAttachment.putImploderAttachment(key, false,%0A%09%09%09%09%09origin.getSort(), origin.getLeftToken(),%0A%09%09%09%09%09origin.getRightToken());%0A%09%09%7D
 %0A%0A%09%09
@@ -1869,24 +1869,25 @@
 (term);%0A%09%09if
+ 
 (termWithImp
@@ -2130,16 +2130,17 @@
 ) %7B%0A%09%09if
+ 
 (entry.o
@@ -2220,16 +2220,17 @@
 source,%0A
+%09
 %09%09%09%09Impl
@@ -2281,16 +2281,16 @@
 igin));%0A
-
 %09%09else%0A%09
@@ -2464,32 +2464,33 @@
 akeList();%0A%09%09for
+ 
 (IndexEntry entr
@@ -2683,32 +2683,33 @@
 akeList();%0A%09%09for
+ 
 (IndexEntry entr
@@ -2911,16 +2911,17 @@
 );%0A%09%09for
+ 
 (IndexEn
@@ -3218,20 +3218,21 @@
 erm(2);%0A
-
 %09%09if
+ 
 (term.ge

Create new imploder attachment to prevent assertion.