package com.onelogin.saml2.http;

import static com.onelogin.saml2.util.Preconditions.checkNotNull;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;

import com.onelogin.saml2.util.Util;

/**
 * Framework-agnostic representation of an HTTP request.
 *
 * @since 2.0.0
 */
public final class HttpRequest {

    public static final Map<String, List<String>> EMPTY_PARAMETERS = Collections.<String, List<String>>emptyMap();

    private final String requestURL;
    private final Map<String, List<String>> parameters;
    private final String queryString;

    /**
     * Creates a new HttpRequest.
     *
     * @param requestURL the request URL (up to but not including query parameters)
     * @throws NullPointerException if requestURL is null
     * @deprecated Not providing a queryString can cause HTTP Redirect binding to fail.
     */
    @Deprecated
    public HttpRequest(String requestURL) {
        this(requestURL, EMPTY_PARAMETERS);
    }

    /**
     * Creates a new HttpRequest.
     *
     * @param requestURL  the request URL (up to but not including query parameters)
     * @param queryString string that is contained in the request URL after the path
     */
    public HttpRequest(String requestURL, String queryString) {
        this(requestURL, EMPTY_PARAMETERS, queryString);
    }

    /**
     * Creates a new HttpRequest.
     *
     * @param requestURL  the request URL (up to but not including query parameters)
     * @param parameters the request query parameters
     * @throws NullPointerException if any of the parameters is null
     * @deprecated Not providing a queryString can cause HTTP Redirect binding to fail.
     */
    @Deprecated
    public HttpRequest(String requestURL, Map<String, List<String>> parameters) {
        this(requestURL, parameters, null);
    }

    /**
     * Creates a new HttpRequest.
     *
     * @param requestURL  the request URL (up to but not including query parameters)
     * @param parameters the request query parameters
     * @param queryString string that is contained in the request URL after the path
     * @throws NullPointerException if any of the parameters is null
     */
    public HttpRequest(String requestURL, Map<String, List<String>> parameters, String queryString) {
        this.requestURL = checkNotNull(requestURL, "requestURL");
        this.parameters = unmodifiableCopyOf(checkNotNull(parameters, "queryParams"));
        this.queryString = StringUtils.trimToEmpty(queryString);
    }

    /**
     * @param name  the query parameter name
     * @param value the query parameter value
     * @return a new HttpRequest with the given query parameter added
     * @throws NullPointerException if any of the parameters is null
     */
    public HttpRequest addParameter(String name, String value) {
        checkNotNull(name, "name");
        checkNotNull(value, "value");

        final List<String> oldValues = parameters.containsKey(name) ? parameters.get(name) : new ArrayList<String>();
        final List<String> newValues = new ArrayList<>(oldValues);
        newValues.add(value);
        final Map<String, List<String>> params = new HashMap<>(parameters);
        params.put(name, newValues);

        return new HttpRequest(requestURL, params, queryString);
    }

    /**
     * @param name  the query parameter name
     * @return a new HttpRequest with the given query parameter removed
     * @throws NullPointerException if any of the parameters is null
     */
    public HttpRequest removeParameter(String name) {
        checkNotNull(name, "name");

        final Map<String, List<String>> params = new HashMap<>(parameters);
        params.remove(name);

        return new HttpRequest(requestURL, params, queryString);
    }
    
    /**
     * The URL the client used to make the request. Includes a protocol, server name, port number, and server path, but
     * not the query string parameters.
     *
     * @return the request URL
     */
    public String getRequestURL() {
        return requestURL;
    }

    /**
     * @param name the query parameter name
     * @return the first value for the parameter, or null
     */
    public String getParameter(String name) {
        List<String> values = getParameters(name);
        return values.isEmpty() ? null : values.get(0);
    }

    /**
     * @param name the query parameter name
     * @return a List containing all values for the parameter
     */
    public List<String> getParameters(String name) {
        List<String> values = parameters.get(name);
        return values != null ? values : Collections.<String>emptyList();
    }

    /**
     * @return a map of all query parameters
     */
    public Map<String, List<String>> getParameters() {
        return parameters;
    }

    /**
     * Return an url encoded get parameter value
     * Prefer to extract the original encoded value directly from queryString since url
     * encoding is not canonical.
     *
     * @param name
     * @return the first value for the parameter, or null
     */
    public String getEncodedParameter(String name) {
        Matcher matcher = Pattern.compile(Pattern.quote(name) + "=([^&#]+)").matcher(queryString);
        if (matcher.find()) {
            return matcher.group(1);
        } else {
            return Util.urlEncoder(getParameter(name));
        }
    }

    /**
     * Return an url encoded get parameter value
     * Prefer to extract the original encoded value directly from queryString since url
     * encoding is not canonical.
     *
     * @param name
     * @param defaultValue
     * @return the first value for the parameter, or url encoded default value
     */
    public String getEncodedParameter(String name, String defaultValue) {
            String value = getEncodedParameter(name);
            return (value != null ? value : Util.urlEncoder(defaultValue));
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
        	return true;
        }

        if (o == null || getClass() != o.getClass()) {
        	return false;
        }

        HttpRequest that = (HttpRequest) o;
        return Objects.equals(requestURL, that.requestURL) &&
                Objects.equals(parameters, that.parameters) &&
                Objects.equals(queryString, this.queryString);
    }

    @Override
    public int hashCode() {
        return Objects.hash(requestURL, parameters, queryString);
    }

    @Override
    public String toString() {
        return "HttpRequest{" +
                "requestURL='" + requestURL + '\'' +
                ", parameters=" + parameters +
                ", queryString=" + queryString +
                '}';
    }

    private static Map<String, List<String>> unmodifiableCopyOf(Map<String, List<String>> orig) {
        Map<String, List<String>> copy = new HashMap<>();
        for (Map.Entry<String, List<String>> entry : orig.entrySet()) {
            copy.put(entry.getKey(), unmodifiableList(new ArrayList<>(entry.getValue())));
        }

        return unmodifiableMap(copy);
    }


}
@@ -6609,18 +6609,18 @@
 ring, th
-is
+at
 .querySt
@@ -7379,8 +7379,9 @@
    %7D%0A%0A%0A%7D
+%0A

Fix typo in HttpRequest.equals()