/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira.drafts
 */
package net.ranides.assira.index;

import java.util.Collection;
import java.util.Comparator;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * IQueryMap is a container which indexes content and allows for quick searching through IQuery interface.
 * It offers adding data, adding indexes and searching.
 *
 * Already inserted records can be modified and correctly indexed using {@link IQuery} interface.
 * More specificaly, you should call:
 * {@code map.find() ... update(record -> change state...) }
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @param <V> type of value
 */
public interface IQueryMap<V> {

    /**
     * Adds new index into storage.
     * It reindexes all already inserted content.
     *
     * @param name name
     * @param index index
     * @return this
     */
    IQueryMap<V> index(String name, Comparator<V> index);

    /**
     * Adds new index into storage.
     * It reindexes all already inserted content.
     *
     * @param name name
     * @param index index
     * @return this
     * @param <T> index type
     */
    <T> IQueryMap<V> index(String name, Function<V,T> index);

    /**
     * Returns IQuery interface which allows to search for specific record using defined indexes.
     *
     * @return query
     */
    IQuery<V> find();

    /**
     * Inserts new record into container. Updates all indexes.
     *
     * @param value value
     */
    void put(V value);

    /**
     * Inserts new records into container.  Updates all indexes.
     *
     * @param values values
     */
    void putAll(Collection<? extends V> values);

    /**
     * Finds specified record inside container, changes its state by applying consumer and then updates indexes.
     *
     * Please note, that is impossible to change outside "consumer".
     * If you change some field used by some index it is impossible to find those record in effient way altough
     * identity relation still holds. In such case we would have to search through all the data inside cointainer.
     *
     * If you really need to modify record externally, you should remove it first and then insert again.
     *
     * Optionally, if you did a lot of modification inside many records, you can trigger full reindexing using
     * (@link {@link #updateAll()}.
     *
     * @param value value
     * @param consumer consumer
     */
    void update(V value, Consumer<? super V> consumer);

    /**
     * Triggers rebuilding of all indexes.
     * It is extremally costly operation. Should be called when you changed a lot of data.
     */
    void updateAll();

    /**
     * Triggers rebuilding only specified index.
     * It is very costly operation, but less than rebuilding all indexes using {@link #updateAll()}.
     * If you modified a lot of records, but you know that changes will affect only some indexes,
     * you can update only them.
     *
     * @param index index
     */
    void updateAll(String index);

    /**
     * Removes specified record from container, updates all indexes.
     * @param value value
     */
    void remove(V value);

    /**
     * Removed all specified records from container and updates indexes.
     * @param values values
     */
    void removeAll(Collection<V> values);

    /**
     * Removes all data and clears all indexes inside container.
     */
    void clear();

}
