package kotlinx.atomicfu.transformer

import org.objectweb.asm.tree.AbstractInsnNode
import org.objectweb.asm.tree.InsnList
import java.io.File
import org.slf4j.Logger
import org.slf4j.LoggerFactory

abstract class AtomicFUTransformerBase(
    var inputDir: File,
    var outputDir: File
) {
    protected operator fun File.div(child: String) =
        File(this, child)

    protected fun File.toOutputFile(): File =
        outputDir / relativeTo(inputDir).toString()

    private val logger = LoggerFactory.getLogger(this::class.java)

    protected fun File.mkdirsAndWrite(outBytes: ByteArray) {
        parentFile.mkdirs()
        writeBytes(outBytes) // write resulting bytes
    }

    protected fun File.isClassFile() = toString().endsWith(".class")

    var verbose = true
    protected var hasErrors = false
    protected var transformed = false

    data class SourceInfo(
        val method: MethodId,
        val source: String?,
        val i: AbstractInsnNode? = null,
        val insnList: InsnList? = null
    ) {
        override fun toString(): String = buildString {
            source?.let { append("$it:") }
            i?.line?.let { append("$it:") }
            append(" $method")
        }
    }

    private fun format(message: String, sourceInfo: SourceInfo? = null): String {
        var loc = if (sourceInfo == null) "" else sourceInfo.toString() + ": "
        if (verbose && sourceInfo != null && sourceInfo.i != null)
            loc += sourceInfo.i.atIndex(sourceInfo.insnList)
        return "$loc$message"
    }

    protected fun info(message: String, sourceInfo: SourceInfo? = null) {
        logger.info(format(message, sourceInfo))
    }

    protected fun debug(message: String, sourceInfo: SourceInfo? = null) {
        logger.debug(format(message, sourceInfo))
    }

    protected fun error(message: String, sourceInfo: SourceInfo? = null) {
        logger.error(format(message, sourceInfo))
        hasErrors = true
    }

    abstract fun transform()

}