/*
 * Copyright 2000-2018 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 kotlinx.metadata

import org.jetbrains.kotlin.metadata.ProtoBuf.*
import org.jetbrains.kotlin.metadata.deserialization.Flags as F
import org.jetbrains.kotlin.metadata.ProtoBuf.Class.Kind as ClassKind

/**
 * Represents a boolean flag that is either present or not in a Kotlin declaration. A "flag" is a boolean trait that is either present
 * or not in a declaration. To check whether the flag is present in the bitmask, call [Flag.invoke] on the flag, passing the bitmask
 * as the argument:
 *
 *     override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor? {
 *         if (Flag.Function.IS_INLINE(flags)) {
 *             ...
 *         }
 *     }
 *
 * To construct a bitmask out of several flags, call [flagsOf] on the needed flags:
 *
 *     v.visitFunction(flagsOf(Flag.Function.IS_DECLARATION, Flag.Function.IS_INLINE), "foo")
 *
 * Flag common to multiple kinds of Kotlin declarations ("common flags") are declared in [Flag.Common].
 * Flag applicable to specific kinds of declarations ("declaration-specific flags") are declared in nested objects of the [Flag] object.
 *
 * Some flags are mutually exclusive, i.e. there are "flag groups" such that no more than one flag from each group can be present
 * in the same bitmask. Among common flags, there are the following flag groups:
 * * visibility flags: [IS_INTERNAL], [IS_PRIVATE], [IS_PROTECTED], [IS_PUBLIC], [IS_PRIVATE_TO_THIS], [IS_LOCAL]
 * * modality flags: [IS_FINAL], [IS_OPEN], [IS_ABSTRACT], [IS_SEALED]
 *
 * Some declaration-specific flags form other flag groups, see the documentation of the corresponding containers for more information.
 *
 * @see Flags
 * @see flagsOf
 */
