// Path: http-builder-ng-core/src/main/java/groovyx/net/http/Traverser.java
// public static <V> Predicate<V> notValue(final V v) { return (toTest) -> !v.equals(toTest); }
// 
// Path: http-builder-ng-core/src/main/java/groovyx/net/http/Traverser.java
// public static <T,V> V traverse(final T target, final Function<T,T> next,
//                                final Function<T,V> getValue, final Predicate<V> testValue) {
//     final V v = getValue.apply(target);
//     if(testValue.test(v)) {
//         return v;
//     }
// 
//     final T nextTarget = next.apply(target);
//     if(nextTarget != null) {
//         return traverse(nextTarget, next, getValue, testValue);
//     }
// 
//     return null;
// }


import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.singletonList;
import groovy.lang.GString;
import org.codehaus.groovy.runtime.GStringImpl;
import java.io.IOException;
import java.net.HttpCookie;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import static groovyx.net.http.Traverser.notValue;
import static groovyx.net.http.Traverser.traverse;
import static java.lang.String.format;
/**
 * Copyright (C) 2017 HttpBuilder-NG Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package groovyx.net.http;




/**
 * Provides a simple means of creating a request URI and optionally overriding its parts.
 *
 * [source,groovy]
 * ----
 * def uri = UriBuilder.basic(UriBuilder.root())
 * .setFull('http://localhost:10101')
 * .setPath('/foo')
 * .toURI()
 * ----
 *
 * Generally, this class is not instantiated directly, but created by the {@link HttpConfig} instance and modified.
 */
public abstract class UriBuilder {

    public static final int DEFAULT_PORT = -1;

    /**
     * Sets the scheme part of the URI.
     *
     * @param val the value to use as the scheme part of the URI
     * @return a reference to this builder
     */
    public abstract UriBuilder setScheme(String val);

    /**
     * Retrieves the scheme part of the URI.
     *
     * @return the URI scheme
     */
    public abstract String getScheme();

    /**
     * Sets the port part of the URI.
     *
     * @param val the value to use as the port part of the URI
     * @return a reference to this builder
     */
    public abstract UriBuilder setPort(int val);

    /**
     * Retrieves the port part of the URI.
     *
     * @return the port part of the URI
     */
    public abstract int getPort();

    /**
     * Sets the host part of the URI.
     *
     * @param val the value to use as the host part of the URI
     * @return a reference to this builder
     */
    public abstract UriBuilder setHost(String val);

    /**
     * Retrieves the host part of the URI.
     *
     * @return the host part of the URI
     */
    public abstract String getHost();

    /**
     * Sets the path part of the URI.
     *
     * @param val the path part of the URI
     * @return a reference to the builder
     */
    public abstract UriBuilder setPath(GString val);

    /**
     * Retrieves the path part of the URI.
     *
     * @return the path part of the URI
     */
    public abstract GString getPath();

    /**
     * Sets the query string part of the `URI` from the provided map. The query string key-value pairs will be generated from the key-value pairs
     * of the map and are NOT URL-encoded. Nested maps or other data structures are not supported.
     *
     * @param val the map of query string parameters
     * @return a reference to the builder
     */
    public abstract UriBuilder setQuery(Map<String, ?> val);

    /**
     * Retrieves the `Map` of query string parameters for the `URI`.
     *
     * @return the `Map` of query string parameters for the `URI`.
     */
    public abstract Map<String, ?> getQuery();

    /**
     * Sets the fragment part of the `URI`.
     *
     * @param val the fragment part of the `URI`
     * @return a reference to the builder
     */
    public abstract UriBuilder setFragment(String val);

    /**
     * Retrieves the fragment part of the `URI`.
     *
     * @return the fragment part of the `URI`
     */
    public abstract String getFragment();

    /**
     * Sets the user info part of the `URI`.
     *
     * @param val the user info part of the `URI`
     * @return a reference to the builder
     */
    public abstract UriBuilder setUserInfo(String val);

    /**
     * Retrieves the user info part of the `URI`.
     *
     * @return the user info part of the `URI`
     */
    public abstract String getUserInfo();

    public abstract UriBuilder getParent();

    /**
     * Sets the path part of the URI.
     *
     * @param str the path part of the URI
     * @return a reference to the builder
     */
    public UriBuilder setPath(final String str) {
        final String[] parts;
        if (str.startsWith("/")) {
            parts = new String[]{str};
        } else {
            final String base = getPath().toString();
            parts = new String[]{base, base.endsWith("/") ? "" : "/", str};
        }

        return setPath(new GStringImpl(EMPTY, parts));
    }

