"use strict";
/**
 * If you decide to structure your mod as a set of feature classes, you can use decorators to
 * automatically register callbacks.
 *
 * Currently, there are two decorators:
 * - `@Callback`
 * - `@CallbackCustom`
 *
 * For example:
 *
 * ```ts
 * export class MyFeature extends ModFeature {
 *   @Callback(ModCallback.POST_GAME_STARTED)
 *   postGameStarted(isContinued: boolean): void {
 *     Isaac.DebugString(`Callback fired: POST_GAME_STARTED`);
 *   }
 * }
 * ```
 *
 * @module
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.PriorityCallbackCustom = exports.PriorityCallback = exports.CallbackCustom = exports.Callback = void 0;
const isaac_typescript_definitions_1 = require("isaac-typescript-definitions");
const ModFeature_1 = require("../classes/ModFeature");
const tstlClass_1 = require("./tstlClass");
/**
 * A decorator function that signifies that the decorated class method should be automatically
 * registered with `Mod.AddCallback`.
 *
 * @allowEmptyVariadic
 * @ignore
 */
// We tell TypeDoc to ignore this function because it generates a bunch of spam.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
function Callback(modCallback, ...optionalArgs) {
    return PriorityCallback(modCallback, isaac_typescript_definitions_1.CallbackPriority.DEFAULT, ...optionalArgs);
}
exports.Callback = Callback;
/**
 * A decorator function that signifies that the decorated class method should be automatically
 * registered with `ModUpgraded.AddCallbackCustom`.
 *
 * @allowEmptyVariadic
 * @ignore
 */
// We tell TypeDoc to ignore this function because it generates a bunch of spam.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
function CallbackCustom(modCallbackCustom, ...optionalArgs) {
    return PriorityCallbackCustom(modCallbackCustom, isaac_typescript_definitions_1.CallbackPriority.DEFAULT, ...optionalArgs);
}
exports.CallbackCustom = CallbackCustom;
/**
 * A decorator function that signifies that the decorated class method should be automatically
 * registered with `Mod.AddPriorityCallback`.
 *
 * @allowEmptyVariadic
 * @ignore
 */
// We tell TypeDoc to ignore this function because it generates a bunch of spam.
function PriorityCallback(modCallback, priority, ...optionalArgs) {
    return (target, propertyKey, _descriptor) => {
        // First, prepare the arguments for the `Mod.AddPriorityCallback` method.
        const methodName = propertyKey;
        const method = target[methodName];
        const callbackTuple = [modCallback, priority, method, optionalArgs];
        // Since the decorator runs prior to instantiation, we only have access to get and set static
        // properties, which are located on the "constructor" table. Thus, we store the callback
        // arguments for later.
        const constructor = target.constructor;
        if (constructor === undefined) {
            const tstlClassName = (0, tstlClass_1.getTSTLClassName)(target) ?? "Unknown";
            error(`Failed to get the constructor for class "${tstlClassName}". Did you decorate a static method? You can only decorate non-static class methods, because the "Mod" object is not present before the class is instantiated.`);
        }
        if (!constructor.has(ModFeature_1.MOD_FEATURE_CALLBACKS_KEY)) {
            constructor.set(ModFeature_1.MOD_FEATURE_CALLBACKS_KEY, []);
        }
        const callbackTuples = constructor.get(ModFeature_1.MOD_FEATURE_CALLBACKS_KEY);
        callbackTuples.push(callbackTuple);
    };
}
exports.PriorityCallback = PriorityCallback;
/**
 * A decorator function that signifies that the decorated class method should be automatically
 * registered with `ModUpgraded.AddCallbackCustom`.
 *
 * @allowEmptyVariadic
 * @ignore
 */
// We tell TypeDoc to ignore this function because it generates a bunch of spam.
function PriorityCallbackCustom(modCallbackCustom, priority, ...optionalArgs) {
    return (target, propertyKey, _descriptor) => {
        // First, prepare the arguments for the `Mod.AddCallbackCustom` method.
        const methodName = propertyKey;
        const method = target[methodName];
        const callbackTuple = [modCallbackCustom, priority, method, optionalArgs];
        // Since the decorator runs prior to instantiation, we only have access to get and set static
        // properties, which are located on the "constructor" table. Thus, we store the callback
        // arguments for later.
        const constructor = target.constructor;
        if (constructor === undefined) {
            const tstlClassName = (0, tstlClass_1.getTSTLClassName)(target) ?? "Unknown";
            error(`Failed to get the constructor for class "${tstlClassName}". Did you decorate a static method? You can only decorate non-static class methods, because the "Mod" object is not present before the class is instantiated.`);
        }
        if (!constructor.has(ModFeature_1.MOD_FEATURE_CUSTOM_CALLBACKS_KEY)) {
            constructor.set(ModFeature_1.MOD_FEATURE_CUSTOM_CALLBACKS_KEY, []);
        }
        const callbackTuples = constructor.get(ModFeature_1.MOD_FEATURE_CUSTOM_CALLBACKS_KEY);
        callbackTuples.push(callbackTuple);
    };
}
exports.PriorityCallbackCustom = PriorityCallbackCustom;
