package tlschannel.async;

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousByteChannel;
import java.nio.channels.CompletionHandler;
import java.nio.channels.InterruptedByTimeoutException;
import java.nio.channels.ReadPendingException;
import java.nio.channels.ShutdownChannelGroupException;
import java.nio.channels.WritePendingException;
import java.util.concurrent.TimeUnit;

/**
 * This interface extends {@link AsynchronousByteChannel} adding optional timeouts and scattering
 * and gathering methods. These additions are analogous to the ones made by {@link
 * java.nio.channels.AsynchronousSocketChannel}.
 */
public interface ExtendedAsynchronousByteChannel extends AsynchronousByteChannel {

    /**
     * Reads a sequence of bytes from this channel into the given buffer.
     *
     * <p>This method initiates an asynchronous read operation to read a sequence of bytes from this
     * channel into the given buffer. The {@code handler} parameter is a completion handler that is
     * invoked when the read operation completes (or fails). The result passed to the completion
     * handler is the number of bytes read or {@code -1} if no bytes could be read because the channel
     * has reached end-of-stream.
     *
     * <p>If a timeout is specified and the timeout elapses before the operation completes then the
     * operation completes with the exception {@link InterruptedByTimeoutException}. Where a timeout
     * occurs, and the implementation cannot guarantee that bytes have not been read, or will not be
     * read from the channel into the given buffer, then further attempts to read from the channel
     * will cause an unspecific runtime exception to be thrown.
     *
     * <p>Otherwise this method works in the same manner as the {@link
     * AsynchronousByteChannel#read(ByteBuffer,Object,CompletionHandler)} method.
     *
     * @param   <A> The type for the object to the attached to the operation
     * @param dst The buffer into which bytes are to be transferred
     * @param timeout The maximum time for the I/O operation to complete
     * @param unit The time unit of the {@code timeout} argument
     * @param attach The object to attach to the I/O operation; can be {@code null}
     * @param handler The handler for consuming the result
     * @throws IllegalArgumentException If the buffer is read-only
     * @throws ReadPendingException If a read operation is already in progress on this channel
     * @throws ShutdownChannelGroupException If the channel group has terminated
     */
    <A> void read(ByteBuffer dst, long timeout, TimeUnit unit, A attach, CompletionHandler<Integer, ? super A> handler);

    /**
     * Reads a sequence of bytes from this channel into a subsequence of the given buffers. This
     * operation, sometimes called a <em>scattering read</em>, is often useful when implementing
     * network protocols that group data into segments consisting of one or more fixed-length headers
     * followed by a variable-length body. The {@code handler} parameter is a completion handler that
     * is invoked when the read operation completes (or fails). The result passed to the completion
     * handler is the number of bytes read or {@code -1} if no bytes could be read because the channel
     * has reached end-of-stream.
     *
     * <p>This method initiates a read of up to <i>r</i> bytes from this channel, where <i>r</i> is
     * the total number of bytes remaining in the specified subsequence of the given buffer array,
     * that is,
     *
     * <blockquote>
     *
     * <pre>
     * dsts[offset].remaining()
     *     + dsts[offset+1].remaining()
     *     + ... + dsts[offset+length-1].remaining()</pre>
     *
     * </blockquote>
     *
     * at the moment that the read is attempted.
     *
     * <p>Suppose that a byte sequence of length <i>n</i> is read, where <code>0</code>&nbsp;<code>
     * &lt;</code>&nbsp;<i>n</i>&nbsp;<code>&lt;=</code>&nbsp;<i>r</i>. Up to the first <code>
     * dsts[offset].remaining()</code> bytes of this sequence are transferred into buffer <code>
     * dsts[offset]</code>, up to the next <code>dsts[offset+1].remaining()</code> bytes are
     * transferred into buffer <code>dsts[offset+1]</code>, and so forth, until the entire byte
     * sequence is transferred into the given buffers. As many bytes as possible are transferred into
     * each buffer, hence the final position of each updated buffer, except the last updated buffer,
     * is guaranteed to be equal to that buffer's limit. The underlying operating system may impose a
     * limit on the number of buffers that may be used in an I/O operation. Where the number of
     * buffers (with bytes remaining), exceeds this limit, then the I/O operation is performed with
     * the maximum number of buffers allowed by the operating system.
     *
     * <p>If a timeout is specified and the timeout elapses before the operation completes then it
     * completes with the exception {@link InterruptedByTimeoutException}. Where a timeout occurs, and
     * the implementation cannot guarantee that bytes have not been read, or will not be read from the
     * channel into the given buffers, then further attempts to read from the channel will cause an
     * unspecific runtime exception to be thrown.
     *
     * @param   <A> The type for the object to the attached to the operation
     * @param dsts The buffers into which bytes are to be transferred
     * @param offset The offset within the buffer array of the first buffer into which bytes are to be
     *     transferred; must be non-negative and no larger than {@code dsts.length}
     * @param length The maximum number of buffers to be accessed; must be non-negative and no larger
     *     than {@code dsts.length - offset}
     * @param timeout The maximum time for the I/O operation to complete
     * @param unit The time unit of the {@code timeout} argument
     * @param attach The object to attach to the I/O operation; can be {@code null}
     * @param handler The handler for consuming the result
     * @throws IndexOutOfBoundsException If the pre-conditions for the {@code offset} and {@code
     *     length} parameter aren't met
     * @throws IllegalArgumentException If the buffer is read-only
     * @throws ReadPendingException If a read operation is already in progress on this channel
     * @throws ShutdownChannelGroupException If the channel group has terminated
     */
    <A> void read(
            ByteBuffer[] dsts,
            int offset,
            int length,
            long timeout,
            TimeUnit unit,
            A attach,
            CompletionHandler<Long, ? super A> handler);

