package com.atlassian.bitbucket.mesh.git;

import com.atlassian.bitbucket.mesh.git.content.Directory;
import com.atlassian.bitbucket.mesh.git.content.DirectoryCallback;
import com.atlassian.bitbucket.mesh.git.content.File;
import com.atlassian.bitbucket.mesh.git.submodule.Submodule;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcListDirectoryResponse;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcListDirectoryResponseFragment;
import io.grpc.stub.StreamObserver;

import javax.annotation.Nonnull;

class ObserverDirectoryCallback
        extends AbstractObserverCallback<RpcListDirectoryResponseFragment>
        implements DirectoryCallback {

    private final RpcListDirectoryResponseFragment.Builder fragment;

    ObserverDirectoryCallback(@Nonnull StreamObserver<RpcListDirectoryResponseFragment> observer) {
        super(observer);

        this.fragment = RpcListDirectoryResponseFragment.newBuilder();
    }

    @Override
    public boolean onDirectory(@Nonnull Directory directory) {
        return queueForNext(RpcListDirectoryResponse.newBuilder()
                .setDirectory(directory.toDirectory()));
    }

    @Override
    public boolean onFile(@Nonnull File file) {
        return queueForNext(RpcListDirectoryResponse.newBuilder()
                .setFile(file.toFile()));
    }

    @Override
    public boolean onSubmodule(@Nonnull Submodule submodule) {
        return queueForNext(RpcListDirectoryResponse.newBuilder()
                .setSubmodule(submodule.toSubmodule()));
    }

    void onEnd() {
        sendLastFragment();
        observer.onCompleted();
    }

    private boolean queueForNext(RpcListDirectoryResponse.Builder builder) {
        fragment.addResponses(builder);
        if (fragment.getResponsesCount() >= FRAGMENT_SIZE) {
            sendFragment();
        }

        return !isCanceled();
    }

    private void sendFragment() {
        observer.onNext(fragment.build());
        fragment.clearResponses();
    }

    private void sendLastFragment() {
        if (fragment.getResponsesCount() > 0) {
            sendFragment();
        }
    }
}
