package jsqlite;

import java.io.*;

/**
 * Internal class implementing java.io.InputStream on
 * SQLite 3.4.0 incremental blob I/O interface.
 */

class BlobR extends InputStream {

    /**
     * Blob instance
     */

    private Blob blob;

    /**
     * Read position, file pointer.
     */

    private int pos;

    /**
     * Contruct InputStream from blob instance.
     */

    BlobR(Blob blob) {
        this.blob = blob;
        this.pos = 0;
    }

    /**
     * Return number of available bytes for reading.
     *
     * @return available input bytes
     */

    public int available() throws IOException {
        int ret = blob.size - pos;
        return (ret < 0) ? 0 : ret;
    }

    /**
     * Mark method; dummy to satisfy InputStream class.
     */

    public void mark(int limit) {
    }

    /**
     * Reset method; dummy to satisfy InputStream class.
     */

    public void reset() throws IOException {
    }

    /**
     * Mark support; not for this class.
     *
     * @return always false
     */

    public boolean markSupported() {
        return false;
    }

    /**
     * Close this blob InputStream.
     */

    public void close() throws IOException {
        blob.close();
        blob = null;
        pos = 0;
    }

    /**
     * Skip over blob data.
     */

    public long skip(long n) throws IOException {
        long ret = pos + n;
        if (ret < 0) {
            ret = 0;
            pos = 0;
        } else if (ret > blob.size) {
            ret = blob.size;
            pos = blob.size;
        } else {
            pos = (int) ret;
        }
        return ret;
    }

    /**
     * Read single byte from blob.
     *
     * @return byte read
     */

    public int read() throws IOException {
        byte b[] = new byte[1];
        int n = blob.read(b, 0, pos, b.length);
        if (n > 0) {
            pos += n;
            return b[0];
        }
        return -1;
    }

    /**
     * Read byte array from blob.
     *
     * @param b byte array to be filled
     * @return number of bytes read
     */

    public int read(byte b[]) throws IOException {
        int n = blob.read(b, 0, pos, b.length);
        if (n > 0) {
            pos += n;
            return n;
        }
        return -1;
    }

    /**
     * Read slice of byte array from blob.
     *
     * @param b   byte array to be filled
     * @param off offset into byte array
     * @param len length to be read
     * @return number of bytes read
     */

    public int read(byte b[], int off, int len) throws IOException {
        if (off + len > b.length) {
            len = b.length - off;
        }
        if (len < 0) {
            return -1;
        }
        if (len == 0) {
            return 0;
        }
        int n = blob.read(b, off, pos, len);
        if (n > 0) {
            pos += n;
            return n;
        }
        return -1;
    }
}

/**
 * Internal class implementing java.io.OutputStream on
 * SQLite 3.4.0 incremental blob I/O interface.
 */

class BlobW extends OutputStream {

    /**
     * Blob instance
     */

    private Blob blob;

    /**
     * Read position, file pointer.
     */

    private int pos;

    /**
     * Contruct OutputStream from blob instance.
     */

    BlobW(Blob blob) {
        this.blob = blob;
        this.pos = 0;
    }

    /**
     * Flush blob; dummy to satisfy OutputStream class.
     */

    public void flush() throws IOException {
    }

    /**
     * Close this blob OutputStream.
     */

    public void close() throws IOException {
        blob.close();
        blob = null;
        pos = 0;
    }

    /**
     * Write blob data.
     *
     * @param v byte to be written at current position.
     */

    public void write(int v) throws IOException {
        byte b[] = new byte[1];
        b[0] = (byte) v;
        pos += blob.write(b, 0, pos, 1);
    }

    /**
     * Write blob data.
     *
     * @param b byte array to be written at current position.
     */

    public void write(byte[] b) throws IOException {
        if (b != null && b.length > 0) {
            pos += blob.write(b, 0, pos, b.length);
        }
    }

    /**
     * Write blob data.
     *
     * @param b   byte array to be written.
     * @param off offset within byte array
     * @param len length of data to be written
     */

    public void write(byte[] b, int off, int len) throws IOException {
        if (b != null) {
            if (off + len > b.length) {
                len = b.length - off;
            }
            if (len <= 0) {
                return;
            }
            pos += blob.write(b, off, pos, len);
        }
    }
}

/**
 * Class to represent SQLite3 3.4.0 incremental blob I/O interface.
 *
 * Note, that all native methods of this class are
 * not synchronized, i.e. it is up to the caller
 * to ensure that only one thread is in these
 * methods at any one time.
 */

public class Blob {

    /**
     * Internal handle for the SQLite3 blob.
     */

    private long handle = 0;

    /**
     * Cached size of blob, setup right after blob
     * has been opened.
     */

    protected int size = 0;

    /**
     * Return InputStream for this blob
     *
     * @return InputStream
     */

    public InputStream getInputStream() {
        return (InputStream) new BlobR(this);
    }

    /**
     * Return OutputStream for this blob
     *
     * @return OutputStream
     */

    public OutputStream getOutputStream() {
        return (OutputStream) new BlobW(this);
    }

    /**
     * Close blob.
     */

    public native void close();

    /**
     * Internal blob write method.
     *
     * @param b   byte array to be written
     * @param off offset into byte array
     * @param pos offset into blob
     * @param len length to be written
     * @return number of bytes written to blob
     */

    native int write(byte[] b, int off, int pos, int len) throws IOException;

    /**
     * Internal blob read method.
     *
     * @param b   byte array to be written
     * @param off offset into byte array
     * @param pos offset into blob
     * @param len length to be written
     * @return number of bytes written to blob
     */

    native int read(byte[] b, int off, int pos, int len) throws IOException;

    /**
     * Destructor for object.
     */

    protected native void finalize();

    /**
     * Internal native initializer.
     */

    private static native void internal_init();

    static {
        internal_init();
    }
}
