/*
 * Copyright 2010-2017 JetBrains s.r.o.
 *
 * 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 org.jetbrains.kotlin.cli.common.arguments

import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.utils.Jsr305State
import org.jetbrains.kotlin.utils.ReportLevel

class Jsr305Parser(private val collector: MessageCollector) {
    fun parse(value: Array<String>?, supportCompatqualCheckerFrameworkAnnotations: String?): Jsr305State {
        var global: ReportLevel? = null
        var migration: ReportLevel? = null
        val userDefined = mutableMapOf<String, ReportLevel>()

        fun parseJsr305UnderMigration(item: String): ReportLevel? {
            val rawState = item.split(":").takeIf { it.size == 2 }?.get(1)
            return ReportLevel.findByDescription(rawState) ?: reportUnrecognizedJsr305(item).let { null }
        }

        value?.forEach { item ->
            when {
                item.startsWith("@") -> {
                    val (name, state) = parseJsr305UserDefined(item) ?: return@forEach
                    val current = userDefined[name]
                    if (current == null) {
                        userDefined[name] = state
                    } else if (current != state) {
                        reportDuplicateJsr305("@$name:${current.description}", item)
                        return@forEach
                    }
                }
                item.startsWith("under-migration") -> {
                    val state = parseJsr305UnderMigration(item)
                    if (migration == null) {
                        migration = state
                    } else if (migration != state) {
                        reportDuplicateJsr305("under-migration:${migration?.description}", item)
                        return@forEach
                    }
                }
                item == "enable" -> {
                    collector.report(
                            CompilerMessageSeverity.STRONG_WARNING,
                            "Option 'enable' for -Xjsr305 flag is deprecated. Please use 'strict' instead"
                    )
                    if (global != null) return@forEach

                    global = ReportLevel.STRICT
                }
                else -> {
                    if (global == null) {
                        global = ReportLevel.findByDescription(item)
                    } else if (global!!.description != item) {
                        reportDuplicateJsr305(global!!.description, item)
                        return@forEach
                    }
                }
            }
        }

        val enableCompatqualCheckerFrameworkAnnotations = when (supportCompatqualCheckerFrameworkAnnotations) {
            "enable" -> true
            "disable" -> false
            null -> null
            else -> {
                collector.report(
                    CompilerMessageSeverity.ERROR,
                    "Unrecognized -Xsupport-compatqual-checker-framework-annotations option: $supportCompatqualCheckerFrameworkAnnotations. Possible values are 'enable'/'disable'"
                )
                null
            }
        }

        val state = Jsr305State(
            global ?: ReportLevel.WARN, migration, userDefined,
            enableCompatqualCheckerFrameworkAnnotations =
            enableCompatqualCheckerFrameworkAnnotations
                    ?: Jsr305State.COMPATQUAL_CHECKER_FRAMEWORK_ANNOTATIONS_SUPPORT_DEFAULT_VALUE
        )
        return if (state == Jsr305State.DISABLED) Jsr305State.DISABLED else state
    }

    private fun reportUnrecognizedJsr305(item: String) {
        collector.report(CompilerMessageSeverity.ERROR, "Unrecognized -Xjsr305 value: $item")
    }

    private fun reportDuplicateJsr305(first: String, second: String) {
        collector.report(CompilerMessageSeverity.ERROR, "Conflict duplicating -Xjsr305 value: $first, $second")
    }

    private fun parseJsr305UserDefined(item: String): Pair<String, ReportLevel>? {
        val (name, rawState) = item.substring(1).split(":").takeIf { it.size == 2 } ?: run {
            reportUnrecognizedJsr305(item)
            return null
        }

        val state = ReportLevel.findByDescription(rawState) ?: run {
            reportUnrecognizedJsr305(item)
            return null
        }

        return name to state
    }
}
