package com.atlassian.bitbucket.io;

import com.atlassian.bitbucket.scm.CommandOutputHandler;
import com.atlassian.utils.process.BaseOutputHandler;
import com.atlassian.utils.process.ProcessException;

import java.io.*;

/**
 * OutputHandler that tries to detect the content type of the data before copying it the content into and OutputStream
 * obtained from the given TypeAwareOutputSupplier.
 */
public class TypeDetectingCopyOutputHandler extends BaseOutputHandler implements CommandOutputHandler<Void> {

    private static final int BUFFER_SIZE = 8192;

    private TypeAwareOutputSupplier outputSupplier;
    private int bufferSize;
    private String path;

    public TypeDetectingCopyOutputHandler(TypeAwareOutputSupplier outputSupplier, String path) {
        this(outputSupplier, path, 1024);
    }

    public TypeDetectingCopyOutputHandler(TypeAwareOutputSupplier outputSupplier, String path, int bufferSize) {
        this.bufferSize = bufferSize;
        this.outputSupplier = outputSupplier;
        this.path = path;
    }

    @Override
    public Void getOutput() {
        return null;
    }

    @Override
    public void process(InputStream output) throws ProcessException {
        try {
            BufferedInputStream bufferedInput = new BufferedInputStream(output, BUFFER_SIZE);

            String type = ContentDetectionUtils.detectContentType(bufferedInput, path);

            if (type == null) {
                type = ContentDetectionUtils.isBinary(bufferedInput, BUFFER_SIZE) ?
                        "application/octet-stream" :
                        "text/plain";
            }

            OutputStream outputStream = outputSupplier.getStream(type);
            copyContent(bufferedInput, outputStream);
        } catch (InterruptedIOException e) {
            // This means the process was asked to stop which can be normal so we just finish
        } catch (IOException e) {
            throw new ProcessException(e);
        }
    }

    private void copyContent(BufferedInputStream output, OutputStream dest) throws IOException {
        byte[] buffer = new byte[bufferSize];
        int num;
        while ((num = output.read(buffer)) != -1) {
            resetWatchdog();
            dest.write(buffer, 0, num);
        }
    }
}
