package estivate;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import estivate.annotations.Select;
import estivate.core.ast.EstivateAST;
import estivate.core.ast.parser.EstivateParser;
import estivate.core.eval.EstivateEvaluator;
import estivate.core.eval.EstivateEvaluator.EvalContext;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
 * <ul>
 * <li>parse members and call registered implementation</li>
 * <li>use dialect JSoup by default</li>
 * <li>get members ordered to evaluate</li>
 * <li>evaluate select elements</li>
 * <li>evaluate reduce value</li>
 * <li>converts value</li>
 * <li>sets value to target</li>
 * </ul>
 * 
 * @author Benoit Theunissen
 *
 */
@Slf4j
public class EstivateMapper {

    @Getter
    @Setter
    protected String encoding = "UTF-8";

    @Getter
    @Setter
    protected String baseURI = "/";

    protected static final String PACKAGE_NAME = Select.class.getPackage().getName();

    protected static final List<Class<?>> STANDARD_TARGET_TYPES = new ArrayList<Class<?>>();
    static {
        STANDARD_TARGET_TYPES.add(Document.class);
        STANDARD_TARGET_TYPES.add(Elements.class);
        STANDARD_TARGET_TYPES.add(Element.class);
    }

    public <T> T map(InputStream document, Class<T> clazz) throws IOException {
        Document doc = Jsoup.parse(document, this.encoding, this.baseURI);
        return this.map(doc, clazz);
    }

    @SuppressWarnings("unchecked")
    public <T> T map(Document document, Class<T> clazz) {

        EstivateAST ast = EstivateParser.parse(clazz);

        EvalContext context = EstivateEvaluator.buildEvalContext(document, new Elements(document), ast);

        return (T) EstivateEvaluator.eval(context, ast);
    }

    public <T> List<T> mapToList(InputStream document, Class<T> clazz) throws IOException {
        Document doc = Jsoup.parse(document, this.encoding, this.baseURI);
        return this.mapToList(doc, clazz);
    }

    @SuppressWarnings("unchecked")
    public <T> List<T> mapToList(Document document, Class<T> clazz) {

        EstivateAST ast = EstivateParser.parse(clazz);

        EvalContext context = EstivateEvaluator.buildEvalContext(document, new Elements(document), ast);

        return (List<T>) EstivateEvaluator.evalToList(context, ast);
    }

    public Object map(InputStream document, Type type) throws IOException {
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;

            // Handle type parameter class
            Class<?> classArgument = (Class<?>) parameterizedType.getActualTypeArguments()[0];

            // Handle type class
            Class<?> rowClass = (Class<?>) parameterizedType.getRawType();

            if (Collection.class.isAssignableFrom(rowClass)) {

                return this.mapToList(document, classArgument);

            } else {
                log.debug(rowClass.getCanonicalName() + " is not a collection");

                throw new IllegalArgumentException("Parameterized type not handled: " + rowClass.getCanonicalName());
            }
        } else {
            return this.map(document, (Class<?>) type);
        }
    }

}

@@ -1601,166 +1601,759 @@
 eam 
-document, Class%3CT%3E clazz) throws IOException %7B%0D%0A        Document doc = Jsoup.parse(document, this.encoding, this.baseURI);%0D%0A        return this.map(doc, clazz
+stream, Class%3CT%3E clazz) throws IOException %7B%0D%0A        Document doc = parseStream(stream);%0D%0A        log.debug(%22%7B%7D%22, doc.toString());%0D%0A        return this.map(doc, clazz);%0D%0A    %7D%0D%0A%0D%0A    public %3CT%3E List%3CT%3E mapToList(InputStream stream, Class%3CT%3E clazz) throws IOException %7B%0D%0A        Document doc = parseStream(stream);%0D%0A        log.debug(%22%7B%7D%22, doc.toString());%0D%0A        return this.mapToList(doc, clazz);%0D%0A    %7D%0D%0A%0D%0A    public Object map(InputStream stream, Type type) throws IOException %7B%0D%0A        Document doc = parseStream(stream);%0D%0A        log.debug(%22%7B%7D%22, doc.toString());%0D%0A        return map(doc, type);%0D%0A    %7D%0D%0A%0D%0A    private Document parseStream(InputStream stream) throws IOException %7B%0D%0A        return Jsoup.parse(stream, this.encoding, this.baseURI
 );%0D%0A
@@ -2695,230 +2695,8 @@
 %0D%0A%0D%0A
-    public %3CT%3E List%3CT%3E mapToList(InputStream document, Class%3CT%3E clazz) throws IOException %7B%0D%0A        Document doc = Jsoup.parse(document, this.encoding, this.baseURI);%0D%0A        return this.mapToList(doc, clazz);%0D%0A    %7D%0D%0A%0D%0A
     
@@ -3067,27 +3067,24 @@
 ect map(
-InputStream
+Document
  documen
@@ -3561,24 +3561,107 @@
 owClass)) %7B%0D
+%0A                log.debug(rowClass.getCanonicalName() + %22 is a Collection type%22);%0D
 %0A%0D%0A         
@@ -3712,26 +3712,24 @@
 Argument);%0D%0A
-%0D%0A
             
@@ -3750,37 +3750,37 @@
             log.
-debug
+error
 (rowClass.getCan
@@ -3805,17 +3805,17 @@
 s not a 
-c
+C
 ollectio
@@ -3815,16 +3815,21 @@
 llection
+ type
 %22);%0D%0A%0D%0A 
@@ -4054,13 +4054,11 @@
 %0A    %7D%0D%0A
-%0D%0A
 %7D%0D%0A

add facade parse methods