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

import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import net.ranides.assira.collection.maps.MultiMap;
import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.xml.impl.CElements;

import org.w3c.dom.Node;

/**
 * This is just aggregating collection for multiple XMLElement items.
 *
 * Allows you to inspect result in fluent way or execute massive modification on all elements inside collection.
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
@SuppressWarnings({
    "PMD.TooManyDifferentMethods",
    "PMD.TooManyPublicMethods"
})
public interface XMLElements extends Iterable<XMLElement> {

    /**
     * Factory: creates new collection
     *
     * @param elements elements
     * @return XMLElements
     */
    static XMLElements of(XMLElement... elements) {
        return CElements.of(elements);
    }

    /**
     * Factory: creates new collection
     *
     * @param elements elements
     * @return XMLElements
     */
    static XMLElements of(XMLElements... elements) {
        return CElements.of(elements);
    }

    /**
     * Interoperability method: returns list of "org.w3c.dom.Node"
     *
     * @return List
     */
    List<Node> nodes();

    /**
     * Returns list with name of each element.
     * Lets see {@link XMLElement#name()}.
     *
     * @return List
     */
    List<String> names();

    /**
     * Returns list with xpath of each element.
     * Lets see {@link XMLElement#xpath()}.
     *
     * @return List
     */
    List<String> xpaths();

    /**
     * Returns concatenation of content of each element.
     * Lets see {@link XMLElement#content()}.
     *
     * @return String
     */
    String content();

    /**
     * Returns list with content of each element.
     * Lets see {@link XMLElement#content()}.
     *
     * @return List
     */
    List<String> contents();

    /**
     * Returns concatenation of text of each element.
     * Lets see {@link XMLElement#text()}.
     *
     * @return String
     */
    String text();

    /**
     * Returns concatenation of text of each element.
     * Transforms each text using provided mapping function.
     *
     * Lets see {@link XMLElement#text()}.
     *
     * @param function function
     * @return String
     */
    String text(Function<String, String> function);

    /**
     * Returns list with text of each element.
     * Lets see {@link XMLElement#text()}.
     *
     * @return List
     */
    List<String> texts();

    /**
     * Returns list with text of each element.
     * Transforms each text using provided mapping function.
     *
     * Lets see {@link XMLElement#text()}.
     *
     * @param function function
     * @return List
     */
    List<String> texts(Function<String, String> function);

    /**
     * Aggregation: executes {@link XMLElement#attrs()} on each element and aggregates results.
     *
     * @return XMLElements
     */
    XMLElements attrs();

    /**
     * Transform: maps each element using {@link XMLElement#attr(String)}
     *
     * @param name name
     * @return XMLElements
     */
    XMLElements attrs(String name);

    /**
     * Returns size of this collection
     *
     * @return int
     */
    int size();

    /**
     * Converts this collection to CQuery
     *
     * @return CQuery
     */
    CQuery<XMLElement> stream();

    /**
     * Converts this collection to CQuery, using provided mapping function for each element
     *
     * @param function function
     * @param <T> T
     * @return CQuery
     */
    <T> CQuery<T> stream(Function<? super XMLElement, T> function);

    /**
     * Iterates through all elements in collection
     *
     * @param consumer consumer
     * @return this
     */
    XMLElements each(Consumer<? super XMLElement> consumer);

    /**
     * Returns first element in collection
     *
     * Throws NoSuchElementException if there is no result
     *
     * @return XMLElement
     */
    XMLElement first();

    /**
     * Returns first element in collection accepted by selector
     *
     * Throws NoSuchElementException if there is no result
     *
     * @param selector selector
     * @return XMLElement
     */
    XMLElement first(String selector);

    /**
     * Returns first element in collection accepted by selector
     *
     * Throws NoSuchElementException if there is no result
     *
     * @param selector selector
     * @return XMLElement
     */
    XMLElement first(Predicate<XMLContext> selector);

    /**
     * Returns last element in collection
     *
     * Throws NoSuchElementException if there is no result
     *
     * @return XMLElement
     */
    XMLElement last();

    /**
     * Returns last element in collection accepted by selector
     *
     * Throws NoSuchElementException if there is no result
     *
     * @param selector selector
     * @return XMLElement
     */
    XMLElement last(String selector);

    /**
     * Returns last element in collection accepted by selector
     *
     * Throws NoSuchElementException if there is no result
     *
     * @param selector selector
     * @return XMLElement
     */
    XMLElement last(Predicate<XMLContext> selector);

    /**
     * Creates new collection with elements from this collection and provided one.
     *
     * @param elements elements
     * @return XMLElements
     */
    XMLElements concat(XMLElements elements);


    /**
     * Returns new collection filtered by selector
     *
     * @param selector selector
     * @return XMLElements
     */
    XMLElements filter(String selector);

    /**
     * Returns new collection filtered by selector
     *
     * @param selector selector
     * @return XMLElements
     */
    XMLElements filter(Predicate<XMLContext> selector);

    /**
     * Returns new collection without elements accepted by selector
     *
     * @param selector selector
     * @return XMLElements
     */
    XMLElements discard(String selector);

    /**
     * Returns new collection without elements accepted by selector
     *
     * @param selector selector
     * @return XMLElements
     */
    XMLElements discard(Predicate<XMLContext> selector);

    /**
     * Returns new collection which contains only first elements matching selector.
     * Stops iteration after first not accepted element.
     *
     * @param selector selector
     * @return XMLElements
     */
    XMLElements limit(String selector);

    /**
     * Returns new collection which contains only first elements matching selector.
     * Stops iteration after first not accepted element.
     *
     * @param selector selector
     * @return XMLElements
     */
    XMLElements limit(Predicate<XMLContext> selector);


    /**
     * Returns true, if this collection is not empty
     * @return boolean
     */
    boolean matches();

    /**
     * Creates new MultiMap with all elements, using tag name as key.
     *
     * @return MultiMap
     */
    MultiMap<String, String> map();

    /**
     * Aggregation: executes selector on each element and aggregates results.
     *
     * @param function function
     * @return XMLElements
     */
    XMLElements map(XMLSelector function);

    /**
     * Aggregation: executes fuction on each element and aggregates results.
     *
     * @param function function
     * @return XMLElements
     */
    XMLElements map(Function<? super XMLElement, XMLElement> function);

    /**
     * Aggregation: executes {@link XMLElement#find(String)} on each element and aggregates results.
     *
     * @param selector selector
     * @return XMLElements
     */
    XMLElements find(String selector);

    /**
     * Aggregation: executes {@link XMLElement#find(XMLSelector)} on each element and aggregates results.
     *
     * @param selector selector
     * @return XMLElements
     */
    XMLElements find(XMLSelector selector);

    /**
     * Aggregation: executes {@link XMLElement#children()} on each element and aggregates results.
     *
     * @return XMLElements
     */
    XMLElements children();

    /**
     * Aggregation: executes {@link XMLElement#children(String)} on each element and aggregates results.
     *
     * @param selector selector
     * @return XMLElements
     */
    XMLElements children(String selector);

    /**
     * Aggregation: executes {@link XMLElement#children(Predicate)} on each element and aggregates results.
     *
     * @param selector selector
     * @return XMLElements
     */
    XMLElements children(Predicate<XMLContext> selector);

    /**
     * Match: executes {@link XMLElement#has()} on each element and returns true if any returned true
     *
     * @return boolean
     */
    boolean has();

    /**
     * Match: executes {@link XMLElement#has(String)} on each element and returns true if any returned true
     *
     * @param selector selector
     * @return boolean
     */
    boolean has(String selector);

    /**
     * Match: executes {@link XMLElement#has(Predicate)} on each element and returns true if any returned true
     *
     * @param selector selector
     * @return boolean
     */
    boolean has(Predicate<XMLContext> selector);

    /**
     * Transform: maps each element using {@link XMLElement#next()}
     *
     * @return XMLElements
     */
    XMLElements next();

    /**
     * Aggregation: executes {@link XMLElement#next(String)} on each element and aggregates results.
     *
     * @param selector selector
     * @return XMLElements
     */
    XMLElements next(String selector);

    /**
     * Aggregation: executes {@link XMLElement#next(Predicate)} on each element and aggregates results.
     *
     * @param selector selector
     * @return XMLElements
     */
    XMLElements next(Predicate<XMLContext> selector);

    /**
     * Aggregation: executes {@link XMLElement#prev()} on each element and aggregates results.
     *
     * @return XMLElements
     */
    XMLElements prev();

    /**
     * Aggregation: executes {@link XMLElement#prev(String)} on each element and aggregates results.
     *
     * @param selector selector
     * @return XMLElements
     */
    XMLElements prev(String selector);

    /**
     * Aggregation: executes {@link XMLElement#prev(Predicate)} on each element and aggregates results.
     *
     * @param selector selector
     * @return XMLElements
     */
    XMLElements prev(Predicate<XMLContext> selector);

    /**
     * Aggregation: executes {@link XMLElement#siblings()} on each element and aggregates results.
     *
     * @return XMLElements
     */
    XMLElements siblings();

    /**
     * Aggregation: executes {@link XMLElement#siblings(String)} on each element and aggregates results.
     *
     * @param selector selector
     * @return XMLElements
     */
    XMLElements siblings(String selector);

    /**
     * Aggregation: executes {@link XMLElement#siblings(Predicate)} on each element and aggregates results.
     *
     * @param selector selector
     * @return XMLElements
     */
    XMLElements siblings(Predicate<XMLContext> selector);

    /**
     * Transform: maps each element using {@link XMLElement#parent()}
     *
     * @return XMLElements
     */
    XMLElements parent();

    /**
     * Aggregation: executes {@link XMLElement#parents(String)} on each element and aggregates results.
     *
     * @param selector selector
     * @return XMLElements
     */
    XMLElements parent(String selector);

    /**
     * Aggregation: executes {@link XMLElement#parents(Predicate)} on each element and aggregates results.
     *
     * @param selector selector
     * @return XMLElements
     */
    XMLElements parent(Predicate<XMLContext> selector);


    /**
     * Executes for each element {@link XMLElement#normalize()}
     *
     * @return this
     */
    XMLElements normalize();

    /**
     * Executes for each element {@link XMLElement#removeAll()}
     *
     * @return this
     */
    XMLElements removeAll();

    /**
     * Executes for each element {@link XMLElement#before(XMLElement)}
     *
     * @param element element
     * @return this
     */
    XMLElements before(XMLElement element);

    /**
     * Executes for each element {@link XMLElement#after(XMLElement)}
     *
     * @param element element
     * @return this
     */
    XMLElements after(XMLElement element);

    /**
     * Executes for each element {@link XMLElement#prepend(XMLElement)}
     *
     * @param element element
     * @return this
     */
    XMLElements prepend(XMLElement element);

    /**
     * Executes for each element {@link XMLElement#append(XMLElement)}
     *
     * @param element element
     * @return this
     */
    XMLElements append(XMLElement element);

    /**
     * Executes for each element {@link XMLElement#replace(XMLElement)}
     *
     * @param element element
     * @return this
     */
    XMLElements replace(XMLElement element);

    /**
     * Executes for each element {@link XMLElement#rename(String)}
     *
     * @param tagname tagname
     * @return this
     */
    XMLElements rename(String tagname);

    /**
     * Executes for each element {@link XMLElement#remove()}
     *
     * @return this
     */
    XMLElements remove();

    /**
     * Executes for each element {@link XMLElement#wrap(String)}
     *
     * @param tagname tagname
     * @return this
     */
    XMLElements wrap(String tagname);

    /**
     * Executes for each element {@link XMLElement#unwrap()}
     *
     * @return this
     */
    XMLElements unwrap();

    /**
     * Executes for each element {@link XMLElement#content(XMLElement)}
     *
     * @param value value
     * @return this
     */
    XMLElements content(XMLElement value);

    /**
     * Executes for each element {@link XMLElement#content(String)}
     *
     * @param value value
     * @return this
     */
    XMLElements content(String value);

    /**
     * Executes for each element {@link XMLElement#text(String)}
     *
     * @param value value
     * @return this
     */
    XMLElements text(String value);
        
}
