package SQLite;

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;
    }

    @Override
    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();
    }
}
