/**
 * Copyright (C) 2013 Matija Mazi
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package si.mazi.rescu;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * This class provides support for various types of HTTP params, especially in the context of RESTful web services, but may be also used to construct urls in other contexts.
 * <p/>
 * Eg. this can be used to produce a URL query string:
 * <p>
 * Params.of("username", "john", "score", 2, "answer", "yes/no").asQueryString()
 * </p>
 * will produce:
 * <p>
 * username=john&score=2&answer=yes%2Fno
 * </p>
 *
 * @author Matija Mazi
 */
public class Params implements Serializable {

    private Map<String, Object> data = new LinkedHashMap<String, Object>();
    private RestMethodMetadata restMethodMetadata;

    /**
     * private Constructor to prevent instantiation
     */
    private Params() {

    }

    public static Params of() {

        return new Params();
    }

    public static Params of(String param, Object value) {

        return of().add(param, value);
    }

    public static Params of(String p1, Object v1, String p2, Object v2) {

        return of(p1, v1).add(p2, v2);
    }

    public static Params of(String p1, Object v1, String p2, Object v2, String p3, Object v3) {

        return of(p1, v1, p2, v2).add(p3, v3);
    }

    public static Params of(String p1, Object v1, String p2, Object v2, String p3, Object v3, String p4, Object v4) {

        return of(p1, v1, p2, v2, p3, v3).add(p4, v4);
    }

    public Params add(String param, Object value) {

        data.put(param, value);
        return this;
    }

    private String toQueryString(boolean encode) {

        StringBuilder b = new StringBuilder();
        for (String param : data.keySet()) {
            if (isParamSet(param)) {
                if (b.length() > 0) {
                    b.append('&');
                }
                b.append(param).append('=').append(encode(getParamValue(param), encode));
            }
        }
        return b.toString();
    }

    private String encode(String data, boolean encode) {

        try {
            return encode ? URLEncoder.encode(data, "UTF-8") : data;
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("Illegal encoding, fix the code.", e); // should not happen
        }
    }

    void setRestMethodMetadata(RestMethodMetadata restMethodMetadata) {

        this.restMethodMetadata = restMethodMetadata;
    }

    public String asQueryString() {

        return toQueryString(true);
    }

    public String asFormEncodedRequestBody() {

        return toQueryString(false);
    }

    public boolean isEmpty() {

        return data.isEmpty();
    }

    public String applyToPath(String path) {

        for (String paramName : data.keySet()) {
            if (!isParamSet(paramName)) {
                throw new IllegalArgumentException("The value of '" + paramName + "' path parameter was not specified.");
            }
            path = path.replace("{" + paramName + "}", getParamValue(paramName));
        }
        return path;
    }

    public Map<String, String> asHttpHeaders() {

        Map<String, String> stringMap = new LinkedHashMap<String, String>();
        for (String key : data.keySet()) {
            if (isParamSet(key)) {
                stringMap.put(key, getParamValue(key));
            }
        }
        return stringMap;
    }

    private String getParamValue(String key) {

        Object paramValue = data.get(key);
        if (paramValue instanceof ParamsDigest) {
            return ((ParamsDigest) paramValue).digestParams(restMethodMetadata);
        }
        // return new ObjectMapper().writeValueAsString(paramValue);
        return paramValue.toString();
    }

    private boolean isParamSet(String key) {

        return data.containsKey(key) && data.get(key) != null;
    }

    @Override
    public String toString() {

        return toQueryString(false);
    }
}

@@ -1993,25 +1993,24 @@
  Params() %7B%0A
-%0A
     %7D%0A%0A    p
@@ -2032,25 +2032,24 @@
 rams of() %7B%0A
-%0A
         retu
@@ -2122,33 +2122,32 @@
 Object value) %7B%0A
-%0A
         return o
@@ -2246,25 +2246,24 @@
 bject v2) %7B%0A
-%0A
         retu
@@ -2388,25 +2388,24 @@
 bject v3) %7B%0A
-%0A
         retu
@@ -2560,25 +2560,24 @@
 bject v4) %7B%0A
-%0A
         retu
@@ -2674,25 +2674,24 @@
 ct value) %7B%0A
-%0A
         data
@@ -2781,33 +2781,32 @@
 olean encode) %7B%0A
-%0A
         StringBu
@@ -3212,25 +3212,24 @@
 n encode) %7B%0A
-%0A
         try 
@@ -3534,25 +3534,24 @@
 Metadata) %7B%0A
-%0A
         this
@@ -3627,33 +3627,32 @@
 QueryString() %7B%0A
-%0A
         return t
@@ -3721,25 +3721,24 @@
 estBody() %7B%0A
-%0A
         retu
@@ -3796,25 +3796,24 @@
 isEmpty() %7B%0A
-%0A
         retu
@@ -3879,25 +3879,24 @@
 ing path) %7B%0A
-%0A
         for 
@@ -4275,25 +4275,24 @@
 Headers() %7B%0A
-%0A
         Map%3C
@@ -4586,33 +4586,32 @@
 e(String key) %7B%0A
-%0A
         Object p
@@ -4933,25 +4933,24 @@
 ring key) %7B%0A
-%0A
         retu
@@ -5021,16 +5021,16 @@
 verride%0A
+
     publ
@@ -5052,17 +5052,16 @@
 ing() %7B%0A
-%0A
         

Reformat code -- testing travis