/*
 * Copyright (C) 2017 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 writer.files

import parser.LOG_NAME
import parser.config
import parser.files.AbstractFileParser
import parser.files.InterfaceFileParser
import writer.getDescSummaryText
import writer.getOutPath
import java.io.File
import java.nio.file.Path
import java.nio.file.Paths

data class EntryData(val fullName: String, //package.BaseName
                     val baseName: String,
                     val packageName: String,
                     val packageVersion: Float,
                     val summary: String,
                     val relPath: Path)

class IndexFileWriter : AbstractFileWriter() {

    override val baseName = "index"
    override val templateResource = "/resources/template/${this.baseName}.html"
    override val path: Path by lazy { Paths.get("${config.outDir}${File.separator}${this.baseName}.html") }

    private val entries = mutableListOf<EntryData>()

    fun addEntry(parser: AbstractFileParser) {
        val summaryStr = when (parser) {
            is InterfaceFileParser -> getDescSummaryText(parser.description)
            else -> ""
        }
        entries.add(EntryData(
                fullName = "${parser.packageName}.${parser.name}",
                baseName = parser.name,
                packageName = parser.packageName,
                packageVersion = parser.packageVersion,
                summary = summaryStr,
                relPath = config.outDir.relativize(getOutPath(parser, config.outDir))
        ))
    }

    override fun printInfo() {
        super.printInfo()
        println( "IndexFileWriter:")
        println("  entries: ${this.entries.size}")
    }

    /*
     * HTML index file
     */

    override fun replaceVars() {
        replaceVar("title", "Index")

        replaceVar("entries") {
            val sb = StringBuilder()
            if (entries.isNotEmpty()) {
                entries.sortWith(EntryNameComparator())
                sb.append(buildEntryTable())
            }
            sb.toString()
        }
    }

    private fun buildEntryTable(): String {
        return """
<table>
  <tr>
    <th>Entry</th>
    <th>Version</th>
    <th>Summary</th>
  </tr>
${entries.map { buildClassEntry(it) }.joinToString("\n")}
</table>
""".trim()
    }

    private fun buildClassEntry(entry: EntryData): String {
        return """
<tr>
  <td>
<a href="${entry.relPath}">${entry.fullName}</a>
  </td>
  <td>
${entry.packageVersion}
  </td>
  <td>
${entry.summary}
  </td>
</tr>
""".trim()
    }

    private class EntryNameComparator : Comparator<EntryData> {
        override fun compare(entry1: EntryData, entry2: EntryData): Int {
            return if (entry1.fullName != entry2.fullName) {
                //sort on name first, alphabetic
                when {
                    entry1.fullName < entry2.fullName -> -1
                    entry1.fullName > entry2.fullName -> 1
                    else -> 0
                }
            } else {
                //if same name, reverse sort on pkg version (highest first)
                when {
                    entry1.packageVersion < entry2.packageVersion -> 1
                    entry1.packageVersion > entry2.packageVersion -> -1
                    else -> 0
                }
            }
        }
    }

    /*
     * YAML toc file
     */

    private val tocFileName = "_book.yaml"
    private val tocFileRelPath = "/reference/hidl"
    private val tocOutPath: Path by lazy { Paths.get("${config.outDir}${File.separator}${this.tocFileName}") }

    //write toc yaml file after html index
    override fun onWrite() {
        super.onWrite()
        if (!config.lintMode) {
            val fp = tocOutPath.toFile()
            fp.bufferedWriter().use {
                it.write(buildTocHeader())
                it.write(buildTocEntries(collectPackages()))
            }
            if (!fp.isFile) throw FileSystemException(fp, reason = "Error writing file")
            println("$LOG_NAME Wrote toc: $tocOutPath")
        }
    }

    private fun buildTocHeader(): String {
        return """
# Generated by hidl-doc
toc:
- title: Index
  path: $tocFileRelPath
""".trimStart()
    }

    private fun buildTocEntries(pkgEntries: Map<String, List<EntryData>>): String {
        val sb = StringBuilder()

        for ((pkgName, entryList) in pkgEntries) {
            sb.append("- title: $pkgName\n  section:\n")

            entryList.forEach { entry ->
                sb.append("  - title: ${entry.baseName} @${entry.packageVersion}\n")
                sb.append("    path: ${tocFileRelPath}/${entry.relPath}\n")
            }
        }
        return sb.toString()
    }

    private fun collectPackages(): Map<String, List<EntryData>> {
        val pkgEntries = mutableMapOf<String, MutableList<EntryData>>()

        this.entries.forEach { entry ->
            if (pkgEntries.containsKey(entry.packageName)) {
                pkgEntries[entry.packageName]!!.add(entry)
            } else {
                pkgEntries[entry.packageName] = mutableListOf(entry)
            }
        }
        //sort on package name. entries *should* already be sorted
        return pkgEntries.toSortedMap()
    }
}