// Copyright (C) 2014 The Android Open Source 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 com.googlesource.gerrit.plugins.xdocs.formatter;

import static com.googlesource.gerrit.plugins.xdocs.XDocGlobalConfig.KEY_ALLOW_HTML;
import static com.googlesource.gerrit.plugins.xdocs.XDocGlobalConfig.KEY_INCLUDE_TOC;
import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.common.io.ByteStreams;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.extensions.annotations.PluginData;
import com.google.inject.Inject;
import com.google.inject.Singleton;

import com.googlesource.gerrit.plugins.xdocs.ConfigSection;

import org.asciidoctor.Asciidoctor;
import org.asciidoctor.Attributes;
import org.asciidoctor.AttributesBuilder;
import org.asciidoctor.Options;
import org.asciidoctor.OptionsBuilder;
import org.asciidoctor.SafeMode;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.Properties;

@Singleton
public class AsciidoctorFormatter implements StringFormatter {
  public static final String NAME = "ASCIIDOCTOR";

  private static final String BACKEND = "html5";
  private static final String DOCTYPE = "article";
  private static final String ERUBY = "erb";

  private final File baseDir;
  private final Properties attributes;
  private final FormatterUtil util;
  private final Formatters formatters;

  @Inject
  public AsciidoctorFormatter(@PluginData File baseDir,
      FormatterUtil formatterUtil, Formatters formatters) throws IOException {
    this.baseDir = baseDir;
    this.attributes = readAttributes();
    this.util = formatterUtil;
    this.formatters = formatters;
  }

  @Override
  public String format(String projectName, String path, String revision,
      String abbrRev, ConfigSection globalCfg, String raw) throws IOException {
    if (!globalCfg.getBoolean(KEY_ALLOW_HTML, false)) {
      raw = suppressHtml(raw);
    }

    ConfigSection projectCfg =
        formatters.getFormatterConfig(NAME, projectName);
    // asciidoctor ignores all attributes if no output file is specified,
    // this is why we must specify an output file and then read its content
    File tmpDir = new File(baseDir, "tmp");
    tmpDir.mkdirs();
    File tmpFile = File.createTempFile("asciidoctor-", null, tmpDir);
    try {
      Asciidoctor.Factory.create(AsciidoctorFormatter.class.getClassLoader())
          .render(raw, createOptions(projectCfg, abbrRev, tmpFile));
      try (FileInputStream input = new FileInputStream(tmpFile)) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteStreams.copy(input, out);
        String html = out.toString(UTF_8.name());
        return util.applyCss(html, NAME, projectName);
      }
    } finally {
      if (!tmpFile.delete()) {
        tmpFile.deleteOnExit();
      }
    }
  }

  private String suppressHtml(String raw) throws IOException {
    try (BufferedReader br = new BufferedReader(new StringReader(raw))) {
      StringBuilder sb = new StringBuilder();
      boolean embeddedHtml = false;
      String line;
      while ((line = br.readLine()) != null) {
        if (line.startsWith("++++")) {
          embeddedHtml = !embeddedHtml;
        }
        if (!embeddedHtml && !line.startsWith("++++")) {
          sb.append(line);
          sb.append("\n");
        }
      }
      return sb.toString();
    }
  }

  private Options createOptions(ConfigSection cfg, String revision, File out) {
    return OptionsBuilder.options()
        .backend(BACKEND)
        .docType(DOCTYPE)
        .eruby(ERUBY)
        .safe(SafeMode.SECURE)
        .attributes(getAttributes(cfg, revision))
        .mkDirs(true)
        .toFile(out)
        .get();
  }

  private Attributes getAttributes(ConfigSection cfg, String revision) {
    AttributesBuilder ab = AttributesBuilder.attributes()
        .tableOfContents(cfg.getBoolean(KEY_INCLUDE_TOC, true))
        .sourceHighlighter("prettify");
    for (String name : attributes.stringPropertyNames()) {
      ab.attribute(name, attributes.getProperty(name));
    }
    ab.attribute("last-update-label!");
    ab.attribute("revnumber", revision);
    return ab.get();
  }

  private static Properties readAttributes() throws IOException {
    Properties attributes = new Properties();
    try (InputStream in = AsciidoctorFormatter.class
        .getResourceAsStream("asciidoctor.properties")) {
      attributes.load(in);
    }
    return attributes;
  }
}

@@ -835,206 +835,8 @@
 OC;%0A
-import static java.nio.charset.StandardCharsets.UTF_8;%0A%0Aimport com.google.common.io.ByteStreams;%0Aimport com.google.gerrit.common.TimeUtil;%0Aimport com.google.gerrit.extensions.annotations.PluginData;
 %0Aimp
@@ -1003,43 +1003,8 @@
 or;%0A
-import org.asciidoctor.Attributes;%0A
 impo
@@ -1045,40 +1045,8 @@
 er;%0A
-import org.asciidoctor.Options;%0A
 impo
@@ -1149,99 +1149,8 @@
 er;%0A
-import java.io.ByteArrayOutputStream;%0Aimport java.io.File;%0Aimport java.io.FileInputStream;%0A
 impo
@@ -1536,38 +1536,8 @@
 %22;%0A%0A
-  private final File baseDir;%0A
   pr
@@ -1691,33 +1691,8 @@
 ter(
-@PluginData File baseDir,
 %0A   
@@ -1722,16 +1722,22 @@
 terUtil,
+%0A     
  Formatt
@@ -1777,36 +1777,8 @@
 n %7B%0A
-    this.baseDir = baseDir;%0A
     
@@ -2239,304 +2239,21 @@
     
-// asciidoctor ignores all attributes if no output file is specified,%0A    // this is why we must specify an output file and then read its content%0A    File tmpDir = new File(baseDir, %22tmp%22);%0A    tmpDir.mkdirs();%0A    File tmpFile = File.createTempFile(%22asciidoctor-%22, null, tmpDir);%0A    try %7B%0A     
+String html =
  Asc
@@ -2333,17 +2333,16 @@
     
-  .rend
+.conv
 er
+t
 (raw
@@ -2380,396 +2380,62 @@
 rRev
-, tmpFile));%0A      try (FileInputStream input = new FileInputStream(tmpFile)) %7B%0A        ByteArrayOutputStream out = new ByteArrayOutputStream();%0A        ByteStreams.copy(input, out);%0A        String html = out.toString(UTF_8.name());%0A        return util.applyCss(html, NAME, projectName);%0A      %7D%0A    %7D finally %7B%0A      if (!tmpFile.delete()) %7B%0A        tmpFile.deleteOnExit();%0A      %7D%0A    %7D
+));%0A    return util.applyCss(html, NAME, projectName);
 %0A  %7D
@@ -2995,16 +2995,23 @@
  Options
+Builder
  createO
@@ -3055,18 +3055,8 @@
 sion
-, File out
 ) %7B%0A
@@ -3271,44 +3271,8 @@
 rue)
-%0A        .toFile(out)%0A        .get()
 ;%0A  
@@ -3294,16 +3294,23 @@
 tributes
+Builder
  getAttr
@@ -3735,14 +3735,8 @@
 n ab
-.get()
 ;%0A  

Simplify code by using the Asciidoctor >= 1.5 API