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

import kotlin.reflect.*
import kotlin.reflect.js.internal.*

@PublishedApi
internal fun <T : Annotation> KClass<*>.findAssociatedObject(annotationClass: KClass<T>): Any? {
    return if (this is KClassImpl<*> && annotationClass is KClassImpl<T>) {
        val key = getAssociatedObjectId(annotationClass.jClass.asDynamic()) ?: return null
        val map = jClass.asDynamic().`$metadata$`?.associatedObjects ?: return null
        val factory = map[key] ?: return null
        return factory()
    } else {
        null
    }
}

internal fun getAssociatedObjectId(annotationClass: dynamic): Int? =
    annotationClass.`$metadata$`?.associatedObjectKey?.unsafeCast<Int>()

/**
 * Calls to this function are generated by the compiler when instantiating class metadata.
 * Its result is passed to the [initMetadataFor] family of functions.
 *
 * Returns a map whose keys are integers that correspond to [AssociatedObjectKey]-annotated annotations, and values are object
 * instantiation functions.
 *
 * [entries] is an array whose odd elements are integer numbers initially obtained via [nextAssociatedObjectId],
 * and even elements are object instantiation functions.
 *
 * Note that this function is only called when incrementally compiling to ES5.
 *
 * In ES6 we emit object literals with [computed property names](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#computed_property_names).
 *
 * In non-incremental mode, the integers representing associated object keys are generated statically, so we also directly emit object
 * literals in both ES5 and ES6 modes.
 */
@Suppress("unused") // Usages are generated by the compiler
internal fun makeAssociatedObjectMapES5(entries: Array<dynamic>): dynamic {
    val map = js("{}")
    var i = 0
    while (i < entries.size) {
        map[getAssociatedObjectId(entries[i])] = entries[i + 1]
        i += 2
    }
    return map
}
