package org.jetbrains

import org.gradle.api.tasks.AbstractExecTask
import org.gradle.internal.os.OperatingSystem
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths

open class CrossPlatformExec : AbstractExecTask<CrossPlatformExec>(CrossPlatformExec::class.java) {
    private val windowsExtensions = listOf(".bat", ".cmd", ".exe")
    private val unixExtensions = listOf("", ".sh")

    private val isWindows = OperatingSystem.current().isWindows

    override fun exec() {
        val commandLine: MutableList<String> = this.commandLine

        if (commandLine.isNotEmpty()) {
            commandLine[0] = findCommand(commandLine[0])
        }

        if (isWindows && commandLine.isNotEmpty() && commandLine[0].isNotBlank()) {
            this.commandLine = listOf("cmd", "/c") + commandLine
        } else {
            this.commandLine = commandLine
        }

        super.exec()
    }

    private fun findCommand(command: String): String {
        val command = normalizeCommandPaths(command)
        val extensions = if (isWindows) windowsExtensions else unixExtensions

        return extensions.map { extension ->
            resolveCommandFromFile(Paths.get("$command$extension"))
        }.firstOrNull { it.isNotBlank() } ?: command
    }

    private fun resolveCommandFromFile(commandFile: Path) =
        if (!Files.isExecutable(commandFile)) {
            ""
        } else {
            commandFile.toAbsolutePath().normalize().toString()
        }


    private fun normalizeCommandPaths(command: String): String {
        // need to escape backslash so it works with regex
        val backslashSeparator = "\\"
        val forwardSlashSeparator = "/"

        // get the actual separator
        val separator = if (File.separatorChar == '\\') backslashSeparator else File.separator

        return command
            // first replace all of the backslashes with forward slashes
            .replace(backslashSeparator, forwardSlashSeparator)
            // then replace all forward slashes with whatever the separator actually is
            .replace(forwardSlashSeparator, separator)
    }
}