package com.atlassian.bitbucket.internal.tag.idx;

import com.atlassian.bitbucket.repository.Ref;
import com.atlassian.bitbucket.repository.Tag;
import com.atlassian.bitbucket.repository.TagCallback;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.OutputStream;

/**
 * Encapsulates functionality for writing tags to the on disk cache.
 */
public class TagIndexWriter implements TagCallback {
    private final OutputStream target;

    public TagIndexWriter(OutputStream target) {
        this.target = target;
    }

    @Override
    public boolean onTag(@Nonnull Tag tag) throws IOException {
        writeRef(tag);
        return true; // true means continue to stream more tags if available
    }

    private void writeRef(@Nonnull Ref ref) {
        // The output is a stream of refs with each ref encoded as follow:
        // - The ref's commit ID (fixed-length field, 20 bytes)
        // - The ref's display ID (variable-length field, terminated by a special byte that cannot be present in the ID)
        // - The ref's absolute ID (variable-length field, terminated by a different, to the above mentioned separator,
        //   special byte that cannot be present in the ID)
        try {
            target.write(Hex.decodeHex(ref.getLatestCommit().toCharArray()));
            target.write(ref.getDisplayId().getBytes(TagIndexConstants.ENCODING));
            target.write(TagIndexConstants.ID_SEPERATOR);
            target.write(ref.getId().getBytes(TagIndexConstants.ENCODING));
            target.write(TagIndexConstants.REF_SEPERATOR);
        } catch (DecoderException e) {
            throw new IllegalArgumentException("Failed to hex decode " + ref.getLatestCommit(), e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}