    public URI forCookie(final HttpCookie cookie) throws URISyntaxException {
// Path: http-builder-ng-core/src/main/java/groovyx/net/http/Traverser.java
// public static <V> Predicate<V> notValue(final V v) { return (toTest) -> !v.equals(toTest); }
// 
// Path: http-builder-ng-core/src/main/java/groovyx/net/http/Traverser.java
// public static <T,V> V traverse(final T target, final Function<T,T> next,
//                                final Function<T,V> getValue, final Predicate<V> testValue) {
//     final V v = getValue.apply(target);
//     if(testValue.test(v)) {
//         return v;
//     }
// 
//     final T nextTarget = next.apply(target);
//     if(nextTarget != null) {
//         return traverse(nextTarget, next, getValue, testValue);
//     }
// 
//     return null;
// }


// Path: http-builder-ng-core/src/main/java/groovyx/net/http/UriBuilder.java
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.singletonList;
import groovy.lang.GString;
import org.codehaus.groovy.runtime.GStringImpl;
import java.io.IOException;
import java.net.HttpCookie;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import static groovyx.net.http.Traverser.notValue;
import static groovyx.net.http.Traverser.traverse;
import static java.lang.String.format;

/**
 * Copyright (C) 2017 HttpBuilder-NG Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package groovyx.net.http;




/**
 * Provides a simple means of creating a request URI and optionally overriding its parts.
 *
 * [source,groovy]
 * ----
 * def uri = UriBuilder.basic(UriBuilder.root())
 * .setFull('http://localhost:10101')
 * .setPath('/foo')
 * .toURI()
 * ----
 *
 * Generally, this class is not instantiated directly, but created by the {@link HttpConfig} instance and modified.
 */
public abstract class UriBuilder {

    public static final int DEFAULT_PORT = -1;

    /**
     * Sets the scheme part of the URI.
     *
     * @param val the value to use as the scheme part of the URI
     * @return a reference to this builder
     */
    public abstract UriBuilder setScheme(String val);

    /**
     * Retrieves the scheme part of the URI.
     *
     * @return the URI scheme
     */
    public abstract String getScheme();

    /**
     * Sets the port part of the URI.
     *
     * @param val the value to use as the port part of the URI
     * @return a reference to this builder
     */
    public abstract UriBuilder setPort(int val);

    /**
     * Retrieves the port part of the URI.
     *
     * @return the port part of the URI
     */
    public abstract int getPort();

    /**
     * Sets the host part of the URI.
     *
     * @param val the value to use as the host part of the URI
     * @return a reference to this builder
     */
    public abstract UriBuilder setHost(String val);

    /**
     * Retrieves the host part of the URI.
     *
     * @return the host part of the URI
     */
    public abstract String getHost();

    /**
     * Sets the path part of the URI.
     *
     * @param val the path part of the URI
     * @return a reference to the builder
     */
    public abstract UriBuilder setPath(GString val);

    /**
     * Retrieves the path part of the URI.
     *
     * @return the path part of the URI
     */
    public abstract GString getPath();

    /**
     * Sets the query string part of the `URI` from the provided map. The query string key-value pairs will be generated from the key-value pairs
     * of the map and are NOT URL-encoded. Nested maps or other data structures are not supported.
     *
     * @param val the map of query string parameters
     * @return a reference to the builder
     */
    public abstract UriBuilder setQuery(Map<String, ?> val);

    /**
     * Retrieves the `Map` of query string parameters for the `URI`.
     *
     * @return the `Map` of query string parameters for the `URI`.
     */
    public abstract Map<String, ?> getQuery();

    /**
     * Sets the fragment part of the `URI`.
     *
     * @param val the fragment part of the `URI`
     * @return a reference to the builder
     */
    public abstract UriBuilder setFragment(String val);

    /**
     * Retrieves the fragment part of the `URI`.
     *
     * @return the fragment part of the `URI`
     */
    public abstract String getFragment();

    /**
     * Sets the user info part of the `URI`.
     *
     * @param val the user info part of the `URI`
     * @return a reference to the builder
     */
    public abstract UriBuilder setUserInfo(String val);

    /**
     * Retrieves the user info part of the `URI`.
     *
     * @return the user info part of the `URI`
     */
    public abstract String getUserInfo();

    public abstract UriBuilder getParent();

    /**
     * Sets the path part of the URI.
     *
     * @param str the path part of the URI
     * @return a reference to the builder
     */
    public UriBuilder setPath(final String str) {
        final String[] parts;
        if (str.startsWith("/")) {
            parts = new String[]{str};
        } else {
            final String base = getPath().toString();
            parts = new String[]{base, base.endsWith("/") ? "" : "/", str};
        }

        return setPath(new GStringImpl(EMPTY, parts));
    }

    public URI forCookie(final HttpCookie cookie) throws URISyntaxException {
        final String scheme = traverse(this, UriBuilder::getParent, UriBuilder::getScheme, Traverser::notNull);