/*
 * Copyright 2010-2015 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.backend.common.bridges

import org.jetbrains.kotlin.utils.DFS
import java.util.*
import kotlin.collections.LinkedHashMap

interface FunctionHandle {
    val isDeclaration: Boolean
    val isAbstract: Boolean

    /** On finding concrete super declaration we should distinguish non-abstract java8/js default methods from
     * class ones (see [findConcreteSuperDeclaration] method in bridges.kt).
     * Note that interface methods with body compiled to jvm 8 target are assumed to be non-abstract in bridges method calculation
     * (more details in [DescriptorBasedFunctionHandle.isBodyOwner] comment).*/
    val mayBeUsedAsSuperImplementation: Boolean

    fun getOverridden(): Iterable<FunctionHandle>

    val mightBeIncorrectCode: Boolean get() = false
}

data class Bridge<out Signature, out Function : FunctionHandle>(
    val from: Signature,
    val to: Signature,
    val originalFunctions: Set<Function>
) {
    override fun toString() = "$from -> $to"
}


fun <Function : FunctionHandle, Signature> generateBridges(
    function: Function,
    signature: (Function) -> Signature
): Set<Bridge<Signature, Function>> {
    // If it's an abstract function, no bridges are needed: when an implementation will appear in some concrete subclass, all necessary
    // bridges will be generated there
    if (function.isAbstract) return setOf()

    val fake = !function.isDeclaration

    // If it's a concrete fake override and all of its super-functions are concrete, then every possible bridge is already generated
    // into some of the super-classes and will be inherited in this class
    if (fake && function.getOverridden().none { it.isAbstract }) return setOf()

    val implementation = findConcreteSuperDeclaration(function) ?: return setOf()

    val bridgesToGenerate = findAllReachableDeclarations(function).groupByTo(LinkedHashMap(), signature)

    if (fake) {
        // If it's a concrete fake override, some of the bridges may be inherited from the super-classes. Specifically, bridges for all
        // declarations that are reachable from all concrete immediate super-functions of the given function. Note that all such bridges are
        // guaranteed to delegate to the same implementation as bridges for the given function, that's why it's safe to inherit them
        @Suppress("UNCHECKED_CAST")
        for (overridden in function.getOverridden() as Iterable<Function>) {
            if (!overridden.isAbstract) {
                for (reachable in findAllReachableDeclarations(overridden)) {
                    bridgesToGenerate.remove(signature(reachable))
                }
            }
        }
    }

    val method = signature(implementation)
    bridgesToGenerate.remove(method)
    return bridgesToGenerate.entries
        .map { (overriddenSignature, overriddenFunctions) ->
            Bridge(overriddenSignature, method, overriddenFunctions.toSet())
        }
        .toSet()
}

fun <Function : FunctionHandle> findAllReachableDeclarations(function: Function): MutableSet<Function> {
    val collector = object : DFS.NodeHandlerWithListResult<Function, Function>() {
        override fun afterChildren(current: Function) {
            if (current.isDeclaration) {
                result.add(current)
            }
        }
    }
    @Suppress("UNCHECKED_CAST")
    DFS.dfs(listOf(function), { it.getOverridden() as Iterable<Function> }, collector)
    return LinkedHashSet(collector.result())
}

/**
 * Given a concrete function, finds an implementation (a concrete declaration) of this function in the supertypes.
 * The implementation is guaranteed to exist because if it wouldn't, the given function would've been abstract
 */
fun <Function : FunctionHandle> findConcreteSuperDeclaration(function: Function): Function? {
    require(!function.isAbstract) { "Only concrete functions have implementations: $function" }

    if (function.isDeclaration) return function

    // To find an implementation of a concrete fake override, we should find among all its super-functions such function that:
    // 1) it's a concrete declaration
    // 2) it's not reachable from any other declaration reachable from the given function
    // The compiler guarantees that there will be exactly one such function.
    // The following algorithm is used: first, we find all declarations reachable from the given function. Then for each such declaration
    // we remove from that result all declarations reachable from it. The result is now guaranteed to have exactly one concrete declaration
    // (and possibly some abstract declarations, which don't matter)

    val result = findAllReachableDeclarations(function)
    val toRemove = HashSet<Function>()
    for (declaration in result) {
        val reachable = findAllReachableDeclarations(declaration)
        reachable.remove(declaration)
        toRemove.addAll(reachable)
    }
    result.removeAll(toRemove)

    val concreteRelevantDeclarations = result.filter { !it.isAbstract && it.mayBeUsedAsSuperImplementation }
    if (concreteRelevantDeclarations.size != 1) {
        if (!function.mightBeIncorrectCode) {
            error("Concrete fake override $function should have exactly one concrete super-declaration: $concreteRelevantDeclarations")
        } else {
            return null
        }
    }

    return concreteRelevantDeclarations[0]
}
