package org.jcnc.snow.compiler.backend;

import org.jcnc.snow.vm.engine.VMOpCode;

import java.util.*;

/**
 * 构建线性 VM 程序，支持对函数调用地址的延迟解析与修补。
 * <p>
 * 本类在生成程序时按顺序追加指令，并在定义函数入口时修补之前遇到的对该函数的调用。
 * 最终可产出完整的指令列表，用于 VM 执行。
 * </p>
 */
public final class VMProgramBuilder {

    /**
     * 记录待修补的调用信息，包括指令索引、目标函数名称及参数个数。
     */
    private record Fix(int index, String target, int nArgs) {}

    /**
     * 用于在目标地址未知时的占位符。
     */
    private static final String PLACEHOLDER = "-1";

    /**
     * 存储生成的指令行，按追加顺序对应程序计数器地址。
     */
    private final List<String> code = new ArrayList<>();

    /**
     * 已知函数名称到其入口地址（程序计数）的映射表。
     */
    private final Map<String, Integer> labelAddr = new HashMap<>();

    /**
     * 存放所有尚未解析目标地址的调用信息，待函数定义时进行修补。
     */
    private final List<Fix> unresolved = new ArrayList<>();

    /**
     * 当前程序计数器，表示下一条指令的地址。
     */
    private int pc = 0;

    /**
     * 标记函数入口位置，并即时修补之前对该函数的所有延迟调用。
     *
     * @param name 函数名称
     */
    public void beginFunction(String name) {
        labelAddr.put(name, pc);
        patchFixesFor(name);
    }

    /**
     * 追加一条原始指令到程序，并将程序计数器递增。
     *
     * @param line 完整的指令文本
     */
    public void emit(String line) {
        code.add(line);
        pc++;
    }

    /**
     * 生成函数调用指令 "CALL &lt;addr&gt; &lt;nArgs&gt;"。
     * <ul>
     *   <li>若目标函数已定义，则直接使用其地址；</li>
     *   <li>否则使用占位符并记录调用信息，待后续修补。</li>
     * </ul>
     *
     * @param targetFn 目标函数名称
     * @param nArgs    调用参数数量
     */
    public void emitCall(String targetFn, int nArgs) {
        Integer addr = resolve(targetFn);
        if (addr != null) {
            emit(VMOpCode.CALL + " " + addr + " " + nArgs);
        } else {
            emit(VMOpCode.CALL + " " + PLACEHOLDER + " " + nArgs);
            unresolved.add(new Fix(pc - 1, targetFn, nArgs));
        }
    }

    /**
     * 完成指令生成并返回不可变的指令列表。
     *
     * @return 按执行顺序排列的完整指令列表
     * @throws IllegalStateException 若仍存在未修补的调用目标
     */
    public List<String> build() {
        if (!unresolved.isEmpty()) {
            throw new IllegalStateException("Unresolved CALL targets: " + unresolved);
        }
        return List.copyOf(code);
    }

    /**
     * 尝试解析给定标签对应的地址。
     * <p>
     * 若标签包含包名（以 '.' 分隔），则尝试匹配简单名称后缀。
     * </p>
     *
     * @param label 函数标签或全限定名称
     * @return 已知地址，或 {@code null} 表示尚未定义
     */
    private Integer resolve(String label) {
        Integer addr = labelAddr.get(label);
        if (addr == null && label.contains(".")) {
            String simpleName = label.substring(label.lastIndexOf('.') + 1);
            addr = labelAddr.get(simpleName);
        }
        return addr;
    }

    /**
     * 修补指定函数名称对应的所有延迟调用，将占位符替换为实际地址。
     *
     * @param name 函数名称
     */
    private void patchFixesFor(String name) {
        for (Iterator<Fix> it = unresolved.iterator(); it.hasNext(); ) {
            Fix fix = it.next();
            if (fix.target.equals(name) || fix.target.endsWith("." + name)) {
                code.set(fix.index, VMOpCode.CALL + " " + pc + " " + fix.nArgs);
                it.remove();
            }
        }
    }
}
