/*
 * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license 
 * that can be found in the license/LICENSE.txt file.
 */

package org.jetbrains.kotlin.fir.lightTree

import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.CharsetToolkit
import com.intellij.psi.impl.DebugUtil
import com.intellij.testFramework.TestDataPath
import com.intellij.util.PathUtil
import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.fir.builder.AbstractRawFirBuilderTestCase
import org.jetbrains.kotlin.fir.builder.StubFirScopeProvider
import org.jetbrains.kotlin.fir.renderer.FirRenderer
import org.jetbrains.kotlin.fir.session.FirSessionFactoryHelper
import org.jetbrains.kotlin.parsing.KotlinLightParser
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners
import org.junit.runner.RunWith
import java.io.File
import kotlin.system.measureNanoTime

@TestDataPath("/")
@RunWith(JUnit3RunnerWithInners::class)
class TotalKotlinTest : AbstractRawFirBuilderTestCase() {
    private fun generateFirFromPsi(onlyPsi: Boolean, text: String, path: String) {
        val ktFile = createPsiFile(FileUtil.getNameWithoutExtension(PathUtil.getFileName(path)), text) as KtFile
        if (onlyPsi) {
            DebugUtil.psiTreeToString(ktFile, false)
        } else {
            val firFile = ktFile.toFirFile()
            FirRenderer().renderElementAsString(firFile)
        }
    }

    private fun generateFirFromLightTree(
        onlyLightTree: Boolean, converter: LightTree2Fir,
        text: CharSequence, sourceFile: KtSourceFile, linesMapping: KtSourceFileLinesMapping
    ) {
        if (onlyLightTree) {
            val lightTree = KotlinLightParser.buildLightTree(text, sourceFile, errorListener = null)
            DebugUtil.lightTreeToString(lightTree, false)
        } else {
            val firFile = converter.buildFirFile(text, sourceFile, linesMapping)
            FirRenderer().renderElementAsString(firFile)
        }
    }

    private fun totalKotlinLight(onlyLightTree: Boolean) {
        val path = System.getProperty("user.dir")
        var counter = 0
        var time = 0L

        @OptIn(ObsoleteTestInfrastructure::class)
        val lightTreeConverter = LightTree2Fir(
            session = FirSessionFactoryHelper.createEmptySession(),
            scopeProvider = StubFirScopeProvider,
            diagnosticsReporter = null
        )

        if (onlyLightTree) println("LightTree generation") else println("Fir from LightTree converter")
        println("BASE PATH: $path")
        path.walkTopDown {
            val sourceFile = KtIoFileSourceFile(it)
            val (code, linesMapping) = with(it.inputStream().reader(Charsets.UTF_8)) {
                this.readSourceFileWithMapping()
            }
            time += measureNanoTime {
                generateFirFromLightTree(onlyLightTree, lightTreeConverter, code, sourceFile, linesMapping)
            }

            counter++
        }
        println("SUCCESS!")
        println("TIME PER FILE: ${(time / counter) * 1e-6} ms, COUNTER: $counter")

    }

    private fun totalKotlinPsi(onlyPsi: Boolean) {
        val path = System.getProperty("user.dir")
        val root = File(path)
        var counter = 0
        var time = 0L

        if (onlyPsi) println("Psi generation") else println("Fir from Psi converter")
        println("BASE PATH: $path")
        for (file in root.walkTopDown()) {
            if (file.isDirectory) continue
            /* TODO: fix this, please !!! */
            if (file.path.contains("kotlin-native") ||
                file.path.lowercase().contains("testdata") ||
                file.path.contains("resources")
            ) continue
            if (file.extension != "kt") continue

            val text = FileUtil.loadFile(file, CharsetToolkit.UTF8, true).trim()
            time += measureNanoTime {
                try {
                    generateFirFromPsi(onlyPsi, text, file.path)
                } catch (e: Exception) {
                    throw IllegalStateException(file.path, e)
                }
            }

            counter++
        }
        println("SUCCESS!")
        println("TIME PER FILE: ${(time / counter) * 1e-6} ms, COUNTER: $counter")

    }

    fun testTotalKotlinOnlyLightTree() {
        totalKotlinLight(true)
    }

    fun testTotalKotlinOnlyPsi() {
        totalKotlinPsi(true)
    }

    fun testTotalKotlinFirFromLightTree() {
        totalKotlinLight(false)
    }

    fun testTotalKotlinFirFromPsi() {
        totalKotlinPsi(false)
    }
}
