package net.jacksum.zzadopt.de.flexiprovider.api;

import net.jacksum.zzadopt.de.flexiprovider.api.exceptions.DigestException;

public abstract class MessageDigest extends java.security.MessageDigestSpi {

    // ****************************************************
    // JCA adapter methods
    // ****************************************************
    @Override
    protected final int engineGetDigestLength() {
        return getDigestLength();
    }

    @Override
    protected final void engineUpdate(byte input) {
        update(input);
    }

    @Override
    protected final void engineUpdate(byte[] input, int offset, int len) {
        update(input, offset, len);
    }

    @Override
    protected final byte[] engineDigest() {
        return digest();
    }

    @Override
    protected final void engineReset() {
        reset();
    }

    // ****************************************************
    // FlexiAPI methods
    // ****************************************************
    /**
     * @return the digest length in bytes
     */
    public abstract int getDigestLength();

    /**
     * Update the digest using the specified byte.
     *
     * @param input the byte to use for the update
     */
    public abstract void update(byte input);

    /**
     * Update the digest using the specified array of bytes, starting at the
     * specified offset.
     *
     * @param input the array of bytes to use for the update
     */
    public final void update(byte[] input) {
        if (input == null) {
            return;
        }
        update(input, 0, input.length);
    }

    /**
     * Update the digest using the specified array of bytes, starting at the
     * specified offset.
     *
     * @param input the array of bytes to use for the update
     * @param offset the offset to start from in the array of bytes
     * @param len the number of bytes to use, starting at <code>offset</code>
     */
    public abstract void update(byte[] input, int offset, int len);

    /**
     * Complete the hash computation by performing final operations such as
     * padding. Once {@link #digest()} has been called, the engine should be
     * reset (see {@link #reset()}). Resetting is the responsibility of the
     * engine implementor.
     *
     * @return the array of bytes for the resulting hash value.
     */
    public abstract byte[] digest();

    /**
     * Update the digest and complete the hash computation by performing final
     * operations such as padding. Once {@link #digest(byte[])} has been called,
     * the engine should be reset (see {@link #reset()}). Resetting is the
     * responsibility of the engine implementor.
     *
     * @param input the array of bytes to use for the update
     * @return the array of bytes for the resulting hash value
     */
    public final byte[] digest(byte[] input) {
        update(input);
        return digest();
    }

    /**
     * Complete the hash computation by performing final operations such as
     * padding. Once {@link #digest(byte[], int, int)} has been called, the
     * engine should be reset (see {@link #reset()}). Resetting is the
     * responsibility of the engine implementor.
     *
     * @param buf the output buffer in which to store the digest
     * @param offset offset to start from in the output buffer
     * @param len number of bytes within buf allotted for the digest. Both this
     * default implementation and the SUN provider do not return partial
     * digests. The presence of this parameter is solely for consistency in our
     * API's. If the value of this parameter is less than the actual digest
     * length, the method will throw a DigestException. This parameter is
     * ignored if its value is greater than or equal to the actual digest
     * length.
     * @return the length of the digest stored in the output buffer.
     * @throws DigestException if an error occurs.
     */
    public final int digest(byte[] buf, int offset, int len)
            throws DigestException {

        byte[] digest = digest();
        if (len < digest.length) {
            throw new DigestException("partial digests not returned");
        }
        if (buf.length - offset < digest.length) {
            throw new DigestException("insufficient space in the output "
                    + "buffer to store the digest");
        }
        System.arraycopy(digest, 0, buf, offset, digest.length);
        return digest.length;
    }

    /**
     * Reset the digest for further use.
     */
    public abstract void reset();

}
