package com.atlassian.bitbucket.mesh.git;

import com.atlassian.bitbucket.mesh.git.builder.GitProcessBuilderSupport;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcStreamChunk;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
 * Defines a mechanism for parsing request fragments. {@link #createBuilder} is used to parse the first fragment
 * received and start the process, and then subsequent fragments are expected to be {@link RpcStreamChunk chunks} of
 * {@code stdin}.
 *
 * @param <ReqT> the fragment request type
 */
interface RpcFragmentHandler<ReqT, RespT> extends RpcResponseFragmentFactory<RespT> {

    /**
     * Creates a <i>fully configured</i> builder for the provided fragment.
     * <p>
     * This method will be called for the <i>first fragment</i> received from an interactive client. It is expected
     * to be a "request" fragment, which can be used to start a process.
     *
     * @param fragment the <i>first</i> fragment received
     * @return a <i>fully configured</i> builder
     */
    @Nonnull
    GitProcessBuilderSupport<?> createBuilder(@Nonnull ReqT fragment);

    /**
     * Signals to the fragment handler that the request has been completed successfully.
     */
    default void onComplete() {
        //no-op
    }

    /**
     * Signals to the fragment handler that the request has completed unsuccessfully and that the handler
     * should release any resources and wrap up processing of data.
     *
     * @param canceled {@code true} if the request was cancelled regularly, {@code false} if not
     * @param t the associated exception
     */
    default void onError(boolean canceled, Throwable t) {
        //no-op
    }

    /**
     * Called when a fragment is received after the first fragment has been processed and the process has been started.
     * <p>
     * This method will be called for every fragment after the first. Once the "request" fragment has been received,
     * subsequent fragments are expected to be "stdin" fragments, possibly followed by hook related fragments.
     * The "final" stdin fragment should close {@code stdin}.
     * <p>
     * If this method returns a {@link RpcStreamChunk}, the bytes contained in the chunk will be written to the process
     * stdin, and if the {@link RpcStreamChunk#getClosed() closed flag} is set to {@code true}, the process stdin will
     * be closed.
     * <p>
     * Implementations are expected to handle non-stdin fragments and return {@code null}.
     *
     * @param fragment the fragment
     * @return the fragment's {@code stdin} chunk, or {@code null} if no stdin was received.
     */
    @Nullable
    RpcStreamChunk onFragment(@Nonnull ReqT fragment);
}
