package de.tu_darmstadt.stg.mubench.cli;

import de.tu_darmstadt.stg.yaml.YamlEntity;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;

/**
 * Interfaces between the MUBench Pipeline and detector implementations. For a usage example
 * see {@link de.tu_darmstadt.stg.mubench.demo.DemoRunner}.
 */
@SuppressWarnings("WeakerAccess")
public final class MuBenchRunner {
    private DetectionStrategy detectOnlyStrategy;
    private DetectionStrategy mineAndDetectStrategy;

    /**
     * @param detectOnlyStrategy Run detection in detect-only mode. Should use {@link DetectorArgs#getTrainingSrcPaths()}
     *                           or {@link DetectorArgs#getTrainingClassPath()} to extract patterns from the provided
     *                           correct usages and identify respective violations in
     *                           {@link DetectorArgs#getTargetSrcPaths()} or {@link DetectorArgs#getTargetClassPath()},
     *                           respectively.
     * @return this runner, for fluent calls
     */
    public MuBenchRunner withDetectOnlyStrategy(DetectionStrategy detectOnlyStrategy) {
        this.detectOnlyStrategy = detectOnlyStrategy;
        return this;
    }

    /**
     * @param mineAndDetectStrategy Run detection in mine-and-detect mode. Should identify misuses in
     *                              {@link DetectorArgs#getTargetSrcPaths()} or
     *                              {@link DetectorArgs#getTargetClassPath()}.
     *                              May use {@link DetectorArgs#getTargetSrcPaths()} and
     *                              {@link DetectorArgs#getTargetClassPath()} for pattern mining.
     * @return this runner, for fluent calls
     */
    public MuBenchRunner withMineAndDetectStrategy(DetectionStrategy mineAndDetectStrategy) {
        this.mineAndDetectStrategy = mineAndDetectStrategy;
        return this;
    }

    /**
     * Invoke the runner.
     *
     * @param args the command-line arguments passed from the MUBench Pipeline.
     * @throws Exception on errors
     */
    @SuppressWarnings("unused")
    public void run(String[] args) throws Exception {
        DetectorArgs detectorArgs = DetectorArgs.parse(args);
        DetectorOutput output = detect(detectorArgs);
        report(output.getFindings(), detectorArgs.getFindingsFile());
        report(output.getRunInfo(), detectorArgs.getRunInfoFile());
    }

    private DetectorOutput detect(DetectorArgs args) throws Exception {
        switch (args.getDetectorMode()) {
            case DETECT_ONLY:
                return detect(detectOnlyStrategy, args);
            case MINE_AND_DETECT:
                return detect(mineAndDetectStrategy, args);
            default:
                throw new IllegalArgumentException("Unsupported run mode: " + args.getDetectorMode());
        }
    }

    private DetectorOutput detect(DetectionStrategy strategy, DetectorArgs args) throws Exception {
        if (strategy == null) {
            throw new UnsupportedOperationException("detector mode not supported");
        }
        DetectorOutput.Builder builder = new DetectorOutput.Builder();
        return strategy.detectViolations(args, builder);
    }

    private void report(YamlEntity entity, Path findingsFile) throws IOException {
        try (OutputStream os = Files.newOutputStream(findingsFile)) {
            entity.write(os);
        }
    }
}