class Flag internal constructor(
    private val offset: Int,
    private val bitWidth: Int,
    private val value: Int
) {
    internal constructor(field: F.FlagField<*>, value: Int) : this(field.offset, field.bitWidth, value)

    internal constructor(field: F.BooleanFlagField) : this(field, 1)

    internal operator fun plus(flags: Flags): Flags =
        (flags and (((1 shl bitWidth) - 1) shl offset).inv()) + (value shl offset)

    /**
     * Checks whether the flag is present in the given bitmask.
     */
    operator fun invoke(flags: Flags): Boolean =
        (flags ushr offset) and ((1 shl bitWidth) - 1) == value

    companion object Common {
        /**
         * Signifies that the corresponding declaration has at least one annotation.
         *
         * This flag is useful for reading Kotlin metadata on JVM efficiently. On JVM, most of the annotations are written not to the Kotlin
         * metadata, but directly on the corresponding declarations in the class file. This flag can be used as an optimization to avoid
         * reading annotations from the class file (which can be slow) in case when a declaration has no annotations.
         */
        @JvmField
        val HAS_ANNOTATIONS = Flag(F.HAS_ANNOTATIONS)


        /**
         * A visibility flag, signifying that the corresponding declaration is `internal`.
         */
        @JvmField
        val IS_INTERNAL = Flag(F.VISIBILITY, Visibility.INTERNAL_VALUE)

        /**
         * A visibility flag, signifying that the corresponding declaration is `private`.
         */
        @JvmField
        val IS_PRIVATE = Flag(F.VISIBILITY, Visibility.PRIVATE_VALUE)

        /**
         * A visibility flag, signifying that the corresponding declaration is `protected`.
         */
        @JvmField
        val IS_PROTECTED = Flag(F.VISIBILITY, Visibility.PROTECTED_VALUE)

        /**
         * A visibility flag, signifying that the corresponding declaration is `public`.
         */
        @JvmField
        val IS_PUBLIC = Flag(F.VISIBILITY, Visibility.PUBLIC_VALUE)

        /**
         * A visibility flag, signifying that the corresponding declaration is "private-to-this", which is a non-denotable visibility of
         * private members in Kotlin which are callable only on the same instance of the declaring class.
         */
        @JvmField
        val IS_PRIVATE_TO_THIS = Flag(F.VISIBILITY, Visibility.PRIVATE_TO_THIS_VALUE)

        /**
         * A visibility flag, signifying that the corresponding declaration is local, i.e. declared inside a code block
         * and not visible from the outside.
         */
        @JvmField
        val IS_LOCAL = Flag(F.VISIBILITY, Visibility.LOCAL_VALUE)


        /**
         * A modality flag, signifying that the corresponding declaration is `final`.
         */
        @JvmField
        val IS_FINAL = Flag(F.MODALITY, Modality.FINAL_VALUE)

        /**
         * A modality flag, signifying that the corresponding declaration is `open`.
         */
        @JvmField
        val IS_OPEN = Flag(F.MODALITY, Modality.OPEN_VALUE)

        /**
         * A modality flag, signifying that the corresponding declaration is `abstract`.
         */
        @JvmField
        val IS_ABSTRACT = Flag(F.MODALITY, Modality.ABSTRACT_VALUE)

        /**
         * A modality flag, signifying that the corresponding declaration is `sealed`.
         */
        @JvmField
        val IS_SEALED = Flag(F.MODALITY, Modality.SEALED_VALUE)
    }

    /**
     * A container of flags applicable to Kotlin classes, including interfaces, objects, enum classes and annotation classes.
     *
     * In addition to the common flag groups, the following flag groups exist for class flags:
     * * class kind flags: [IS_CLASS], [IS_INTERFACE], [IS_ENUM_CLASS], [IS_ENUM_ENTRY], [IS_ANNOTATION_CLASS], [IS_OBJECT],
     * [IS_COMPANION_OBJECT]
     */
    object Class {
        /**
         * A class kind flag, signifying that the corresponding class is a usual `class`.
         */
        @JvmField
        val IS_CLASS = Flag(F.CLASS_KIND, ClassKind.CLASS_VALUE)

        /**
         * A class kind flag, signifying that the corresponding class is an `interface`.
         */
        @JvmField
        val IS_INTERFACE = Flag(F.CLASS_KIND, ClassKind.INTERFACE_VALUE)

        /**
         * A class kind flag, signifying that the corresponding class is an `enum class`.
         */
        @JvmField
        val IS_ENUM_CLASS = Flag(F.CLASS_KIND, ClassKind.ENUM_CLASS_VALUE)

        /**
         * A class kind flag, signifying that the corresponding class is an enum entry.
         */
        @JvmField
        val IS_ENUM_ENTRY = Flag(F.CLASS_KIND, ClassKind.ENUM_ENTRY_VALUE)

        /**
         * A class kind flag, signifying that the corresponding class is an `annotation class`.
         */
        @JvmField
        val IS_ANNOTATION_CLASS = Flag(F.CLASS_KIND, ClassKind.ANNOTATION_CLASS_VALUE)

        /**
         * A class kind flag, signifying that the corresponding class is a non-companion `object`.
         */
        @JvmField
        val IS_OBJECT = Flag(F.CLASS_KIND, ClassKind.OBJECT_VALUE)

        /**
         * A class kind flag, signifying that the corresponding class is a `companion object`.
         */
        @JvmField
        val IS_COMPANION_OBJECT = Flag(F.CLASS_KIND, ClassKind.COMPANION_OBJECT_VALUE)


        /**
         * Signifies that the corresponding class is `inner`.
         */
        @JvmField
        val IS_INNER = Flag(F.IS_INNER)

        /**
         * Signifies that the corresponding class is `data`.
         */
        @JvmField
        val IS_DATA = Flag(F.IS_DATA)

        /**
         * Signifies that the corresponding class is `external`.
         */
        @JvmField
        val IS_EXTERNAL = Flag(F.IS_EXTERNAL_CLASS)

        /**
         * Signifies that the corresponding class is `expect`.
         */
        @JvmField
        val IS_EXPECT = Flag(F.IS_EXPECT_CLASS)

        /**
         * Signifies that the corresponding class is `inline`.
         */
        @JvmField
        val IS_INLINE = Flag(F.IS_INLINE_CLASS)
    }

    /**
     * A container of flags applicable to Kotlin constructors.
     */
    object Constructor {
        /**
         * Signifies that the corresponding constructor is primary, i.e. declared in the class header, not in the class body.
         */
        @JvmField
        val IS_PRIMARY = Flag(F.IS_SECONDARY, 0)
    }

    /**
     * A container of flags applicable to Kotlin functions.
     *
     * In addition to the common flag groups, the following flag groups exist for function flags:
     * * member kind flags: [IS_DECLARATION], [IS_FAKE_OVERRIDE], [IS_DELEGATION], [IS_SYNTHESIZED]
     */
    object Function {
        /**
         * A member kind flag, signifying that the corresponding function is explicitly declared in the containing class.
         */
        @JvmField
        val IS_DECLARATION = Flag(F.MEMBER_KIND, MemberKind.DECLARATION_VALUE)

        /**
         * A member kind flag, signifying that the corresponding function exists in the containing class because a function with a suitable
         * signature exists in a supertype. This flag is not written by the Kotlin compiler and its effects are unspecified.
         */
        @JvmField
        val IS_FAKE_OVERRIDE = Flag(F.MEMBER_KIND, MemberKind.FAKE_OVERRIDE_VALUE)

        /**
         * A member kind flag, signifying that the corresponding function exists in the containing class because it has been produced
         * by interface delegation (delegation "by").
         */
        @JvmField
        val IS_DELEGATION = Flag(F.MEMBER_KIND, MemberKind.DELEGATION_VALUE)

        /**
         * A member kind flag, signifying that the corresponding function exists in the containing class because it has been synthesized
         * by the compiler and has no declaration in the source code.
         */
        @JvmField
        val IS_SYNTHESIZED = Flag(F.MEMBER_KIND, MemberKind.SYNTHESIZED_VALUE)


        /**
         * Signifies that the corresponding function is `operator`.
         */
        @JvmField
        val IS_OPERATOR = Flag(F.IS_OPERATOR)

        /**
         * Signifies that the corresponding function is `infix`.
         */
        @JvmField
        val IS_INFIX = Flag(F.IS_INFIX)

        /**
         * Signifies that the corresponding function is `inline`.
         */
        @JvmField
        val IS_INLINE = Flag(F.IS_INLINE)

        /**
         * Signifies that the corresponding function is `tailrec`.
         */
        @JvmField
        val IS_TAILREC = Flag(F.IS_TAILREC)

        /**
         * Signifies that the corresponding function is `external`.
         */
        @JvmField
        val IS_EXTERNAL = Flag(F.IS_EXTERNAL_FUNCTION)

        /**
         * Signifies that the corresponding function is `suspend`.
         */
        @JvmField
        val IS_SUSPEND = Flag(F.IS_SUSPEND)

        /**
         * Signifies that the corresponding function is `expect`.
         */
        @JvmField
        val IS_EXPECT = Flag(F.IS_EXPECT_FUNCTION)
    }

    /**
     * A container of flags applicable to Kotlin properties.
     *
     * In addition to the common flag groups, the following flag groups exist for property flags:
     * * member kind flags: [IS_DECLARATION], [IS_FAKE_OVERRIDE], [IS_DELEGATION], [IS_SYNTHESIZED]
     */
    object Property {
        /**
         * A member kind flag, signifying that the corresponding property is explicitly declared in the containing class.
         */
        @JvmField
        val IS_DECLARATION = Flag(F.MEMBER_KIND, MemberKind.DECLARATION_VALUE)

        /**
         * A member kind flag, signifying that the corresponding property exists in the containing class because a property with a suitable
         * signature exists in a supertype. This flag is not written by the Kotlin compiler and its effects are unspecified.
         */
        @JvmField
        val IS_FAKE_OVERRIDE = Flag(F.MEMBER_KIND, MemberKind.FAKE_OVERRIDE_VALUE)

        /**
         * A member kind flag, signifying that the corresponding property exists in the containing class because it has been produced
         * by interface delegation (delegation "by").
         */
        @JvmField
        val IS_DELEGATION = Flag(F.MEMBER_KIND, MemberKind.DELEGATION_VALUE)

        /**
         * A member kind flag, signifying that the corresponding property exists in the containing class because it has been synthesized
         * by the compiler and has no declaration in the source code.
         */
        @JvmField
        val IS_SYNTHESIZED = Flag(F.MEMBER_KIND, MemberKind.SYNTHESIZED_VALUE)


        /**
         * Signifies that the corresponding property is `var`.
         */
        @JvmField
        val IS_VAR = Flag(F.IS_VAR)

        /**
         * Signifies that the corresponding property has a getter.
         */
        @JvmField
        val HAS_GETTER = Flag(F.HAS_GETTER)

        /**
         * Signifies that the corresponding property has a setter.
         */
        @JvmField
        val HAS_SETTER = Flag(F.HAS_SETTER)

        /**
         * Signifies that the corresponding property is `const`.
         */
        @JvmField
        val IS_CONST = Flag(F.IS_CONST)

        /**
         * Signifies that the corresponding property is `lateinit`.
         */
        @JvmField
        val IS_LATEINIT = Flag(F.IS_LATEINIT)

        /**
         * Signifies that the corresponding property has a constant value. On JVM, this flag allows an optimization similarly to
         * [F.HAS_ANNOTATIONS]: constant values of properties are written to the bytecode directly, and this flag can be used to avoid
         * reading the value from the bytecode in case there isn't one.
         */
        @JvmField
        val HAS_CONSTANT = Flag(F.HAS_CONSTANT)

        /**
         * Signifies that the corresponding property is `external`.
         */
        @JvmField
        val IS_EXTERNAL = Flag(F.IS_EXTERNAL_PROPERTY)

        /**
         * Signifies that the corresponding property is a delegated property.
         */
        @JvmField
        val IS_DELEGATED = Flag(F.IS_DELEGATED)

        /**
         * Signifies that the corresponding property is `expect`.
         */
        @JvmField
        val IS_EXPECT = Flag(F.IS_EXPECT_PROPERTY)
    }

    /**
     * A container of flags applicable to Kotlin property getters and setters.
     */
    object PropertyAccessor {
        /**
         * Signifies that the corresponding property accessor is not default, i.e. it has a body and/or annotations in the source code.
         */
        @JvmField
        val IS_NOT_DEFAULT = Flag(F.IS_NOT_DEFAULT)

        /**
         * Signifies that the corresponding property accessor is `external`.
         */
        @JvmField
        val IS_EXTERNAL = Flag(F.IS_EXTERNAL_ACCESSOR)

        /**
         * Signifies that the corresponding property accessor is `inline`.
         */
        @JvmField
        val IS_INLINE = Flag(F.IS_INLINE_ACCESSOR)
    }

    /**
     * A container of flags applicable to Kotlin types.
     */
    object Type {
        /**
         * Signifies that the corresponding type is marked as nullable, i.e. has a question mark at the end of its notation.
         */
        @JvmField
        val IS_NULLABLE = Flag(0, 1, 1)

        /**
         * Signifies that the corresponding type is `suspend`.
         */
        @JvmField
        val IS_SUSPEND = Flag(F.SUSPEND_TYPE.offset + 1, F.SUSPEND_TYPE.bitWidth, 1)
    }

    /**
     * A container of flags applicable to Kotlin type parameters.
     */
    object TypeParameter {
        /**
         * Signifies that the corresponding type parameter is `reified`.
         */
        @JvmField
        val IS_REIFIED = Flag(0, 1, 1)
    }

    /**
     * A container of flags applicable to Kotlin value parameters.
     */
    object ValueParameter {
        /**
         * Signifies that the corresponding value parameter declares a default value. Note that the default value itself can be a complex
         * expression and is not available via metadata. Also note that in case of an override of a parameter with default value, the
         * parameter in the derived method does _not_ declare the default value ([DECLARES_DEFAULT_VALUE] == false), but the parameter is
         * still optional at the call site because the default value from the base method is used.
         */
        @JvmField
        val DECLARES_DEFAULT_VALUE = Flag(F.DECLARES_DEFAULT_VALUE)

        /**
         * Signifies that the corresponding value parameter is `crossinline`.
         */
        @JvmField
        val IS_CROSSINLINE = Flag(F.IS_CROSSINLINE)

        /**
         * Signifies that the corresponding value parameter is `noinline`.
         */
        @JvmField
        val IS_NOINLINE = Flag(F.IS_NOINLINE)
    }

    /**
     * A container of flags applicable to Kotlin effect expressions.
     *
     * Contracts are an internal feature of the standard Kotlin library, and their behavior and/or binary format
     * may change in a subsequent release.
     */
    object EffectExpression {
        /**
         * Signifies that the corresponding effect expression should be negated to compute the proposition or the conclusion of an effect.
         */
        @JvmField
        val IS_NEGATED = Flag(F.IS_NEGATED)

        /**
         * Signifies that the corresponding effect expression checks whether a value of some variable is `null`.
         */
        @JvmField
        val IS_NULL_CHECK_PREDICATE = Flag(F.IS_NULL_CHECK_PREDICATE)
    }
}
