package com.atlassian.bitbucket.mesh.git.builder.revlist;

import com.atlassian.bitbucket.mesh.git.commit.Commit;
import com.atlassian.bitbucket.mesh.git.commit.CommitCallback;
import com.atlassian.bitbucket.mesh.git.content.MinimalChange;
import com.atlassian.bitbucket.mesh.process.OsProcess;
import com.atlassian.bitbucket.mesh.process.handler.LineEncoder;
import com.google.common.collect.Iterables;

import javax.annotation.Nonnull;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.stream.Stream;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * An implementation of {@link AbstractCommitRevListStdioHandler} which streams {@link Commit}s to a
 * {@link CommitCallback}.
 */
public class CallbackRevListStdioHandler
        extends AbstractCommitRevListStdioHandler<Void> {

    private final CommitCallback callback;
    private final LineEncoder stdinEncoder;

    private int toSkip;

    public CallbackRevListStdioHandler(CommitCallback callback, boolean withMessages, int maxMessageLength,
                                       Collection<String> paths) {
        this(callback, withMessages, maxMessageLength, paths, null);
    }

    public CallbackRevListStdioHandler(CommitCallback callback, boolean withMessages, int maxMessageLength,
                                       Collection<String> paths, Stream<String> stdin) {
        super(withMessages, !paths.isEmpty(), maxMessageLength, Iterables.getFirst(paths, null));

        this.callback = callback;

        stdinEncoder = stdin == null ? null : new LineEncoder(stdin, UTF_8);
    }

    @Override
    public Void getResult() {
        return null;
    }

    public void setSkip(int skip) {
        toSkip = skip;
    }

    @Override
    protected boolean onCommit(Commit commit, MinimalChange rename) {
        if (toSkip > 0) {
            toSkip--;
            return true;
        }

        return callback.onCommit(commit, rename);
    }

    @Override
    public void onStart(@Nonnull OsProcess process) {
        if (stdinEncoder != null && stdinEncoder.hasMore()) {
            process.wantWrite();
        } else {
            //stdinEncoder is null, or empty
            process.closeStdin(false);
        }
    }

    @Override
    public boolean onStdinReady(@Nonnull ByteBuffer buffer) {
        if (stdinEncoder.encodeTo(buffer)) {
            return true;
        }
        closeStdin();
        return false;
    }
}