/*
 * Copyright 2014 Florian Müller & Jay Brown
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *
 * This code is based on the Apache Chemistry OpenCMIS FileShare project
 * <http://chemistry.apache.org/java/developing/repositories/dev-repositories-fileshare.html>.
 *
 * It is part of a training exercise and not intended for production use!
 *
 */
package org.example.cmis.server;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;

import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;

public class ContentRangeInputStream extends FilterInputStream {

    private static final int BUFFER_SIZE = 4096;

    private long offset;
    private long length;
    private long remaining;

    public ContentRangeInputStream(InputStream stream, BigInteger offset, BigInteger length) {
        super(stream);

        this.offset = offset != null ? offset.longValue() : 0;
        this.length = length != null ? length.longValue() : Long.MAX_VALUE;

        this.remaining = this.length;

        if (this.offset > 0) {
            skipBytes();
        }
    }

    private void skipBytes() {
        long remainingSkipBytes = offset;

        try {
            while (remainingSkipBytes > 0) {
                long skipped = super.skip(remainingSkipBytes);
                remainingSkipBytes -= skipped;

                if (skipped == 0) {
                    // stream might not support skipping
                    skipBytesByReading(remainingSkipBytes);
                    break;
                }
            }
        } catch (IOException e) {
            throw new CmisRuntimeException("Skipping the stream failed!", e);
        }
    }

    private void skipBytesByReading(long remainingSkipBytes) {
        try {
            final byte[] buffer = new byte[BUFFER_SIZE];
            while (remainingSkipBytes > 0) {
                long skipped = super.read(buffer, 0, (int) Math.min(buffer.length, remainingSkipBytes));
                if (skipped == -1) {
                    break;
                }

                remainingSkipBytes -= skipped;
            }
        } catch (IOException e) {
            throw new CmisRuntimeException("Reading the stream failed!", e);
        }
    }

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

    @Override
    public long skip(long n) throws IOException {
        if (remaining <= 0) {
            return 0;
        }

        long skipped = super.skip(n);
        remaining -= skipped;

        return skipped;
    }

    @Override
    public int available() throws IOException {
        if (remaining <= 0) {
            return 0;
        }

        int avail = super.available();

        if (remaining < avail) {
            return (int) remaining;
        }

        return avail;
    }

    @Override
    public int read() throws IOException {
        if (remaining <= 0) {
            return -1;
        }

        remaining--;

        return super.read();
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        if (remaining <= 0) {
            return -1;
        }

        int readBytes = super.read(b, off, (int) Math.min(len, remaining));
        if (readBytes == -1) {
            return -1;
        }

        remaining -= readBytes;

        return readBytes;
    }

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