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

import java.util.List;
import java.util.Map;
import net.ranides.assira.generic.TypeToken;

/**
 * Interfejs reprezentujący zbiór wartości konfiguracyjnych, zorganizowanych w
 * strukturę drzewiastą. Gałąź może zawierać nie tylko konkretne wartości, ale
 * również kolejne, zagnieżdżone gałęzie.
 * @author ranides
 */
public interface Branch {

    /**
     * <p>
     * Zwraca mapę, która przechowuje wartości konfiguracyjne. Format mapy nie
     * jest określony - w szczególności obiekty zagnieżdżone mogą być
     * reprezentowane jako zagnieżdżone mapy, albo być spłaszczone. 1-elementowe
     * listy obiektów mogą być kolekacji albo po prostu elementami bezpośrednio
     * wstawionymi do mapy. Modyfikacja mapy jest niezdefiniowana: może wpływać
     * na konfigurację, ale nie musi.
     * </p><p>
     * Z powyższych powodów metoda zalecana tylko podczas pracy z konkretną
     * implementacją.
     * </p>
     * @return
     */
    Map<String,Object> asMap();

    /**
     * Zwraca spłaszczoną mapę, zawierającą wszystkie ustawienia w postaci:
     * "ścieżka - wartość". Modyfikacja mapy nie wpływa na wartość konfiguracji.
     * Ale jeśli wartości zapisane w konfiguracji nie są obiektami typu
     * <i>immutable</i> to ich modyfikacja jest niezdefiniowana.
     * @return
     */
    Map<String,Object> asFlatMap();

    /**
     * Zwraca gałąź zagnieżdżoną w bieżącym obiekcie.
     * @param path
     * @return
     */
    Branch branch(String path);

    /**
     * Zwraca listę ścieżek, reprezentujących gałęzie zawarte w bieżącym obiekcie.
     * @param path
     * @return
     */
    List<Branch> branchList(String path);

/* ************************************************************************** */

    /**
     * Pobiera wartość zapisaną we wskazanej lokalizacji dokonując w razie potrzeby
     * rzutowania za pomocą {@link net.ranides.assira.text.LexicalCast#cast}. Zwraca wartość domyślną, jeśli
     * wartość nie istnieje.
     * @param <T>
     * @param type
     * @param path
     * @param ddefault
     * @return
     */
<T> T get(TypeToken<T> type, String path, T ddefault);

    /**
     * Pobiera wartość zapisaną we wskazanej lokalizacji dokonując w razie potrzeby
     * rzutowania za pomocą {@link LexicalCast#cast}. Rzuca wyjątkiem, jeśli wartość
     * nie istnieje.
     * @param <T>
     * @param type
     * @param path
     * @return
     * @throws ConfigException
     */
    <T> T get(TypeToken<T> type, String path) throws ConfigException;

    /**
     * Pobiera wartość zapisaną we wskazanej lokalizacji dokonując w razie potrzeby
     * rzutowania za pomocą {@link LexicalCast#cast}. Zwraca wartość domyślną, jeśli
     * wartość nie istnieje.
     * @param <T>
     * @param type
     * @param path
     * @param ddefault
     * @return
     */
    <T> T get(Class<T> type, String path, T ddefault);

    /**
     * Pobiera wartość zapisaną we wskazanej lokalizacji dokonując w razie potrzeby
     * rzutowania za pomocą {@link LexicalCast#cast}. Rzuca wyjątkiem, jeśli wartość
     * nie istnieje.
     * @param <T>
     * @param type
     * @param path
     * @return
     * @throws ConfigException
     */
    <T> T get(Class<T> type, String path) throws ConfigException;

    /**
     * Pobiera wartość zapisaną we wskazanej lokalizacji
     * @param path
     * @param ddefault
     * @return odczytana wartość, lub ddefault, jeśli nie znaleziono w konfiguracji
     * @throws ConfigException
     */
    Object get(String path, Object ddefault) throws ConfigException;

    /**
     * Pobiera wartość zapisaną we wskazanej lokalizacji
     * @param path
     * @return odczytana wartość
     * @throws ConfigException jeśli nie znaleziono w konfiguracji
     */
    Object get(String path) throws ConfigException;

    /**
     * Pobiera listę wartości zapisanych we wskazanej lokalizacji. Może zwrócić
     * pustą listę, jeśli nie znaleziono niczego we wskazanej lokalizacji. W razie
     * potrzeby dokonuje odpowiedniego rzutowania elementów listy za pomocą
     * {@link LexicalCast#cast}.
     * @param <T>
     * @param type
     * @param path
     * @return
     */
    <T> List<T> getList(TypeToken<T> type, String path);

    /**
     * Pobiera listę wartości zapisanych we wskazanej lokalizacji. Może zwrócić
     * pustą listę, jeśli nie znaleziono niczego we wskazanej lokalizacji. W razie
     * potrzeby dokonuje odpowiedniego rzutowania elementów listy za pomocą
     * {@link LexicalCast#cast}.
     * @param <T>
     * @param type
     * @param path
     * @return
     */
    <T> List<T> getList(Class<T> type, String path);

    /**
     * Pobiera listę wartości zapisanych we wskazanej lokalizacji
     * @param path
     * @return pusta lista, jeśli nie znaleziono w konfiguracji
     * @throws ConfigException
     */
    List<?> getList(String path) throws ConfigException;

    /**
     * Zmienia wartość pod wskazaną lokalizacją. W razie potrzeby dokonuje rzutowania
     * {@code value} na typ {@type} za pomocą {@link LexicalCast#cast}.
     * @param <T>
     * @param type
     * @param path
     * @param value
     * @throws ConfigException
     */
    <T> void put(TypeToken<T> type, String path, Object value) throws ConfigException;

    /**
     * Zmienia wartość pod wskazaną lokalizacją. W razie potrzeby dokonuje rzutowania
     * {@code value} na typ {@type} za pomocą {@link LexicalCast#cast}.
     * @param <T>
     * @param type
     * @param path
     * @param value
     * @throws ConfigException
     */
    <T> void put(Class<T> type, String path, Object value) throws ConfigException;

    /**
     * Zmienia wartość pod wskazaną lokalizacją
     * @param path
     * @param value
     * @throws ConfigException
     */
    void put(String path, Object value) throws ConfigException;

    /**
     * Wstawia wiele wartości pod wskazaną lokalizację (usuwając poprzednie).
     * W razie potrzeby dokonuje rzutowania elementów listy na typ {@type} za pomocą
     * {@link LexicalCast#cast}.
     * @param <T>
     * @param type
     * @param path
     * @param value
     * @throws ConfigException
     */
    <T> void putList(TypeToken<T> type, String path, List<?> value) throws ConfigException;

    /**
     * Umieszcza listę wartości pod wskazaną lokalizacją.
     * W razie potrzeby dokonuje rzutowania elementów listy na typ {@type} za pomocą
     * {@link LexicalCast#cast}.
     * @param <T>
     * @param type
     * @param path
     * @param value
     * @throws ConfigException
     */
    <T> void putList(Class<T> type, String path, List<?> value) throws ConfigException;

    /**
     * Umieszcza listę wartości pod wskazaną lokalizacją
     * @param path
     * @param value
     * @throws ConfigException
     */
    void putList(String path, List<?> value) throws ConfigException;
}
