/*
 * Copyright 2019 Red Hat, Inc.
 *
 * Red Hat licenses this file to you 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 io.vertx.ext.web.handler.graphql.impl;

import io.vertx.core.MultiMap;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.http.impl.MimeMapping;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.MIMEHeader;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.graphql.GraphiQLHandler;
import io.vertx.ext.web.handler.graphql.GraphiQLHandlerOptions;
import io.vertx.ext.web.impl.Utils;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Map;
import java.util.Objects;
import java.util.Scanner;
import java.util.function.Function;

/**
 * @author Thomas Segismont
 */
public class GraphiQLHandlerImpl implements GraphiQLHandler {

  private static final Logger log = LoggerFactory.getLogger(GraphiQLHandlerImpl.class);

  private static final Function<RoutingContext, MultiMap> DEFAULT_GRAPHIQL_REQUEST_HEADERS_FACTORY = rc -> null;

  private final GraphiQLHandlerOptions options;

  private Function<RoutingContext, MultiMap> graphiQLRequestHeadersFactory = DEFAULT_GRAPHIQL_REQUEST_HEADERS_FACTORY;

  public GraphiQLHandlerImpl(GraphiQLHandlerOptions options) {
    Objects.requireNonNull(options, "options");
    this.options = options;
  }

  @Override
  public GraphiQLHandler graphiQLRequestHeaders(Function<RoutingContext, MultiMap> factory) {
    graphiQLRequestHeadersFactory = factory != null ? factory : DEFAULT_GRAPHIQL_REQUEST_HEADERS_FACTORY;
    return this;
  }

  @Override
  public void handle(RoutingContext rc) {
    if (!options.isEnabled()) {
      rc.next();
      return;
    }
    HttpServerResponse response = rc.response();
    String filename = Utils.pathOffset(rc.normalisedPath(), rc);
    if (filename.isEmpty()) {
      if (rc.parsedHeaders().accept().stream().map(MIMEHeader::subComponent).anyMatch(sub -> "html".equalsIgnoreCase(sub))) {
        rc.response().setStatusCode(301).putHeader(HttpHeaders.LOCATION, rc.currentRoute().getPath()).end();
      } else {
        rc.next();
      }
      return;
    }
    if (filename.equals("/")) {
      filename = "/index.html";
    }
    String resource = loadResource(filename);
    if (resource == null) {
      rc.next();
      return;
    }
    if (filename.equals("/index.html")) {
      resource = resource.replace("__VERTX_GRAPHIQL_CONFIG__", replacement(rc));
      response.putHeader(HttpHeaders.CACHE_CONTROL, "no-cache");
    } else {
      response.putHeader(HttpHeaders.CACHE_CONTROL, "max-age=31536000");
    }
    String contentType = MimeMapping.getMimeTypeForFilename(filename);
    if (contentType != null) {
      if (contentType.startsWith("text")) {
        response.putHeader(HttpHeaders.CONTENT_TYPE, contentType + ";charset=utf8");
      } else {
        response.putHeader(HttpHeaders.CONTENT_TYPE, contentType);
      }
    }
    response.end(resource);
  }

  private String loadResource(String relativePath) {
    ClassLoader classLoader = getClass().getClassLoader();
    URL resource = classLoader.getResource("io/vertx/ext/web/handler/graphiql" + relativePath);
    if (resource == null) {
      return null;
    }
    try (InputStream stream = resource.openStream()) {
      return new Scanner(stream, "UTF-8").useDelimiter("\\A").next();
    } catch (IOException e) {
      if (log.isTraceEnabled()) {
        log.trace("Unable to load resource: " + relativePath, e);
      }
      return null;
    }
  }

  private String replacement(RoutingContext rc) {
    JsonObject json = new JsonObject();
    if (options.getGraphQLUri() != null) {
      json.put("graphQLUri", options.getGraphQLUri());
    }
    MultiMap headers = MultiMap.caseInsensitiveMultiMap();
    Map<String, String> fixedHeaders = options.getHeaders();
    if (fixedHeaders != null) {
      fixedHeaders.forEach(headers::add);
    }
    Function<RoutingContext, MultiMap> rh;
    synchronized (this) {
      rh = this.graphiQLRequestHeadersFactory;
    }
    MultiMap dynamicHeaders = rh.apply(rc);
    if (dynamicHeaders != null) {
      headers.addAll(dynamicHeaders);
    }
    if (!headers.isEmpty()) {
      JsonObject headersJson = new JsonObject();
      headers.forEach(header -> headersJson.put(header.getKey(), header.getValue()));
      json.put("headers", headersJson);
    }
    if (options.getQuery() != null) {
      json.put("query", options.getQuery());
    }
    if (options.getVariables() != null) {
      json.put("parameters", options.getVariables());
    }
    return json.encode();
  }
}

@@ -690,24 +690,62 @@
 e.MultiMap;%0A
+import io.vertx.core.file.FileSystem;%0A
 import io.ve
@@ -994,44 +994,8 @@
 ct;%0A
-import io.vertx.ext.web.MIMEHeader;%0A
 impo
@@ -1192,85 +1192,8 @@
 s;%0A%0A
-import java.io.IOException;%0Aimport java.io.InputStream;%0Aimport java.net.URL;%0A
 impo
@@ -1240,34 +1240,8 @@
 ts;%0A
-import java.util.Scanner;%0A
 impo
@@ -1273,16 +1273,72 @@
 ction;%0A%0A
+import static java.nio.charset.StandardCharsets.UTF_8;%0A%0A
 /**%0A * @
@@ -2449,136 +2449,8 @@
 ) %7B%0A
-      if (rc.parsedHeaders().accept().stream().map(MIMEHeader::subComponent).anyMatch(sub -%3E %22html%22.equalsIgnoreCase(sub))) %7B%0A  
     
@@ -2556,50 +2556,8 @@
 ();%0A
-      %7D else %7B%0A        rc.next();%0A      %7D%0A
     
@@ -2650,47 +2650,153 @@
     
-String resource = loadResource(filename
+FileSystem fs = rc.vertx().fileSystem();%0A    String resource = fs.readFileBlocking(%22io/vertx/ext/web/handler/graphiql%22 + filename).toString(UTF_8
 );%0A 
@@ -3503,562 +3503,8 @@
  %7D%0A%0A
-  private String loadResource(String relativePath) %7B%0A    ClassLoader classLoader = getClass().getClassLoader();%0A    URL resource = classLoader.getResource(%22io/vertx/ext/web/handler/graphiql%22 + relativePath);%0A    if (resource == null) %7B%0A      return null;%0A    %7D%0A    try (InputStream stream = resource.openStream()) %7B%0A      return new Scanner(stream, %22UTF-8%22).useDelimiter(%22%5C%5CA%22).next();%0A    %7D catch (IOException e) %7B%0A      if (log.isTraceEnabled()) %7B%0A        log.trace(%22Unable to load resource: %22 + relativePath, e);%0A      %7D%0A      return null;%0A    %7D%0A  %7D%0A%0A
   pr

Simplify GraphiQLHandlerImpl using Vert.x FS instead of manual CL resource loading