/*
 *  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 org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask
import org.apache.groovy.gradle.ConcurrentExecutionControlBuildService

plugins {
    id 'org.asciidoctor.jvm.convert'
}

configurations {
    asciidocElements {
        canBeConsumed = true
        canBeResolved = false
        attributes {
            attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION))
            attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, DocsType.USER_MANUAL))
        }
        outgoing {
            artifact tasks.named('asciidoctor')
        }
    }
    asciidocExtensions
}

tasks.withType(AbstractAsciidoctorTask).configureEach {
    outputs.cacheIf { true }
    usesService(ConcurrentExecutionControlBuildService.restrict(AbstractAsciidoctorTask, gradle))
}

asciidoctor {
    baseDirFollowsSourceFile()
    configurations 'asciidocExtensions'
    def (full, major, minor, patch, flavor) = (groovyVersion =~ /(\d+)\.(\d++)\.(\d+)(?:-(.+))?/)[0]
    logDocuments = true
    sourceDir = project.file('src/spec/doc')
    sources {
        include '*.adoc'
    }
    outputDir = layout.buildDirectory.dir("asciidoc/html5")
    resources {
        duplicatesStrategy = DuplicatesStrategy.WARN

        from("${rootProject.projectDir}/src/spec/doc/assets") {
            include 'css/style.css'
        }
        from layout.buildDirectory.dir("tmp/asciidoctor-assets")
        into 'assets'
    }
    forkOptions {
        jvmArgs("--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED")
    }
    attributes 'source-highlighter': 'prettify',
            groovyversion: groovyVersion,
            'groovy-major-version': major,
            'groovy-minor-version': minor,
            'groovy-patch-version': patch,
            'groovy-full-version': groovyVersion,
            'groovy-short-version': "${major}.${minor}",
            doctype: 'book',
            revnumber: groovyVersion,
            icons: 'font',
            toc2: '',
            linkcss: '',
            stylesheet: "assets/css/style.css",
            encoding: 'utf-8',
            toclevels: 10,
            numbered: '',
            sectanchors: true,
            sectlinks: true
}

asciidoctorj {
    jrubyVersion = versions.jruby
    version = versions.asciidoctorj
    def vers = versions.groovy
    resolutionStrategy {
        it.disableDependencyVerification()
    }
    def baseUrls = [
        jdk: 'https://docs.oracle.com/en/java/javase/11/docs/api/index.html',
        gjdk: "https://docs.groovy-lang.org/${vers}/html/groovy-jdk/index.html",
        gapi: "https://docs.groovy-lang.org/${vers}/html/gapi/index.html",
        gapid: "https://docs.groovy-lang.org/${vers}/html/gapi/",
    ]
    for (def entry : baseUrls.entrySet()) {
        def macroName = entry.key
        def baseUrl = entry.value
        docExtensions """
            inline_macro(name: '${macroName}') { parent, target, attributes ->
                def classNameAndAnchor = target.split('#')
                def className = classNameAndAnchor[0]
                def anchor = classNameAndAnchor.length > 1 ? classNameAndAnchor[1] : null
                def baseURL = '${baseUrl}'
                def base = className == 'index' ?
                        baseURL : baseURL + '?' + className.replace('.', '/') + '.html' + (anchor ? '#' + anchor : '')
                createPhraseNode(parent, 'anchor', attributes.text ?: target, attributes, [type: ':link', target: base])
            }
        """
    }
    modules {
        diagram.version(versions.asciidoctorDiagram)
        pdf.version(versions.asciidoctorPdf)
        groovyDsl.version(versions.asciidoctorGroovyDsl)
    }
}

// skip the asciidoctor task if there's no directory with asciidoc files
asciidoctor.onlyIf { project.file('src/spec/doc').exists() }

def asciidoctorAssets = tasks.register("asciidoctorAssets", Copy) {
    from project.fileTree('src/spec/doc/assets')
    into layout.buildDirectory.dir("tmp/asciidoctor-assets")
}
asciidoctor.dependsOn asciidoctorAssets

def adocSanityCheck = { file, text, errors ->
    Set localErrors = []
    text.eachLine(1) { line, i ->
        if (line =~ /tag:[a-zA-Z0-9]/) {
            localErrors << "line $i misses semicolon. Should be tag::\n $line"
        }
        if (line =~ /end:[a-zA-Z0-9]/) {
            localErrors << "line $i misses semicolon. Should be end::\n $line"
        }
        if (line =~ /(tag|end)::[^\[\]]$/) {
            localErrors << "line $i contains incorrect tag definition (misses []):\n $line"
        }
    }
    localErrors.collect(errors) { "    $file, $it" }
}

def htmlOutputSanityCheck = { file, text, errors ->
    Set localErrors = []
    text.eachLine(1) { line, i ->
        if (line =~ /^={1,5} /) {
            localErrors << "line $i starting with asciidoctor raw markup:\n$line"
        }
        if (line =~ /<code class=".+?"><\/code>/) {
            localErrors << "contains empty code block, probably incorrect import of a tag."
        }
        if (line =~ /(gapi|jdk|gjdk):(\S.+?)/) {
            localErrors << "line $i starting with asciidoctor raw markup:\n$line"
        }
    }
    localErrors.collect(errors) { "    $file, $it" }
}

asciidoctor {
    def errors = new LinkedHashSet<String>()
    doFirst {
        def specTestDir = file('src/spec/test')
        if (specTestDir.exists()) {
            specTestDir.eachFileRecurse { file ->
                if (file.isFile()) {
                    adocSanityCheck(file, file.getText('utf-8'), errors)
                }
            }
        }
        if (errors) {
            throw new GradleException("Incorrect Asciidoctor input:\n${errors.join('\n')}")
        }
    }

    doLast {
        def scripts = '''<link rel="stylesheet" href="assets/css/view-example.css">
<script src='assets/js/jquery-min-2.1.1.js'></script>
<script src='assets/js/view-example.js'></script>'''

        // gapi macro expansion
        outputDir.eachFileMatch(~'.*html') { File file ->
            def text = file.getText('UTF-8')
            text = text.replaceAll('</head>', "$scripts</head>")
            htmlOutputSanityCheck(file, text, errors)
            file.write(text, 'UTF-8')
        }
        if (errors) {
            throw new GradleException("Incorrect Asciidoctor output:\n${errors.join('\n')}")
        }
    }
}
