package com.quui.tm2;

import java.io.Serializable;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.List;

import com.quui.tm2.util.FileIO;

/**
 * @author Fabian Steeg (fsteeg)
 */
public final class Retrieval {
    /***/
    private AnnotationReader reader;

    /**
     * @param location The location of the XML file containing the annotations written by an
     *        experiment
     */
    public Retrieval(final String location) {
        reader = new AnnotationReader(location);
    }

    public Retrieval(Experiment experiment) throws MalformedURLException {
        this(experiment.getOutputAnnotationLocation());
    }

    /**
     * @return Returns a list of agent names that wrote annotations into the file associated with
     *         this retrieval
     */
    public List<String> getAgents() {
        return reader.readAgents();
    }

    /**
     * @param <S> The type of annotations to search in
     * @param <R> The type of annotations to return results for
     * @param search The agent that produced the annotations to search in
     * @param value The value to search for
     * @param from The agent that produced the annotations to return the result from
     * @return Returns a List of annotations produced by the <code>from</code> agent, which
     *         correspond to the annotations produced by <code>search</code> with the specified
     *         value
     */
    public <S extends Comparable<S> & Serializable, R extends Comparable<R> & Serializable> List<Annotation<R>> search(
            final Class<? extends Agent<?, S>> search, final S value,
            final Class<? extends Agent<?, R>> from) {
        List<Annotation<S>> annotationToSearchIn = reader.readAnnotations(search);
        List<Annotation<S>> hits = new ArrayList<Annotation<S>>();
        // TODO use binary search?
        for (Annotation<S> annotation : annotationToSearchIn) {
            if (annotation.getValue().equals(value)) {
                hits.add(annotation);
            }
        }
        List<Annotation<R>> result = new ArrayList<Annotation<R>>();
        // TODO do this more efficiently:
        List<Annotation<R>> annotationsToReturnFrom = reader.readAnnotations(from);
        for (Annotation<R> ra : annotationsToReturnFrom) {
            for (Annotation<S> ha : hits) {
                // TODO add to interface?
                if (((ImmutableAnnotation<R>) ra).covers((ImmutableAnnotation<S>) ha)) {
                    result.add(ra);
                }
            }
        }
        return result;
    }

    /**
     * @param <T> The type of the annotations
     * @param agent The agent
     * @return Returns the annotations produced by the agent
     */
    public <T extends Comparable<T> & Serializable> List<Annotation<T>> annotationsOf(
            final Class<? extends Agent<?, T>> agent) {
        return reader.readAnnotations(agent);
    }

    /**
     * @return Returns the actual data, the XML string containing the annotations
     */
    public String getData() {
        // TODO should this method really exist?
        String data = FileIO.read(reader.getData());
        return data;
    }

    /**
     * @param <T> The annotation type
     * @param annotations The annotations to get the values of
     * @return Returns a list containing the values of the annotations
     */
    public static <T extends Comparable<T>> List<T> valuesOf(final List<Annotation<T>> annotations) {
        List<T> result = new ArrayList<T>();
        for (Annotation<T> a : annotations) {
            result.add(a.getValue());
        }
        return result;
    }

    /**
     * @return The data location TODO: remove?
     */
    public String getDataLocation() {
        return reader.getData();

    }

    public <T extends Comparable<T> & Serializable> Annotation<T> annotationCorresponding(
            Annotation<?> a, Class<? extends Agent<?, T>> by) {
        List<Annotation<T>> candidates = annotationsOf(by);
        for (Annotation<T> annotation : candidates) {
            if (Annotations.fullOverlap(annotation, a)) {
                return annotation;
            }
        }
        return null;
    }
}
