package p.studio.compiler.workspaces.stages;

import lombok.extern.slf4j.Slf4j;
import p.studio.compiler.backend.irvm.IRVMLoweringException;
import p.studio.compiler.backend.irvm.IRVMValidationException;
import p.studio.compiler.backend.irvm.LowerToIRVMService;
import p.studio.compiler.messages.BuildingIssueSink;
import p.studio.compiler.models.BuilderPipelineContext;
import p.studio.compiler.workspaces.PipelineStage;
import p.studio.utilities.logs.LogAggregator;

@Slf4j
public class LowerToIRVMPipelineStage implements PipelineStage {
    private final LowerToIRVMService lowerToIRVMService;

    public LowerToIRVMPipelineStage() {
        this(new LowerToIRVMService());
    }

    LowerToIRVMPipelineStage(final LowerToIRVMService lowerToIRVMService) {
        this.lowerToIRVMService = lowerToIRVMService;
    }

    @Override
    public BuildingIssueSink run(BuilderPipelineContext ctx, LogAggregator logs) {
        if (ctx.irBackend == null) {
            return BuildingIssueSink.empty()
                    .report(builder -> builder
                            .error(true)
                            .phase("BACKEND_LOWER_TO_IRVM")
                            .code("MARSHAL_FORMAT_STAGE_INPUT_MISSING")
                            .message("[BUILD]: IRBackend is missing before LowerToIRVM stage"));
        }
        try {
            ctx.irvm = lowerToIRVMService.lower(ctx.irBackend, ctx.config.vmProfile());
        } catch (IRVMLoweringException e) {
            return BuildingIssueSink.empty()
                    .report(builder -> builder
                            .error(true)
                            .phase("BACKEND_LOWER_TO_IRVM")
                            .code(e.code().name())
                            .fileId(e.fileId())
                            .start(e.start())
                            .end(e.end())
                            .message("[BUILD]: lower to irvm failed: " + e.getMessage())
                            .exception(e));
        } catch (IRVMValidationException e) {
            return BuildingIssueSink.empty()
                    .report(builder -> builder
                            .error(true)
                            .phase("BACKEND_LOWER_TO_IRVM")
                            .code(e.code().name())
                            .functionIndex(e.functionIndex())
                            .pc(e.pc())
                            .message("[BUILD]: lower to irvm validation failed: " + e.getMessage())
                            .exception(e));
        }
        return BuildingIssueSink.empty();
    }
}
