package org.dynmap.servlet;

import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

@SuppressWarnings("serial")
public class MainServlet extends HttpServlet {
    public static class Header {
        public String name;
        public String value;
        public Header(String name, String value) {
            this.name = name;
            this.value = value;
        }
    }
    
    private static class Registration {
        public String pattern;
        public HttpServlet servlet;
        
        public Registration(String pattern, HttpServlet servlet) {
            this.pattern = pattern;
            this.servlet = servlet;
        }
    }
    
    List<Registration> registrations = new LinkedList<Registration>();
    public List<Header> customHeaders = new LinkedList<Header>();
    
    public void addServlet(String pattern, HttpServlet servlet) {
        registrations.add(new Registration(pattern, servlet));
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HashMap<String, Object> properties = new HashMap<String, Object>();
        String path = req.getPathInfo();
        
        for(Header header : customHeaders) {
            resp.setHeader(header.name, header.value);
        }
        
        Registration bestMatch = null;
        String bestMatchPart = null;
        HashMap<String, Object> bestProperties = null;
        
        for (Registration r : registrations) {
            String matchingPart = match(r.pattern, path, properties);
            if (matchingPart != null) {
                if (bestMatchPart == null || bestMatchPart.length() < matchingPart.length()) {
                    bestMatch = r;
                    bestMatchPart = matchingPart;
                    bestProperties = properties;
                    properties = new HashMap<String, Object>();
                }
            }
        }
        if (bestMatch == null) {
            resp.sendError(404);
        } else {
            String leftOverPath = path.substring(bestMatchPart.length());
            HttpServletRequest newreq = new RequestWrapper(req, leftOverPath);
            for(String key : bestProperties.keySet()) {
                newreq.setAttribute(key,  bestProperties.get(key));
            }
            bestMatch.servlet.service(newreq, resp);
        }
    }
    
    public String match(String pattern, String path, Map<String, Object> properties) {
        int patternStart = 0;
        int pathStart = 0;
        while (patternStart < pattern.length()) {
            if (pattern.charAt(patternStart) == '{') {
                // Found a variable.
                int endOfVariable = pattern.indexOf('}', patternStart+1);
                String variableName = pattern.substring(patternStart+1, endOfVariable);
                
                int endOfSection = indexOfAny(path, new char[] { '/', '?' }, pathStart);
                if (endOfSection < 0) {
                    endOfSection = path.length();
                }
                String variableValue = path.substring(pathStart, endOfSection);
                
                // Store variable.
                properties.put(variableName, variableValue);
                
                patternStart = endOfVariable+1;
                pathStart = endOfSection;
            } else {
                int endOfLiteral = pattern.indexOf('{', patternStart);
                if (endOfLiteral < 0) {
                    endOfLiteral = pattern.length();
                }
                String literal = pattern.substring(patternStart, endOfLiteral);
                int endOfPathLiteral = pathStart + literal.length();
                if (endOfPathLiteral > path.length()) {
                    return null;
                }
                String matchingLiteral = path.substring(pathStart, endOfPathLiteral);
                if (!literal.equals(matchingLiteral)) {
                    return null;
                }
                
                patternStart = endOfLiteral;
                pathStart = endOfPathLiteral;
            }
        }
        // Return the part of the url that matches the pattern. (if the pattern does not contain any variables, this will be equal to the pattern)
        return path.substring(0, pathStart);
    }
    
    private int indexOfAny(String s, char[] cs, int startIndex) {
        for(int i = startIndex; i < s.length(); i++) {
            char c = s.charAt(i);
            for(int j = 0; j < cs.length; j++) {
                if (c == cs[j]) {
                    return i;
                }
            }
        }
        return -1;
    }
    
    class RequestWrapper extends HttpServletRequestWrapper {
        String pathInfo;
        public RequestWrapper(HttpServletRequest request, String pathInfo) {
            super(request);
            this.pathInfo = pathInfo;
        }
        @Override
        public String getPathInfo() {
            return pathInfo;
        }
    }
}
