package com.atlassian.bitbucket.mesh.git;

import com.atlassian.bitbucket.mesh.git.ref.AnnotatedTag;
import com.atlassian.bitbucket.mesh.git.ref.AnnotatedTagCallback;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcNonTag;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcResolveAnnotatedTagResponse;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcResolveAnnotatedTagResponseFragment;
import com.google.protobuf.ByteString;
import io.grpc.stub.StreamObserver;

import javax.annotation.Nonnull;

public class ObserverAnnotatedTagCallback
        extends AbstractObserverCallback<RpcResolveAnnotatedTagResponseFragment>
        implements AnnotatedTagCallback {

    private final RpcResolveAnnotatedTagResponseFragment.Builder fragment;

    public ObserverAnnotatedTagCallback(StreamObserver<RpcResolveAnnotatedTagResponseFragment> observer) {
        super(observer);

        fragment = RpcResolveAnnotatedTagResponseFragment.newBuilder();
    }

    @Override
    public void onMissing(@Nonnull String objectId) {
        queueForNext(RpcResolveAnnotatedTagResponse.newBuilder()
                .setMissing(ByteString.copyFromUtf8(objectId)));
    }

    @Override
    public void onNonTag(@Nonnull String objectId, @Nonnull GitObjectType type) {
        queueForNext(RpcResolveAnnotatedTagResponse.newBuilder()
                .setNonTag(RpcNonTag.newBuilder()
                        .setObjectId(ByteString.copyFromUtf8(objectId))
                        .setObjectType(type.toRpc())));
    }

    @Override
    public void onTag(@Nonnull AnnotatedTag tag) {
        queueForNext(RpcResolveAnnotatedTagResponse.newBuilder()
                .setTag(tag.toAnnotatedTag()));
    }

    void onEnd() {
        sendLastFragment();

        observer.onCompleted();
    }

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

    private void queueForNext(RpcResolveAnnotatedTagResponse.Builder builder) {
        fragment.addResponses(builder);
        if (fragment.getResponsesCount() >= FRAGMENT_SIZE) {
            callOnNext();
        }
    }

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