/*
 *  @author Ranides Atterwim <ranides@gmail.com>
 *  @copyright Ranides Atterwim
 *  @license WTFPL
 *  @url http://ranides.net/projects/assira
 */

package net.ranides.assira.collection.list;

import java.util.List;
import net.ranides.assira.generic.BiFunction;

/**
 * Klasa abstrakcyjna umożliwiająca leniwą konwersję listy elementów między dwoma
 * formatami w obie strony (a więc tworzenie list do odczytu i zapisu).
 * <p>
 * Ogólny wariant adaptera zakłada, że konwersja może zwracać jako wynik obiekty
 * innej klasy, niż źródłowa. Specjalizacja {@link ListBiAdapterTT} służy do przeprowadzania
 * konwersji, które mają wpływ jedynie na wartości, ale nie na klasy elementów.
 * </p><p>
 * Rozwiązuje problem synchronizacji między dwoma listami, które reprezentują ten 
 * sam zbiór, ale w innym formacie: modyfikacja jednej natychmiast aktualizuje 
 * drugą - nie musimy pamiętać o kontroli spójności oraz wstawianiu do obu list
 * wartości w odpowiednich formatach. Scenariusze w których ListAdapter dobrze 
 * się sprawdza:
 * </p>
 * <pre>
 *      <span style="color:#808080">// spójna obsługa dwóch list</span>
 *      List&lt;INTERN&gt; source = ...
 *      List&lt;PUBLIC&gt; target = konwersja
 *      ...
 *      operacje get(PUBLIC &amp; INTERN) + set(PUBLIC &amp; INTERN)
 *
 *      <span style="color:#808080">// mieszana obsługa dwóch list</span>
 *      List&lt;INTERN&gt; source = ...
 *      List&lt;PUBLIC&gt; target = konwersja
 *      ...
 *      bardzo dużo operacji na get(PUBLIC) + set(PUBLIC &amp; INTERN)
 *
 *      <span style="color:#808080">// obsługa dwóch list niszcząca spójność danych (A)</span>
 *      List&lt;INTERN&gt; source = ...
 *      List&lt;PUBLIC&gt; target = konwersja
 *      ...
 *      bardzo dużo operacji na get(PUBLIC &amp; INTERN) + set(PUBLIC)
 *
 *      <span style="color:#808080">// obsługa dwóch list niszcząca aktualność danych (B)</span>
 *      List&lt;INTERN&gt; source = ...
 *      List&lt;PUBLIC&gt; target = konwersja
 *      ...
 *      bardzo dużo operacji na get(PUBLIC &amp; INTERN) + set(INTERN)
 *
 *      <span style="color:#808080">// obsługa dwóch list niszcząca aktualność danych (C)</span>
 *      List&lt;INTERN&gt; source = ...
 *      List&lt;PUBLIC&gt; target = konwersja
 *      ...
 *      bardzo dużo operacji na get(PUBLIC &amp; INTERN) + set(INTERN) + set(PUBLIC)
 * </pre>
 *
 * <p>
 * <b>Wada:</b> konwersja odbywa się przy <u>każdym</u> odczycie i zapisie, jeśli lista
 * adaptująca jest bardzo intensywnie używana klasyczne rozwiązanie będzie znacznie
 * wydajniejsze (jakkolwiek lista źródłowa nie będzie na bieżąco odświeżana):
 * </p>
 * <pre>
 *      List&lt;INTERN&gt; source = ...
 *      List&lt;PUBLIC&gt; target = konwersja
 *      ...
 *      bardzo dużo operacji get(PUBLIC) + set(PUBLIC)
 *      ...
 *      source = konwersja powrotna
 * </pre>
 *
 * <p>"bardzo intensywne użycie" to (szacunkowo) ponad 1.5N odczytów <u>oraz</u> zapisów, 
 * gdzie N to rozmiar całej listy. Przykładowo 2-3 krotne przeiterowanie po wszystkich elementach
 * oraz zwiększenie ich wartości będzie instensywnym użyciem.
 * </p><p>
 * W metodach {@link #apply(java.lang.Object) apply} oraz {@link #inverse(java.lang.Object) inverse} 
 * można nie tylko konwertować wartości, ale również logować operacje odczytu i zapisu.
 * Z tego powodu adapter może być użyty z powodzeniem jako wydajna realizacja
 * obserwatora.
 * </p>
 *
 * @author ranides
 * @param <T> format publiczny, udostępniany konsumentowi
 * @param <S> wewnętrzny format danych
 */

public abstract class ListBiAdapter<T,S> extends ListAdapter<T,S> implements BiFunction<T,S> {

    /**
     * Tworzy nowy adapter odwzorowujący podaną listę.
     * @param content
     */
    public ListBiAdapter(List<S> content) {
        super(content);
    }

    /**
     * Tworzy nowy adapter odwzorowujący podaną tablicę.
     * @param content
     */
    public ListBiAdapter(S[] content) {
        super(content);
    }

    @Override
    public T set(int index, T element) {
        return apply( content.set(index, inverse(element) ) );
    }

    @Override
    public void add(int index, T element) {
        content.add(index, inverse(element) );
    }

    @Override
    public T remove(int index) {
        return apply( content.remove(index) );
    }
    
}