package aQute.remote.agent;

import java.io.IOException;
import java.io.InputStream;

/**
 * An filter stream that takes a string from the supervisor and then provides it
 * as a read to the user of this Input Stream. It also dispatches the text to an
 * original stream.It does not use Piped*Stream because they turned hard to
 * break. This implementation uses a ring buffer.
 */
public class RedirectInput extends InputStream {

	private InputStream	org;
	private byte[]		ring	= new byte[65536];
	private int			in, out;

	/**
	 * Create a redirector input stream with an original input stream
	 *
	 * @param in the original
	 */
	public RedirectInput(InputStream in) throws IOException {
		this.org = in;
	}

	/**
	 * Create a redirector without an original
	 */
	public RedirectInput() {}

	/**
	 * Get the original inputstream, potentially null
	 *
	 * @return null or the original input stream
	 */
	public InputStream getOrg() {
		return org;
	}

	/**
	 * Provide the string that should be treated as input for the running code.
	 *
	 * @param s the string
	 */
	public synchronized void add(String s) throws IOException {
		byte[] bytes = s.getBytes();
		for (byte element : bytes) {
			write(element);
		}
	}

	/**
	 * Write to the ring buffer
	 */
	private void write(byte b) {
		synchronized (ring) {
			ring[in] = b;

			in = (in + 1) % ring.length;
			if (in == out) {
				// skip oldest output
				out = (out + 1) % ring.length;
			}
			ring.notifyAll();
		}
	}

	@Override
	public void close() {
		// ignore
	}

	/**
	 * Read a byte from the input buffer. We will be fully interruptible, in the
	 * case of an interrupt we return -1 (eof)
	 */
	@Override
	public int read() throws IOException {
		System.out.flush();
		synchronized (ring) {
			while (in == out) {
				try {
					ring.wait(400);
				} catch (InterruptedException e) {
					return -1;
				}
			}
			int c = 0xFF & ring[out];
			out = (out + 1) % ring.length;
			return c;
		}
	}

	/**
	 * And the read for a larger buffer
	 */
	@Override
	public int read(byte[] buffer, int offset, int length) throws IOException {
		int n = 0;
		for (int i = offset; i < length; i++) {
			int c = read();
			if (c < 0)
				break;
			buffer[i] = (byte) (0xFF & c);
			n++;

			if (c == '\n')
				break;
		}
		return n;
	}

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