package org.python.core;

/**
 * This interface provides a base for the key interface of the buffer API, {@link PyBuffer},
 * including symbolic constants used by the consumer of a {@code PyBuffer} to specify its
 * requirements and assumptions. The Jython buffer API emulates the CPython buffer API. There are
 * two reasons for separating parts of {@code PyBuffer} into this interface:
 * <ul>
 * <li>The constants defined in CPython have the names {@code PyBUF_SIMPLE}, {@code PyBUF_WRITABLE},
 * etc., and the trick of defining ours here means we can write {@code PyBUF.SIMPLE},
 * {@code PyBUF.WRITABLE}, etc. so source code looks similar.</li>
 * <li>It is not so easy in Java as it is in C to treat a {@code byte} array as storing anything
 * other than {@code byte}, and we prepare for the possibility of buffers with a series of different
 * primitive types by defining here those methods that would be in common between
 * (Byte){@code Buffer} and an assumed future {@code FloatBuffer} or {@code TypedBuffer<T>}.
 * (Compare {@code java.nio.Buffer}.)</li>
 * </ul>
 * It is unlikely any classes would implement {@code PyBUF}, except indirectly through other
 * interfaces. Users of the Jython buffer API can mostly overlook the distinction and just use
 * {@code PyBuffer}.
 */
public interface PyBUF {

    /**
     * Determine whether the consumer is entitled to write to the exported storage.
     *
     * @return true if writing is not allowed, false if it is.
     */
    boolean isReadonly();

    /**
     * The number of dimensions to the buffer. This number is the length of the {@code shape} array.
     * The actual storage may be a linear array, but this is the number of dimensions in the
     * interpretation that the exporting object gives the data.
     *
     * @return number of dimensions
     */
    int getNdim();

    /**
     * An array reporting the size of the buffer, considered as a multidimensional array, in each
     * dimension and (by its length) giving the number of dimensions. The size of the buffer is its
     * size in "items". An item is the amount of buffer content addressed by one index or set of
     * indices. In the simplest case an item is a single unit (byte), and there is one dimension. In
     * complex cases, the array is multi-dimensional, and the item at each location is multi-unit
     * (multi-byte). The consumer must not modify this array. A valid {@code shape} array is always
     * returned (difference from CPython).
     *
     * @return the dimensions of the buffer as an array
     */
    int[] getShape();

    /**
     * The number of bytes stored in each indexable item.
     *
     * @return the number of bytes comprising each item.
     */
    int getItemsize();

    /**
     * The total number of bytes represented by the view, which will be the product of the elements
     * of the {@code shape} array, and the item size in bytes.
     *
     * @return the total number of bytes represented.
     */
    int getLen();

    /**
     * The {@code strides} array gives the distance in the storage array between adjacent items (in
     * each dimension). In the rawest parts of the buffer API, the consumer of the buffer is able to
     * navigate the exported storage. The "strides" array is part of the support for interpreting
     * the buffer as an n-dimensional array of items. It provides the coefficients of the
     * "addressing polynomial". (More on this in the CPython documentation.) The consumer must not
     * modify this array. A valid {@code strides} array is always returned (difference from
     * CPython).
     *
     * @return the distance in the storage array between adjacent items (in each dimension)
     */
    int[] getStrides();

    /**
     * The {@code suboffsets} array is a further part of the support for interpreting the buffer as
     * an n-dimensional array of items, where the array potentially uses indirect addressing (like a
     * real Java array of arrays, in fact). This is only applicable when there is more than 1
     * dimension, and it works in conjunction with the {@code strides} array. (More on this in the
     * CPython documentation.) When used, {@code suboffsets[k]} is an integer index, not a byte
     * offset as in CPython. The consumer must not modify this array. When not needed for navigation
     * {@code null} is returned (as in CPython).
     *
     * @return suboffsets array or {@code null} if not necessary for navigation
     */
    int[] getSuboffsets();

