package ru.yandex.qe.jetty.server;

import java.util.ArrayList;

import javax.servlet.Servlet;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;

import org.eclipse.jetty.http.pathmap.ServletPathSpec;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlet.ServletMapping;
import org.eclipse.jetty.servlet.Source;
import org.eclipse.jetty.util.LazyList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Accepts a class, which should be annotated as WebServlet.
 * Contains ServletHolder and path to bind.
 * @author lvovich
 */
public class AnnotationConfiguredServlet {

    private final static Logger LOG = LoggerFactory.getLogger(AnnotationConfiguredServlet.class);

    private final ServletHolder servletHolder;
    private final ServletMapping servletMapping;

    private AnnotationConfiguredServlet(final ServletHolder servletHolder, final ServletMapping servletMapping) {
        this.servletHolder = servletHolder;
        this.servletMapping = servletMapping;
    }

    public ServletHolder getServletHolder() {
        return servletHolder;
    }

    public ServletMapping getServletMapping() {
        return servletMapping;
    }

    /**
     * Constructs the servlet from annotated class
     * @param rawClazz
     * @return null if the class does not conform to Servlet 3.0 spec
     */
    public static AnnotationConfiguredServlet construct(Class<?> rawClazz) {

        //Servlet Spec 8.1.1
        if (!HttpServlet.class.isAssignableFrom(rawClazz))
        {
            LOG.warn(rawClazz.getName()+" is not assignable from javax.servlet.http.HttpServlet");
            return null;
        }

        Class<Servlet> clazz = (Class<Servlet>) rawClazz;

        WebServlet annotation = (WebServlet)clazz.getAnnotation(WebServlet.class);

        if (annotation.urlPatterns().length > 0 && annotation.value().length > 0)
        {
            LOG.warn(clazz.getName()+ " defines both @WebServlet.value and @WebServlet.urlPatterns");
            return null;
        }

        String[] urlPatterns = annotation.value();
        if (urlPatterns.length == 0)
            urlPatterns = annotation.urlPatterns();

        if (urlPatterns.length == 0)
        {
            LOG.warn(clazz.getName()+ " defines neither @WebServlet.value nor @WebServlet.urlPatterns");
            return null;
        }

        //canonicalize the patterns
        ArrayList<String> urlPatternList = new ArrayList<String>();
        for (String p : urlPatterns)
            urlPatternList.add(ServletPathSpec.normalize(p));

        String servletName = (annotation.name().equals("")?clazz.getName():annotation.name());

        Source source = new Source(Source.Origin.ANNOTATION, clazz.getName());

        ServletHolder holder = new ServletHolder(source);
        holder.setHeldClass(clazz);
        holder.setName(servletName);
        holder.setDisplayName(annotation.displayName());
        holder.setInitOrder(annotation.loadOnStartup());
        holder.setAsyncSupported(annotation.asyncSupported());
        for (WebInitParam ip:annotation.initParams())
        {
            holder.setInitParameter(ip.name(), ip.value());
        }

        ServletMapping mapping = new ServletMapping(source);
        mapping.setServletName(holder.getName());
        mapping.setPathSpecs( LazyList.toStringArray(urlPatternList));

        return new AnnotationConfiguredServlet(holder, mapping);
    }
}
