package com.byteowls.jopencage;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map.Entry;

import org.apache.http.HttpEntity;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.AbstractResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.byteowls.jopencage.model.JOpenCageForwardRequest;
import com.byteowls.jopencage.model.JOpenCageRequest;
import com.byteowls.jopencage.model.JOpenCageResponse;
import com.byteowls.jopencage.model.JOpenCageReverseRequest;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;


public class JOpenCageGeocoder {

  private final static Logger LOGGER = LoggerFactory.getLogger(JOpenCageGeocoder.class);

  private final static String OPENCAGE_HOST = "api.opencagedata.com";
  private final static String OPENCAGE_PATH = "/geocode/v1/";

  /**
   * Not used right now because problems with httpclient
   */
  private boolean httpsEnabled = false;
  private String host = OPENCAGE_HOST;
  private String path = OPENCAGE_PATH;
  private String format = "json";
  private String apiKey;

  public JOpenCageGeocoder(String apiKey) {
    this.apiKey = apiKey;
  }

  public JOpenCageResponse forward(JOpenCageForwardRequest request) {
    return sendRequest(request);
  }

  public JOpenCageResponse reverse(JOpenCageReverseRequest request) {
    return sendRequest(request);
  }

  public URI buildUri(JOpenCageRequest jOpenCageRequest) throws URISyntaxException {
    URIBuilder uriBuilder = new URIBuilder();
    if (httpsEnabled) {
      uriBuilder.setScheme("https");
    } else {
      uriBuilder.setScheme("http");
    }
    uriBuilder.setHost(host)
    .setPath(path + format);

    for (Entry<String, String> e : jOpenCageRequest.getParameter().entrySet()) {
      if (e.getValue() != null) {
        uriBuilder.setParameter(e.getKey(), e.getValue());
      }
    }
    uriBuilder.setParameter("key", apiKey);
    return uriBuilder.build();
  }

  private JOpenCageResponse sendRequest(JOpenCageRequest jOpenCageRequest) {
    URI url = null;
    try {
      url = buildUri(jOpenCageRequest);
      LOGGER.info("JOpencage request url '{}'", url);
    } catch (URISyntaxException e1) {
      LOGGER.error("", e1);
    }

    if (url != null) {
      try (CloseableHttpClient httpclient = HttpClientBuilder.create().build()) {
        HttpGet getRequest = new HttpGet(url);

        ResponseHandler<JOpenCageResponse> rh = new AbstractResponseHandler<JOpenCageResponse>() {
          @Override
          public JOpenCageResponse handleEntity(HttpEntity entity) throws IOException {
            ObjectMapper mapper = new ObjectMapper();
            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            return mapper.readValue(entity.getContent(), JOpenCageResponse.class);
          }
        };

        return httpclient.execute(getRequest, rh);
      } catch (HttpResponseException e) {
        switch (e.getStatusCode()) {
          case 400:
            LOGGER.error("Invalid request (bad request; a required parameter is missing)!");
            break;
          case 402:
            LOGGER.error("Valid request but quota exceeded (payment required)!");
            break;
          case 403:
            LOGGER.error("Invalid or missing api key!");
            break;
          case 404:
            LOGGER.error("Invalid API endpoint!");
            break;
          case 408:
            LOGGER.error("Timeout; you can try again!");
            break;
          case 410:
            LOGGER.error("Request too long!");
            break;
        }
      } catch (IOException e) {
        LOGGER.error("", e);
      }
    } else {
      LOGGER.error("No jopencage request url build!");
    }
    return null;
  }

  public boolean isHttpsEnabled() {
    return httpsEnabled;
  }

  public void setHttpsEnabled(boolean httpsEnabled) {
    this.httpsEnabled = httpsEnabled;
  }

  public String getHost() {
    return host;
  }

  public void setHost(String host) {
    this.host = host;
  }

  public String getPath() {
    return path;
  }

  public void setPath(String path) {
    this.path = path;
  }

  public String getApiKey() {
    return apiKey;
  }

}