package com.wizzardo.http.mapping;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by wizzardo on 16.04.15.
 */
public class UrlTemplate {
    private String host;
    private String context;
    private List<TemplateHolder> holders = new ArrayList<TemplateHolder>();
    private static Pattern variables = Pattern.compile("(/)?\\$\\{?([a-zA-Z_]+[\\w]*)\\}?\\??");

    public UrlTemplate(String host, String context, String url) {
        this.host = host;
        this.context = context;

        if (url.endsWith("/*"))
            url = url.substring(0, url.length() - 2);
        else if (url.endsWith("*"))
            url = url.substring(0, url.length() - 1);

        Matcher m = variables.matcher(url);
        if (!m.find()) {
            holders.add(new StringHolder(url));
        } else {
            int last = 0;
            do {
                if (last != m.start())
                    holders.add(new StringHolder(url.substring(last, m.start())));

                String prefix = null;
                if (m.start() != 0) {
                    prefix = m.group(1);
                } else {
                    holders.add(new StringHolder("/"));
                }

                holders.add(new VariableHolder(m.group(2), prefix, m.group().endsWith("?")));
                last = m.end();
            } while (m.find());
            if (last < url.length())
                holders.add(new StringHolder(url.substring(last)));
        }
    }

    public String getUrl(String base, Map<String, Object> params) {
        return getRelativeUrl(params, null, new StringBuilder(base));
    }

    public String getAbsoluteUrl() {
        return getRelativeUrl(null, null, new StringBuilder(host));
    }

    public String getAbsoluteUrl(Map<String, Object> params) {
        return getRelativeUrl(params, null, new StringBuilder(host));
    }

    public String getAbsoluteUrl(Map<String, Object> params, String suffix) {
        return getRelativeUrl(params, suffix, new StringBuilder(host));
    }

    public String getAbsoluteUrl(String suffix) {
        return getRelativeUrl(null, suffix, new StringBuilder(host));
    }

    public String getRelativeUrl() {
        return getRelativeUrl(null, null, null);
    }

    public String getRelativeUrl(Map<String, Object> params) {
        return getRelativeUrl(params, null, null);
    }

    public String getRelativeUrl(Map<String, Object> params, String suffix) {
        return getRelativeUrl(params, suffix, null);
    }

    public String getRelativeUrl(String suffix) {
        return getRelativeUrl(null, suffix, null);
    }

    public String getRelativeUrl(Map<String, ?> paramsSrc, String suffix, StringBuilder sb) {
        if (sb == null)
            sb = new StringBuilder();

        if (context != null)
            sb.append('/').append(context);

        Map<String, Object> params;

        if (paramsSrc != null)
            params = new LinkedHashMap<>(paramsSrc);
        else
            params = Collections.emptyMap();

        for (TemplateHolder holder : holders) {
            if (!holder.append(sb, params))
                break;
        }

        if (suffix != null)
            sb.append(suffix);

        if (!params.isEmpty()) {
            sb.append("?");
            boolean and = false;
            for (Map.Entry param : params.entrySet()) {
                if (and)
                    sb.append("&");
                else
                    and = true;
                sb.append(param.getKey()).append("=").append(param.getValue());
            }
        }
        return sb.toString();
    }

    interface TemplateHolder {
        boolean append(StringBuilder sb, Map params);
    }

    class StringHolder implements TemplateHolder {
        String value;
        byte[] bytes;

        public StringHolder(String value) {
            this.value = value;
            bytes = value.getBytes();
        }


        @Override
        public boolean append(StringBuilder sb, Map params) {
            sb.append(value);
            return true;
        }
    }

    class VariableHolder implements TemplateHolder {
        String param;
        String prefix;
        byte[] prefixBytes;
        boolean optional = false;

        public VariableHolder(String param, String prefix, boolean optional) {
            this.param = param;
            this.optional = optional;
            this.prefix = prefix;
            if (prefix != null)
                prefixBytes = prefix.getBytes();
        }

        @Override
        public boolean append(StringBuilder sb, Map params) {
            Object value = params.remove(param);
            if (optional && value == null)
                return false;
            if (prefix != null)
                sb.append(prefix);
            sb.append(value);
            return true;
        }
    }
}

@@ -3822,30 +3822,8 @@
 lue;
-%0A        byte%5B%5D bytes;
 %0A%0A  
@@ -3900,46 +3900,8 @@
 ue;%0A
-            bytes = value.getBytes();%0A
     
@@ -4162,36 +4162,8 @@
 ix;%0A
-        byte%5B%5D prefixBytes;%0A
     
@@ -4342,16 +4342,16 @@
 tional;%0A
+
         
@@ -4380,89 +4380,8 @@
 ix;%0A
-            if (prefix != null)%0A                prefixBytes = prefix.getBytes();%0A
     

remove useless fields