/*
 * Copyright (C) 2015 Index Data
 *
 * 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 org.folio.okapi.common;

import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpClientRequest;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.ext.web.RoutingContext;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import static org.folio.okapi.common.ErrorType.*;

/**
 * Okapi client.
 * Makes requests to other Okapi modules, or Okapi itself. Handles all the
 * things we need with the headers etc. Bote that the client keeps a list
 * of necessary headers (which it can get from the RoutingContext, or
 * separately), so it is bound to one request, or at least one tenant.
 * Your module should not just keep one client around for everything it
 * does.
 * @author heikki
 */
public class OkapiClient {
  private final Logger logger = LoggerFactory.getLogger("okapi");

  private RoutingContext ctx;
  private String okapiUrl;
  private HttpClient httpClient;
  private Vertx vertx;
  private Map<String,String> headers;
  // TODO X-Okapi headers
  // TODO Response headers: do we need a trace or something?
  // TODO Return type: Need a more complex container class with room for
  //   response headers, the whole response, and so on.
  // TODO Use this in the discovery-deployment communications

  /**
   * Constructor from a vert.x ctx.
   * That ctx contains all the headers we need.
   * @param ctx
   */
  public OkapiClient(RoutingContext ctx) {
    init(ctx.vertx());
    this.ctx = ctx;
    this.okapiUrl = ctx.request().getHeader("X-Okapi-Url");
    for (String hdr : ctx.request().headers().names()) {
      if (hdr.startsWith("X-Okapi")) {
        String hv = ctx.request().getHeader(hdr);
        headers.put(hdr,hv);
      }
    }
  }

  /** Explicit constructor.
   *
   * @param okapiUrl
   * @param vertx
   * @param headers may be null
   */
  public OkapiClient(String okapiUrl, Vertx vertx, Map<String,String> headers) {
    init(vertx);
    this.ctx = null;
    this.okapiUrl = okapiUrl;
    if (headers != null )
      this.headers.putAll(headers);
  }

  private void init(Vertx vertx) {
    this.vertx = vertx;
    this.httpClient = vertx.createHttpClient();
    this.headers = new HashMap<>();
  }

  /**
   * Make a request to Okapi.
   *
   * @param method GET or POST or such
   * @param path like "/foomodule/something"
   * @param data for the request. Most likely a Json string
   * @param fut callback when done. Most likely a Json string if all went
   * well, or a plain text string in case of errors.
   */
  public void request( HttpMethod method, String path, String data,
        Handler<ExtendedAsyncResult<String>> fut) {
    String url = this.okapiUrl + path;
    logger.warn("Ok " + method.toString() + " request to " + url);
    HttpClientRequest req = httpClient.requestAbs(method, url, postres -> {
      final Buffer buf = Buffer.buffer();
      postres.handler(b -> {
        logger.warn("Ok Buffering response " + b.toString());
        buf.appendBuffer(b);
      });
      postres.endHandler(e -> {
        logger.warn("Ok endhandler " + postres.statusCode());
        String reply = buf.toString();
        if (postres.statusCode() >= 201 && postres.statusCode() <= 299 ) {
          fut.handle(new Success<>(reply));
        } else if (postres.statusCode() == 404) {
          fut.handle(new Failure<>(NOT_FOUND, reply));
        } else if (postres.statusCode() == 400) {
          fut.handle(new Failure<>(USER, reply));
        } else {
          fut.handle(new Failure<>(INTERNAL, reply));
        }
      });
    });
    req.exceptionHandler(x -> {
      fut.handle(new Failure<>(INTERNAL, x.getMessage()));
    });
    logger.warn("Ok: adding headers ");
    req.headers().addAll(headers);
    req.end(data);
  }

  public void post(String path, String data,
        Handler<ExtendedAsyncResult<String>> fut) {
    request(HttpMethod.POST, path, data, fut);
  }

  public void get(String path,
        Handler<ExtendedAsyncResult<String>> fut) {
    request(HttpMethod.GET, path, "", fut);
  }

  public void delete(String path,
        Handler<ExtendedAsyncResult<String>> fut) {
    request(HttpMethod.DELETE, path, "", fut);
  }


}

@@ -1255,17 +1255,17 @@
 rs etc. 
-B
+N
 ote that
@@ -3157,26 +3157,27 @@
 kely a J
-son
+SON
  string
+.
 %0A   * @p
@@ -3224,11 +3224,11 @@
  a J
-son
+SON
  str

Fix typos