package org.implab.gradle.common.exec.dsl;

import java.io.File;
import java.io.OutputStream;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.gradle.api.provider.Provider;
import org.gradle.util.Configurable;
import org.implab.gradle.common.core.lang.Closures;
import org.implab.gradle.common.exec.runtime.RedirectTo;

import groovy.lang.Closure;

@NonNullByDefault
public class RedirectToSpec implements Configurable<RedirectToSpec> {
    private Supplier<RedirectTo> streamRedirect;

    public boolean isRedirected() {
        return getRedirection().isPresent();
    }

    public Optional<RedirectTo> getRedirection() {
        return streamRedirect != null ? Optional.ofNullable(streamRedirect.get()) : Optional.empty();
    }

    public @Nullable RedirectTo getRedirectionOrNull() {
        return streamRedirect != null ? streamRedirect.get() : null;
    }

    public void toFile(File file) {
        streamRedirect = () -> RedirectTo.file(file);
    }

    public void toFile(Provider<File> fileProvider) {
        streamRedirect = fileProvider.map(RedirectTo::file)::getOrNull;
    }

    public void toStream(OutputStream stream) {
        streamRedirect = () -> RedirectTo.stream(stream);
    }

    public void toStream(Provider<OutputStream> streamProvider) {
        streamRedirect = streamProvider.map(RedirectTo::stream)::getOrNull;
    }

    public void to(Object output) {
        if (output instanceof Provider<?> outputProvider) {
            streamRedirect = outputProvider.map(RedirectTo::any)::get;
        } else {
            streamRedirect = () -> RedirectTo.any(output);
        }
    }

    public void eachLine(Consumer<String> consumer) {
        streamRedirect = () -> RedirectTo.eachLine(consumer);
    }

    public void allText(Consumer<String> consumer) {
        streamRedirect = () -> RedirectTo.allText(consumer);
    }

    public void discard() {
        streamRedirect = () -> null;
    }

    @Override
    public RedirectToSpec configure(Closure cl) {
        Closures.apply(cl, this);
        return this;
    }
}