    /**
     * Enquire whether the array is represented contiguously in the backing storage, according to C
     * or Fortran ordering. A one-dimensional contiguous array is both.
     *
     * @param order 'C', 'F' or 'A', as the storage order is C, Fortran or either.
     * @return true iff the array is stored contiguously in the order specified
     */
    boolean isContiguous(char order);

    /* Constants taken from CPython object.h in v3.3 */

    /**
     * The maximum allowed number of dimensions (CPython restriction).
     */
    static final int MAX_NDIM = 64;
    /**
     * A constant used by the consumer in its call to {@link BufferProtocol#getBuffer(int)} to
     * specify that it expects to write to the buffer contents. {@code getBuffer} will raise an
     * exception if the exporter's buffer cannot meet this requirement.
     */
    static final int WRITABLE = 0x0001;
    /**
     * A constant used by the consumer in its call to {@link BufferProtocol#getBuffer(int)} to
     * specify that it assumes a simple one-dimensional organisation of the exported storage with
     * item size of one. {@code getBuffer} will raise an exception if the consumer sets this flag
     * and the exporter cannot represent itself as byte array data.
     */
    static final int SIMPLE = 0;
    /**
     * A constant used by the consumer in its call to {@link BufferProtocol#getBuffer(int)} to
     * specify that it requires {@link PyBuffer#getFormat()} to return a {@code String} indicating
     * the type of the unit. This exists for compatibility with CPython, as in Jython the format is
     * always provided by {@code getFormat()}.
     */
    static final int FORMAT = 0x0004;
    /**
     * A constant used by the consumer in its call to {@link BufferProtocol#getBuffer(int)} to
     * specify that it is prepared to navigate the buffer as multi-dimensional using the
     * {@code shape} array. {@code getBuffer} will raise an exception if consumer does not specify
     * the flag but the exporter's buffer cannot be navigated without taking into account its
     * multiple dimensions.
     */
    static final int ND = 0x0008;
    /**
     * A constant used by the consumer in its call to {@link BufferProtocol#getBuffer(int)} to
     * specify that it expects to use the {@code strides} array. {@code getBuffer} will raise an
     * exception if consumer does not specify the flag but the exporter's buffer cannot be navigated
     * without using the {@code strides} array.
     */
    static final int STRIDES = 0x0010 | ND;
    /**
     * A constant used by the consumer in its call to {@link BufferProtocol#getBuffer(int)} to
     * specify that it will assume C-order organisation of the items. {@code getBuffer} will raise
     * an exception if the exporter's buffer is not C-ordered. {@code C_CONTIGUOUS} implies
     * {@code STRIDES}.
     */
    // It is possible this should have been (0x20|ND) expressing the idea that C-order addressing
    // will be assumed *instead of* using a strides array.
    static final int C_CONTIGUOUS = 0x0020 | STRIDES;
    /**
     * A constant used by the consumer in its call to {@link BufferProtocol#getBuffer(int)} to
     * specify that it will assume Fortran-order organisation of the items. {@code getBuffer} will
     * raise an exception if the exporter's buffer is not Fortran-ordered. {@code F_CONTIGUOUS}
     * implies {@code STRIDES}.
     */
    static final int F_CONTIGUOUS = 0x0040 | STRIDES;
    /**
     * A constant used by the consumer in its call to {@link BufferProtocol#getBuffer(int)} to
     * specify that it will assume a contiguous organisation of the items, but will enquire which
     * organisation it actually is.
     *
     * {@code getBuffer} will raise an exception if the exporter's buffer is not contiguous.
     * {@code ANY_CONTIGUOUS} implies {@code STRIDES}.
     */
    // Further CPython strangeness since it uses the strides array to answer the enquiry.
    static final int ANY_CONTIGUOUS = 0x0080 | STRIDES;
    /**
     * A constant used by the consumer in its call to {@link BufferProtocol#getBuffer(int)} to
     * specify that it understands the {@code suboffsets} array. {@code getBuffer} will raise an
     * exception if consumer does not specify the flag but the exporter's buffer cannot be navigated
     * without understanding the {@code suboffsets} array. {@code INDIRECT} implies {@code STRIDES}.
     */
    static final int INDIRECT = 0x0100 | STRIDES;
    /**
     * Equivalent to {@code (ND | WRITABLE)}
     */
    static final int CONTIG = ND | WRITABLE;
    /**
     * Equivalent to {@code ND}
     */
    static final int CONTIG_RO = ND;
    /**
     * Equivalent to {@code (STRIDES | WRITABLE)}
     */
    static final int STRIDED = STRIDES | WRITABLE;
    /**
     * Equivalent to {@code STRIDES}
     */
    static final int STRIDED_RO = STRIDES;
    /**
     * Equivalent to {@code (STRIDES | WRITABLE | FORMAT)}
     */
    static final int RECORDS = STRIDES | WRITABLE | FORMAT;
    /**
     * Equivalent to {@code (STRIDES | FORMAT)}
     */
    static final int RECORDS_RO = STRIDES | FORMAT;
    /**
     * Equivalent to {@code (INDIRECT | WRITABLE | FORMAT)}. Also use this in the request if you
     * plan only to use the fully-encapsulated API ({@code byteAt}, {@code storeAt}, {@code copyTo},
     * {@code copyFrom}, etc.), without ever calling {@link PyBuffer#getNIOByteBuffer()} or using
     * {@link PyBuffer.Pointer}.
     */
    static final int FULL = INDIRECT | WRITABLE | FORMAT;
    /**
     * Equivalent to {@code (INDIRECT | FORMAT)}. Also use this in the request if you plan only to
     * use the fully-encapsulated API ({@code byteAt}, {@code copyTo}, etc.), read only, without
     * ever calling {@link PyBuffer#getNIOByteBuffer()} or using {@link PyBuffer.Pointer}.
     */
    static final int FULL_RO = INDIRECT | FORMAT;

