package org.codefilarete.stalactite.dsl.relation;

import java.util.Collection;
import java.util.function.Supplier;

import org.codefilarete.reflection.SerializablePropertyAccessor;
import org.codefilarete.reflection.SerializablePropertyMutator;
import org.codefilarete.stalactite.dsl.property.CascadeOptions;

/**
 * @author Guillaume Mary
 */
public interface ManyToOneOptions<C, O, S extends Collection<C>> extends CascadeOptions {
	
	/** Marks the relation as mandatory. Hence joins will be inner ones and a checking for non null value will be done before insert and update */
	ManyToOneOptions<C, O, S> mandatory();

	/**
	 * Defines combiner of current entity with target entity. This is a more fine-grained way to define how to combine current entity with target
	 * entity than {@link #reverseCollection(SerializablePropertyAccessor)} : sometimes a method already exists in entities to fill the relation instead of
	 * calling getter + Collection.add. This method is here to benefit from it.
	 * This method has no consequence on database mapping since it only interacts in memory.
	 *
	 * @param reverseLink opposite owner of the relation
	 * @return the global mapping configurer
	 */
	ManyToOneOptions<C, O, S> reverselySetBy(SerializablePropertyMutator<O, C> reverseLink);
	
	/**
	 * Defines reverse collection accessor.
	 * Used to fill an (in-memory) bi-directionality.
	 * This method has no consequence on database mapping since it only interacts in memory.
	 *
	 * @param collectionAccessor opposite owner of the relation
	 * @return the global mapping configurer
	 */
	ManyToOneOptions<C, O, S> reverseCollection(SerializablePropertyAccessor<O, S> collectionAccessor);
	
	/**
	 * Defines reverse collection mutator.
	 * Used to fill an (in-memory) bi-directionality.
	 * This method has no consequence on database mapping since it only interacts in memory.
	 *
	 * @param collectionMutator opposite setter of the relation
	 * @return the global mapping configurer
	 */
	ManyToOneOptions<C, O, S> reverseCollection(SerializablePropertyMutator<O, S> collectionMutator);
	
	/**
	 * Defines the factory of reverse collection. If not defined and collection is found null, the collection is set with a default value.
	 * This method has no consequence on database mapping since it only interacts in memory.
	 *
	 * @param collectionFactory opposite collection factory
	 * @return the global mapping configurer
	 */
	ManyToOneOptions<C, O, S> reverselyInitializeWith(Supplier<S> collectionFactory);
	
	/**
	 * Asks to load relation in some separate query (actually may use several queries according to association table presence or polymorphism)
	 *
	 * @return the global mapping configurer
	 */
	ManyToOneOptions<C, O, S> fetchSeparately();
	
	/**
	 * Give the column name of the foreign key column referencing the target entity key.
	 * Valuable only for single key cases.
	 *
	 * @param columnName foreign key column name
	 * @return the global mapping configurer
	 */
	ManyToOneOptions<C, O, S> columnName(String columnName);
}
