/*
    Copyright 2019-2020 Dmitry Isaenko

    This file is part of NS-USBloader.

    NS-USBloader is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    NS-USBloader is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with NS-USBloader.  If not, see <https://www.gnu.org/licenses/>.
*/
package nsusbloader.com.helpers;

import java.io.*;

/**
 *  Handle Split files
 * */
public class NSSplitReader implements Closeable  {

    private final String splitFileDir;
    private final long referenceSplitChunkSize;

    private byte subFileNum;
    private long curPosition;
    private BufferedInputStream biStream;

    public NSSplitReader(File file, long seekToPosition) throws IOException, NullPointerException {
        this.splitFileDir = file.getAbsolutePath()+File.separator;
        File subFile = new File(file.getAbsolutePath()+File.separator+"00");
        if (! file.exists())
            throw new FileNotFoundException("File not found on "+file.getAbsolutePath()+File.separator+"00");
        this.referenceSplitChunkSize = subFile.length();
        this.subFileNum = (byte) (seekToPosition / referenceSplitChunkSize);
        this.biStream = new BufferedInputStream(new FileInputStream(splitFileDir + String.format("%02d", subFileNum)));
        this.curPosition = seekToPosition;

        seekToPosition -= referenceSplitChunkSize * subFileNum;

        if (seekToPosition != biStream.skip(seekToPosition))
            throw new IOException("Unable to seek to requested position of "+seekToPosition+" for file "+splitFileDir+String.format("%02d", subFileNum));
    }

    public long seek(long position) throws IOException{

        byte subFileRequested = (byte) (position / referenceSplitChunkSize);

        if ((subFileRequested != this.subFileNum) || (curPosition > position)) {
            biStream.close();
            biStream = new BufferedInputStream(new FileInputStream(splitFileDir + String.format("%02d", subFileRequested)));
            this.subFileNum = subFileRequested;
            this.curPosition = referenceSplitChunkSize * subFileRequested;
        }

        long retVal = biStream.skip(position - curPosition);

        retVal += curPosition;
        this.curPosition = position;
        return retVal;
    }

    public int read(byte[] readBuffer) throws IOException, NullPointerException {
        final int requested = readBuffer.length;
        int readPrtOne;

        if ( (curPosition + requested) <= (referenceSplitChunkSize * (subFileNum+1))) {
            if ((readPrtOne = biStream.read(readBuffer)) < 0 )
                return readPrtOne;
            curPosition += readPrtOne;
            return readPrtOne;
        }

        int partOne = (int) (referenceSplitChunkSize * (subFileNum+1) - curPosition);
        int partTwo = requested - partOne;
        int readPrtTwo;

        if ( (readPrtOne = biStream.read(readBuffer, 0, partOne)) < 0)
            return readPrtOne;

        curPosition += readPrtOne;

        if (readPrtOne != partOne)
            return readPrtOne;

        biStream.close();
        subFileNum += 1;
        biStream = new BufferedInputStream(new FileInputStream(splitFileDir + String.format("%02d", subFileNum)));

        if ( (readPrtTwo = biStream.read(readBuffer, partOne, partTwo) ) < 0)
            return readPrtTwo;

        curPosition += readPrtTwo;

        return readPrtOne + readPrtTwo;
    }

    @Override
    public void close() throws IOException {
        if (biStream != null)
            biStream.close();
    }
}