    /**
     * Writes a sequence of bytes to this channel from the given buffer.
     *
     * <p>This method initiates an asynchronous write operation to write a sequence of bytes to this
     * channel from the given buffer. The {@code handler} parameter is a completion handler that is
     * invoked when the write operation completes (or fails). The result passed to the completion
     * handler is the number of bytes written.
     *
     * <p>If a timeout is specified and the timeout elapses before the operation completes then it
     * completes with the exception {@link InterruptedByTimeoutException}. Where a timeout occurs, and
     * the implementation cannot guarantee that bytes have not been written, or will not be written to
     * the channel from the given buffer, then further attempts to write to the channel will cause an
     * unspecific runtime exception to be thrown.
     *
     * <p>Otherwise this method works in the same manner as the {@link
     * AsynchronousByteChannel#write(ByteBuffer,Object,CompletionHandler)} method.
     *
     * @param   <A> The type for the object to the attached to the operation
     * @param src The buffer from which bytes are to be retrieved
     * @param timeout The maximum time for the I/O operation to complete
     * @param unit The time unit of the {@code timeout} argument
     * @param attach The object to attach to the I/O operation; can be {@code null}
     * @param handler The handler for consuming the result
     * @throws WritePendingException If a write operation is already in progress on this channel
     * @throws ShutdownChannelGroupException If the channel group has terminated
     */
    <A> void write(
            ByteBuffer src, long timeout, TimeUnit unit, A attach, CompletionHandler<Integer, ? super A> handler);

    /**
     * Writes a sequence of bytes to this channel from a subsequence of the given buffers. This
     * operation, sometimes called a <em>gathering write</em>, is often useful when implementing
     * network protocols that group data into segments consisting of one or more fixed-length headers
     * followed by a variable-length body. The {@code handler} parameter is a completion handler that
     * is invoked when the write operation completes (or fails). The result passed to the completion
     * handler is the number of bytes written.
     *
     * <p>This method initiates a write of up to <i>r</i> bytes to this channel, where <i>r</i> is the
     * total number of bytes remaining in the specified subsequence of the given buffer array, that
     * is,
     *
     * <blockquote>
     *
     * <pre>
     * srcs[offset].remaining()
     *     + srcs[offset+1].remaining()
     *     + ... + srcs[offset+length-1].remaining()</pre>
     *
     * </blockquote>
     *
     * at the moment that the write is attempted.
     *
     * <p>Suppose that a byte sequence of length <i>n</i> is written, where <code>0</code>&nbsp;<code>
     * &lt;</code>&nbsp;<i>n</i>&nbsp;<code>&lt;=</code>&nbsp;<i>r</i>. Up to the first <code>
     * srcs[offset].remaining()</code> bytes of this sequence are written from buffer <code>
     * srcs[offset]</code>, up to the next <code>srcs[offset+1].remaining()</code> bytes are written
     * from buffer <code>srcs[offset+1]</code>, and so forth, until the entire byte sequence is
     * written. As many bytes as possible are written from each buffer, hence the final position of
     * each updated buffer, except the last updated buffer, is guaranteed to be equal to that buffer's
     * limit. The underlying operating system may impose a limit on the number of buffers that may be
     * used in an I/O operation. Where the number of buffers (with bytes remaining), exceeds this
     * limit, then the I/O operation is performed with the maximum number of buffers allowed by the
     * operating system.
     *
     * <p>If a timeout is specified and the timeout elapses before the operation completes then it
     * completes with the exception {@link InterruptedByTimeoutException}. Where a timeout occurs, and
     * the implementation cannot guarantee that bytes have not been written, or will not be written to
     * the channel from the given buffers, then further attempts to write to the channel will cause an
     * unspecific runtime exception to be thrown.
     *
     * @param   <A> The type for the object to the attached to the operation
     * @param srcs The buffers from which bytes are to be retrieved
     * @param offset The offset within the buffer array of the first buffer from which bytes are to be
     *     retrieved; must be non-negative and no larger than {@code srcs.length}
     * @param length The maximum number of buffers to be accessed; must be non-negative and no larger
     *     than {@code srcs.length - offset}
     * @param timeout The maximum time for the I/O operation to complete
     * @param unit The time unit of the {@code timeout} argument
     * @param attach The object to attach to the I/O operation; can be {@code null}
     * @param handler The handler for consuming the result
     * @throws IndexOutOfBoundsException If the pre-conditions for the {@code offset} and {@code
     *     length} parameter aren't met
     * @throws WritePendingException If a write operation is already in progress on this channel
     * @throws ShutdownChannelGroupException If the channel group has terminated
     */
    <A> void write(
            ByteBuffer[] srcs,
            int offset,
            int length,
            long timeout,
            TimeUnit unit,
            A attach,
            CompletionHandler<Long, ? super A> handler);
}
