package us.shandian.giga.io;

import org.schabi.newpipe.streams.io.SharpStream;

import java.io.IOException;

public class ChunkFileInputStream extends SharpStream {
    private static final int REPORT_INTERVAL = 256 * 1024;

    private SharpStream source;
    private final long offset;
    private final long length;
    private long position;

    private long progressReport;
    private final ProgressReport onProgress;

    public ChunkFileInputStream(SharpStream target, long start, long end, ProgressReport callback) throws IOException {
        source = target;
        offset = start;
        length = end - start;
        position = 0;
        onProgress = callback;
        progressReport = REPORT_INTERVAL;

        if (length < 1) {
            source.close();
            throw new IOException("The chunk is empty or invalid");
        }
        if (source.length() < end) {
            try {
                throw new IOException(String.format("invalid file length. expected = %s  found = %s", end, source.length()));
            } finally {
                source.close();
            }
        }

        source.seek(offset);
    }

    /**
     * Get absolute position on file
     *
     * @return the position
     */
    public long getFilePointer() {
        return offset + position;
    }

    @Override
    public int read() throws IOException {
        if ((position + 1) > length) {
            return 0;
        }

        int res = source.read();
        if (res >= 0) {
            position++;
        }

        return res;
    }

    @Override
    public int read(byte[] b) throws IOException {
        return read(b, 0, b.length);
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        if ((position + len) > length) {
            len = (int) (length - position);
        }
        if (len == 0) {
            return 0;
        }

        int res = source.read(b, off, len);
        position += res;

        if (onProgress != null && position > progressReport) {
            onProgress.report(position);
            progressReport = position + REPORT_INTERVAL;
        }

        return res;
    }

    @Override
    public long skip(long pos) throws IOException {
        pos = Math.min(pos + position, length);

        if (pos == 0) {
            return 0;
        }

        source.seek(offset + pos);

        long oldPos = position;
        position = pos;

        return pos - oldPos;
    }

    @Override
    public long available() {
        return (int) (length - position);
    }

    @SuppressWarnings("EmptyCatchBlock")
    @Override
    public void close() {
        source.close();
        source = null;
    }

    @Override
    public boolean isClosed() {
        return source == null;
    }

    @Override
    public void rewind() throws IOException {
        position = 0;
        source.seek(offset);
    }

    @Override
    public boolean canRewind() {
        return true;
    }

    @Override
    public boolean canRead() {
        return true;
    }

    @Override
    public boolean canWrite() {
        return false;
    }

    @Override
    public void write(byte value) {
    }

    @Override
    public void write(byte[] buffer) {
    }

    @Override
    public void write(byte[] buffer, int offset, int count) {
    }

}
