package com.atlassian.bitbucket.mesh.git.hosting;

import com.atlassian.bitbucket.mesh.rpc.v1.RpcStreamChunk;

import javax.annotation.Nonnull;

/**
 * Filter for {@code git-receive-pack} stdin, provided in {@link RpcStreamChunk} chunks. According to git's
 * <a href="https://git-scm.com/docs/pack-protocol">pack protocol</a>, stdin consists of a list of pkt-lines followed
 * by a PACK header and the pack contents.
 * <p>
 * A registered filter will receive {@link #onPacketLine callbacks for every pkt-line} as long as the filter returns
 * {@code true} from {@link #wantsPacketLines()}.
 * <p>
 * If the analyzed stdin stream also contains a PACK section, the pack section is provided to
 * {@link #onPack(RpcStreamChunk)} in chunks as long as the filter returns {@code true} from {@link #wantsPack()}.
 * <p>
 * Packet line and pack processing are independent of each other; a filter may signal interest in packet lines, pack
 * data or both through the {@link #wantsPacketLines()} and {@link #wantsPack()} methods.
 */
public interface GitReceivePackFilter {

    /**
     * Called when stdin does not follow git's pack protocol. When this method is called, no other methods will
     * be invoked on the filter.
     */
    default void onInvalid() {
    }

    /**
     * Called for all subsequent chunks once the start of the PACK section has been detected. The chunk that is
     * provided the first time this method is invoked will start with the PACK header. The final pack chunk will
     * have the {@link RpcStreamChunk#getClosed() closed flag} set to {@code true}.
     * <p>
     * If the method does not want to manipulate the pack chunk, it should return the provided chunk unchanged.
     * The return value of this method will be used as input to downstream filters or the stdin consumer if this
     * filter is the last in the chain.
     * <p>
     * Note: the provided chunk may be backed by a shared buffer which is recycled (at some point) after the method
     * completes. If the filter needs to cache the chunk or part of its data, or transfers it to another thread for
     * further processing, the filter MUST create a deep copy of the chunk.
     *
     * @param chunk the chunk with pack data
     * @return the chunk to provide to downstream filters and/or the stdin consumer
     */
    @Nonnull
    default RpcStreamChunk onPack(@Nonnull RpcStreamChunk chunk) {
        return chunk;
    }

    /**
     * Called for every detected packet line. The provided chunk will contain exactly one packet line, including
     * the 4 byte packet line prefix that encodes the packet line length. If there is pack section, no packet line
     * chunk will have the {@link RpcStreamChunk#getClosed() closed flag} set to {@code true}; otherwise, the last
     * packet line chunk will be marked as closed.
     * <p>
     * If the method does not want to manipulate the packet line, it should return the provided chunk unchanged.
     * The return value of this method will be used as input to downstream filters or the stdin consumer if this
     * filter is the last in the chain.
     * <p>
     * Note: the provided chunk may be backed by a shared buffer which is recycled (at some point) after the method
     * completes. If the filter needs to cache the chunk or part of its data, or transfers it to another thread for
     * further processing, the filter MUST create a deep copy of the chunk.
     *
     * @param chunk the chunk a single packet line
     * @return the chunk to provide to downstream filters and/or the stdin consumer
     */
    @Nonnull
    default RpcStreamChunk onPacketLine(@Nonnull RpcStreamChunk chunk) {
        return chunk;
    }

    /**
     * Called to check whether the filter wants to receive pack chunks. When this method returns {@code false}, it
     * signals to the system that the filter is no longer interested in pack chunks. The system may or may not check
     * this method again once it has returned {@code false}.
     *
     * @return {@code true} if the filter wants to receive pack chunks
     */
    default boolean wantsPack() {
        return false;
    }

    /**
     * Called to check whether the filter wants to receive pack lines. When this method returns {@code false}, it
     * signals to the system that the filter is no longer interested in packet lines. The system may or may not check
     * this method again once it has returned {@code false}.
     *
     * @return {@code true} if the filter wants to receive git packet lines
     */
    default boolean wantsPacketLines() {
        return false;
    }
}
