/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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.
 */

import com.vladsch.flexmark.ast.Heading;
import com.vladsch.flexmark.ext.abbreviation.AbbreviationExtension;
import com.vladsch.flexmark.ext.attributes.AttributesExtension;
import com.vladsch.flexmark.ext.autolink.AutolinkExtension;
import com.vladsch.flexmark.ext.tables.TablesExtension;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.parser.ParserEmulationProfile;
import com.vladsch.flexmark.util.ast.Document;
import com.vladsch.flexmark.util.data.MutableDataSet;
import com.vladsch.flexmark.util.sequence.Escaping;
import groovy.text.SimpleTemplateEngine
import org.apache.lucene.gradle.plugins.globals.LuceneBuildGlobalsExtension

configure(project(':lucene:documentation')) {
  tasks.register("markdownToHtml", Copy, {
    dependsOn "copyDocumentationAssets"

    from(project.parent.projectDir) {
      include 'MIGRATE.md'
      include 'JRE_VERSION_MIGRATION.md'
      include 'SYSTEM_REQUIREMENTS.md'
    }

    filteringCharset = 'UTF-8'
    includeEmptyDirs = false

    rename(/\.md$/, '.html')
    filter(MarkdownFilter)

    into project.ext.docroot
  })

  tasks.register("createDocumentationIndex", MarkdownTemplateTask, {
    dependsOn "markdownToHtml"

    outputFile = file("${project.ext.docroot}/index.html")
    templateFile = file("${project.ext.markdownSrc}/index.template.md")

    def defaultCodecFile = project(':lucene:core').file('src/java/org/apache/lucene/codecs/Codec.java')
    inputs.file(defaultCodecFile)

    // list all properties used by the template here to allow uptodate checks to be correct:
    inputs.property('version', project.version)

    def buildGlobals = project.extensions.getByType(LuceneBuildGlobalsExtension)

    binding.put("project", [
      "version": project.version,
      "majorVersion": buildGlobals.majorVersion
    ])

    binding.put('defaultCodecPackage', providers.provider {
      // static Codec defaultCodec   =   LOADER    .   lookup    (   "LuceneXXX"  )   ;
      def regex = ~/\bdefaultCodec\s*=\s*LOADER\s*\.\s*lookup\s*\(\s*"([^"]+)"\s*\)\s*;/
      def matcher = regex.matcher(defaultCodecFile.getText('UTF-8'))
      if (!matcher.find()) {
        throw new GradleException("Cannot determine default codec from file ${defaultCodecFile}")
      }
      return matcher.group(1).toLowerCase(Locale.ROOT)
    })

    withProjectList()
  })
}

// filter that can be used with the "copy" task of Gradle that transforms Markdown files
// from source location to HTML (adding HTML header, styling,...)
class MarkdownFilter extends FilterReader {

  public MarkdownFilter(Reader reader) throws IOException {
    // this is not really a filter: it reads whole file in ctor,
    // converts it and provides result downstream as a StringReader
    super(new StringReader(convert(reader.text)));
  }

  public static String convert(String markdownSource) {
    // first replace LUCENE and SOLR issue numbers with a markdown link
    markdownSource = markdownSource.replaceAll(/(?s)\b(LUCENE|SOLR)\-\d+\b/,
        '[$0](https://issues.apache.org/jira/browse/$0)');
    markdownSource = markdownSource.replaceAll(/(?s)\b(GITHUB#|GH-)(\d+)\b/,
        '[$0](https://github.com/apache/lucene/issues/$2)');

    // convert the markdown
    MutableDataSet options = new MutableDataSet();
    options.setFrom(ParserEmulationProfile.MARKDOWN);
    options.set(Parser.EXTENSIONS, [
      AbbreviationExtension.create(),
      AutolinkExtension.create(),
      AttributesExtension.create(),
      TablesExtension.create(),
    ]);
    options.set(HtmlRenderer.RENDER_HEADER_ID, true);
    options.set(HtmlRenderer.MAX_TRAILING_BLANK_LINES, 0);
    Document parsed = Parser.builder(options).build().parse(markdownSource);

    StringBuilder html = new StringBuilder('<html>\n<head>\n');
    CharSequence title = parsed.getFirstChildAny(Heading.class)?.getText();
    if (title != null) {
      html.append('<title>').append(Escaping.escapeHtml(title, false)).append('</title>\n');
    }
    html.append('<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\n')
        .append('</head>\n<body>\n');
    HtmlRenderer.builder(options).build().render(parsed, html);
    html.append('</body>\n</html>\n');
    return html;
  }
}

// Applies a binding of variables using a template and
// produces Markdown, which is converted to HTML
class MarkdownTemplateTask extends DefaultTask {

  @Internal
  Project productProject = project.parent

  @InputFile
  File templateFile

  @OutputFile
  File outputFile

  @Input
  @Optional
  final MapProperty<String,Object> binding = project.objects.mapProperty(String, Object)

  /** adds a property "projectList" containing all subprojects with javadocs as markdown bullet list */
  void withProjectList() {
    binding.put('projectList', project.providers.provider{
      def projects = productProject.subprojects.findAll{ it.tasks.findByName('renderSiteJavadoc')?.enabled }
      .sort(false, Comparator.comparing{
        (it.name != 'core') as Boolean
      }
      .thenComparing(Comparator.comparing{ (it.name != 'solrj') as Boolean })
      .thenComparing(Comparator.comparing{ (it.name == 'test-framework') as Boolean })
      .thenComparing(Comparator.comparing{ it.path }));
      return projects.collect{ project ->
        def text = "**[${project.relativeDocPath.replace('/','-')}](${project.relativeDocPath}/index.html):** ${project.description}"
        if (project.name == 'core') {
          text = text.concat(' {style="font-size:larger; margin-bottom:.5em"}')
        }
        return '* ' + text;
      }.join('\n')
    })
  }

  @TaskAction
  void transform() {
    def engine = new SimpleTemplateEngine();
    def resolvedBinding = new HashMap(binding.get())
    String markdown = templateFile.withReader('UTF-8') {
      engine.createTemplate(it).make(resolvedBinding).toString();
    }
    outputFile.getParentFile().mkdirs();
    outputFile.write(MarkdownFilter.convert(markdown), 'UTF-8');
  }
}