    /* Constants for additional feature(s), not standard for CPython */

    /**
     * A constant used by the consumer in its call to {@link BufferProtocol#getBuffer(int)} to
     * specify that it expects to access the buffer contents directly as an array (rather than
     * through the purely abstract part of the API). {@code getBuffer} will raise an exception if
     * the exporter cannot expose its storage as Java array.
     */
    // XXX Pending: @Deprecated
    static final int AS_ARRAY = 0x10000000;

    /* Constants for readability, not standard for CPython */

    /**
     * Field mask, used as in {@code if ((flags&NAVIGATION) == STRIDES) ...}. The importance of the
     * subset of flags defined by this mask is not so much in their "navigational" character as in
     * the way they are treated in a buffer request.
     * <p>
     * The {@code NAVIGATION} set are used to specify which navigation arrays the consumer will use,
     * and therefore the consumer must ask for all those necessary to use the buffer successfully
     * (which is a function of the buffer's actual type). Asking for extra ones is not an error,
     * since all are supplied (in Jython): asking for too few is an error.
     * <p>
     * Flags outside the {@code NAVIGATION} set, work the other way round. Asking for one the buffer
     * cannot match is an error: not asking for a feature the buffer does not have is an error.
     */
    static final int NAVIGATION = SIMPLE | ND | STRIDES | INDIRECT;
    /**
     * A constant used by the exporter in processing {@link BufferProtocol#getBuffer(int)} to check
     * for assumed C-order organisation of the items.
     * {@code C_CONTIGUOUS = IS_C_CONTIGUOUS | STRIDES}.
     */
    static final int IS_C_CONTIGUOUS = C_CONTIGUOUS & ~STRIDES;
    /**
     * A constant used by the exporter in processing {@link BufferProtocol#getBuffer(int)} to check
     * for assumed C-order Fortran-order organisation of the items.
     * {@code F_CONTIGUOUS = IS_F_CONTIGUOUS | STRIDES}.
     */
    static final int IS_F_CONTIGUOUS = F_CONTIGUOUS & ~STRIDES;
    /**
     * Field mask, used as in if {@code ((flags&CONTIGUITY)== ... ) ...}.
     */
    static final int CONTIGUITY = (C_CONTIGUOUS | F_CONTIGUOUS | ANY_CONTIGUOUS) & ~STRIDES;
}
