package org.implab.gradle.common.exec;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Scanner;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
 * RedirectSpec
 */
@NonNullByDefault
public interface RedirectTo {
    CompletableFuture<Void> redirect(InputStream from);

    public interface StringConsumer extends Consumer<String> {
    }

    public static RedirectTo consumer(final Consumer<String> consumer) {
        return consumer(new StringConsumer() {
            @Override
            public void accept(String s) {
                consumer.accept(s);
            }
        });
    }

    public static RedirectTo consumer(final StringConsumer consumer) {
        return (src) -> CompletableFuture.runAsync(() -> {
            try (Scanner sc = new Scanner(src)) {
                while (sc.hasNextLine()) {
                    consumer.accept(sc.nextLine());
                }
            }
        });
    }

    public static RedirectTo file(final File file) {
        return src -> CompletableFuture.runAsync(() -> {
            try (OutputStream out = new FileOutputStream(file)) {
                src.transferTo(out);
            } catch (Exception e) {
                // silence!
            }
        });
    }

    public static RedirectTo stream(final OutputStream dest) {
        return src -> CompletableFuture.runAsync(() -> {
            try (dest; src) {
                src.transferTo(dest);
            } catch (Exception e) {
                // silence!
            }
        });
    }

    public static RedirectTo any(final Object output) {
        if (output instanceof StringConsumer fn) {
            return consumer(s -> fn.accept(s));
        } else if (output instanceof File f) {
            return file(f);
        } else if (output instanceof OutputStream stm) {
            return stream(stm);
        } else if (output instanceof RedirectTo self) {
            return self;
        } else {
            throw new IllegalArgumentException("The specified argument type isn't supported: " + output.getClass());
        }
    }
}