package org.codefilarete.stalactite.engine.runtime;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;

import org.codefilarete.stalactite.engine.cascade.AfterInsertCollectionCascader;
import org.codefilarete.stalactite.mapping.EntityMapping;
import org.codefilarete.tool.trace.MutableInt;

import static org.codefilarete.stalactite.engine.runtime.onetomany.AbstractOneToManyWithAssociationTableEngine.INDEXED_COLLECTION_FIRST_INDEX_VALUE;

/**
 * @author Guillaume Mary
 */
public class IndexedAssociationRecordInsertionCascader<SRC, TRGT, SRCID, TRGTID, C extends Collection<TRGT>>
		extends AfterInsertCollectionCascader<SRC, IndexedAssociationRecord> {
	
	private final Function<SRC, C> collectionGetter;
	private final EntityMapping<SRC, SRCID, ?> mappingStrategy;
	private final EntityMapping<TRGT, TRGTID, ?> targetStrategy;
	
	public IndexedAssociationRecordInsertionCascader(AssociationRecordPersister<IndexedAssociationRecord, ?> persister,
													 Function<SRC, C> collectionGetter,
													 EntityMapping<SRC, SRCID, ?> mappingStrategy,
													 EntityMapping<TRGT, TRGTID, ?> targetStrategy) {
		super(persister);
		this.collectionGetter = collectionGetter;
		this.mappingStrategy = mappingStrategy;
		this.targetStrategy = targetStrategy;
	}
	
	@Override
	protected void postTargetInsert(Iterable<? extends IndexedAssociationRecord> entities) {
		// Nothing to do. Identified#isPersisted flag should be fixed by target persister
	}
	
	@Override
	protected Collection<IndexedAssociationRecord> getTargets(SRC src) {
		Collection<TRGT> targets = collectionGetter.apply(src);
		// We only insert non-persisted instances (for logic and to prevent duplicate primary key error)
		List<IndexedAssociationRecord> result = new ArrayList<>(targets.size());
		MutableInt index = new MutableInt(INDEXED_COLLECTION_FIRST_INDEX_VALUE - 1);    // -1 because increment() increments before giving the value, though index will start at 1
		targets.forEach(target -> result.add(new IndexedAssociationRecord(mappingStrategy.getId(src), targetStrategy.getId(target), index.increment())));
		return result;
	}
}
