import { substituteIrType as irSubstitute, } from "../types/ir-substitution.js";
import { computeReceiverSubstitution } from "./call-resolution-signatures.js";
import { substitutePolymorphicThis } from "./call-resolution-utilities.js";
const collectTypeParameterNames = (type, acc) => {
    if (!type)
        return;
    switch (type.kind) {
        case "typeParameterType":
            acc.add(type.name);
            return;
        case "arrayType":
            collectTypeParameterNames(type.elementType, acc);
            return;
        case "tupleType":
            for (const element of type.elementTypes) {
                collectTypeParameterNames(element, acc);
            }
            return;
        case "dictionaryType":
            collectTypeParameterNames(type.keyType, acc);
            collectTypeParameterNames(type.valueType, acc);
            return;
        case "referenceType":
            for (const argument of type.typeArguments ?? []) {
                collectTypeParameterNames(argument, acc);
            }
            for (const member of type.structuralMembers ?? []) {
                if (member.kind === "propertySignature") {
                    collectTypeParameterNames(member.type, acc);
                }
                else {
                    for (const parameter of member.parameters) {
                        collectTypeParameterNames(parameter.type, acc);
                    }
                    collectTypeParameterNames(member.returnType, acc);
                }
            }
            return;
        case "unionType":
        case "intersectionType":
            for (const member of type.types) {
                collectTypeParameterNames(member, acc);
            }
            return;
        case "functionType":
            for (const parameter of type.parameters) {
                collectTypeParameterNames(parameter.type, acc);
            }
            collectTypeParameterNames(type.returnType, acc);
            return;
        default:
            return;
    }
};
const collectReceiverGenericNames = (rawSig, signature) => {
    const names = new Set();
    for (const parameter of signature.workingParams) {
        collectTypeParameterNames(parameter, names);
    }
    collectTypeParameterNames(signature.workingThisParam, names);
    collectTypeParameterNames(signature.workingReturn, names);
    if (signature.workingPredicate) {
        collectTypeParameterNames(signature.workingPredicate.targetType, names);
    }
    for (const methodTp of rawSig.typeParameters) {
        names.delete(methodTp.name);
    }
    return names;
};
export const applyReceiverSubstitution = (state, rawSig, effectiveReceiverType, exactDeclaringClrType, signature) => {
    let { workingParams, workingThisParam, workingReturn, workingPredicate } = signature;
    if (effectiveReceiverType &&
        rawSig.declaringTypeTsName &&
        rawSig.declaringMemberName) {
        let receiverSubst = computeReceiverSubstitution(state, effectiveReceiverType, rawSig.declaringTypeTsName, rawSig.declaringMemberName, rawSig.declaringTypeParameterNames, exactDeclaringClrType);
        if ((!receiverSubst || receiverSubst.size === 0) &&
            effectiveReceiverType.kind === "arrayType") {
            const receiverGenericNames = collectReceiverGenericNames(rawSig, {
                workingParams,
                workingThisParam,
                workingReturn,
                workingPredicate,
            });
            if (receiverGenericNames.size === 1) {
                const [only] = receiverGenericNames;
                if (only) {
                    receiverSubst = new Map([
                        [only, effectiveReceiverType.elementType],
                    ]);
                }
            }
        }
        if (receiverSubst && receiverSubst.size > 0) {
            workingParams = workingParams.map((parameter) => parameter ? irSubstitute(parameter, receiverSubst) : undefined);
            if (workingThisParam) {
                workingThisParam = irSubstitute(workingThisParam, receiverSubst);
            }
            workingReturn = irSubstitute(workingReturn, receiverSubst);
            if (workingPredicate) {
                workingPredicate = {
                    ...workingPredicate,
                    targetType: irSubstitute(workingPredicate.targetType, receiverSubst),
                };
            }
        }
    }
    if (effectiveReceiverType) {
        workingParams = workingParams.map((parameter) => parameter
            ? (substitutePolymorphicThis(parameter, effectiveReceiverType) ??
                parameter)
            : undefined);
        if (workingThisParam) {
            workingThisParam =
                substitutePolymorphicThis(workingThisParam, effectiveReceiverType) ??
                    workingThisParam;
        }
        workingReturn =
            substitutePolymorphicThis(workingReturn, effectiveReceiverType) ??
                workingReturn;
        if (workingPredicate) {
            workingPredicate = {
                ...workingPredicate,
                targetType: substitutePolymorphicThis(workingPredicate.targetType, effectiveReceiverType) ?? workingPredicate.targetType,
            };
        }
    }
    return {
        workingParams,
        workingThisParam,
        workingReturn,
        workingPredicate,
    };
};
//# sourceMappingURL=call-resolution-receiver-substitution.js.map