package org.urllib.internal;

import com.google.auto.value.AutoValue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.urllib.Query;
import org.urllib.Query.KeyValue;

public class Queries {

  private static final Query empty = of(Collections.<KeyValue>emptyList());

  public static Query create(Map<String, String> paramMap) {
    List<KeyValue> params = new ArrayList<>(paramMap.size());
    for (Entry<String, String> param : paramMap.entrySet()) {
      params.add(create(param.getKey(), param.getValue()));
    }
    return of(params);
  }

  public static Query empty() {
    return empty;
  }

  public static Query of(List<KeyValue> params) {
    return ImmutableQuery.create(params);
  }

  public static Query parse(String query) {
    if (query.isEmpty()) return empty();
    query = query.replace('+', ' ');
    List<KeyValue> params = new LinkedList<>();
    int p = 0;
    int equal = -1;
    for (int i = 0; i <= query.length(); i++) {
      if (i == query.length() || query.charAt(i) == '&') {
        if (i == p) {
        } else if (equal == -1) {
          String key = PercentDecoder.decodeAll(query.substring(p, i));
          params.add(create(key, ""));
        } else {
          String key = (p == equal)
              ? ""
              : PercentDecoder.decodeAll(query.substring(p, equal));
          String value = (i == equal + 1)
              ? ""
              : PercentDecoder.decodeAll(query.substring(equal + 1, i));
          params.add(create(key, value));
        }
        equal = -1;
        p = i + 1;
      } else if (query.charAt(i) == '=' && equal == -1) {
        equal = i;
      }
    }
    return of(params);
  }

  public static KeyValue create(String key, String value) {
    return new AutoValue_Queries_ImmutableKeyValue(key, Strings.nullToEmpty(value));
  }

  @AutoValue
  abstract static class ImmutableKeyValue implements KeyValue {
  }

  @AutoValue
  abstract static class ImmutableQuery implements Query {

    static Query create(List<KeyValue> params) {
      return new AutoValue_Queries_ImmutableQuery(
          Collections.unmodifiableList(params), toMap(params), encode(params));
    }

    private static Map<String, String> toMap(List<KeyValue> params) {
      Map<String, String> map = new HashMap<>();
      for (KeyValue keyValue : params) {
        if (!map.containsKey(keyValue.key())) {
          map.put(keyValue.key(), keyValue.value());
        }
      }
      return Collections.unmodifiableMap(map);
    }

    private static String encode(List<KeyValue> params) {
      StringBuilder sb = new StringBuilder();
      for (Iterator<KeyValue> iterator = params.iterator(); iterator.hasNext(); ) {
        KeyValue param = iterator.next();
        sb.append(PercentEncoder.encodeQueryComponentNoPlusForSpace(param.key()));
        if (param.value() != null && !param.value().isEmpty()) {
          sb.append('=')
              .append(PercentEncoder.encodeQueryComponentNoPlusForSpace(param.value()));
        }
        if (iterator.hasNext()) {
          sb.append('&');
        }
      }
      return sb.toString();
    }

    @Override public boolean isEmpty() {
      return params().isEmpty();
    }
  }
}