// 
// Decompiled by Procyon v0.6.0
// 

package org.bouncycastle.crypto.digests;

import org.bouncycastle.crypto.OutputLengthException;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.crypto.CryptoServicePurpose;
import org.bouncycastle.crypto.Xof;

public class Blake2xsDigest implements Xof
{
    public static final int UNKNOWN_DIGEST_LENGTH = 65535;
    private static final int DIGEST_LENGTH = 32;
    private static final long MAX_NUMBER_BLOCKS = 4294967296L;
    private int digestLength;
    private Blake2sDigest hash;
    private byte[] h0;
    private byte[] buf;
    private int bufPos;
    private int digestPos;
    private long blockPos;
    private long nodeOffset;
    private final CryptoServicePurpose purpose;
    
    public Blake2xsDigest() {
        this(65535, CryptoServicePurpose.ANY);
    }
    
    public Blake2xsDigest(final int n, final CryptoServicePurpose cryptoServicePurpose) {
        this(n, null, null, null, cryptoServicePurpose);
    }
    
    public Blake2xsDigest(final int n) {
        this(n, CryptoServicePurpose.ANY);
    }
    
    public Blake2xsDigest(final int n, final byte[] array) {
        this(n, array, null, null, CryptoServicePurpose.ANY);
    }
    
    public Blake2xsDigest(final int digestLength, final byte[] array, final byte[] array2, final byte[] array3, final CryptoServicePurpose purpose) {
        this.h0 = null;
        this.buf = new byte[32];
        this.bufPos = 32;
        this.digestPos = 0;
        this.blockPos = 0L;
        if (digestLength < 1 || digestLength > 65535) {
            throw new IllegalArgumentException("BLAKE2xs digest length must be between 1 and 2^16-1");
        }
        this.digestLength = digestLength;
        this.nodeOffset = this.computeNodeOffset();
        this.purpose = purpose;
        this.hash = new Blake2sDigest(32, array, array2, array3, this.nodeOffset, purpose);
    }
    
    public Blake2xsDigest(final Blake2xsDigest blake2xsDigest) {
        this.h0 = null;
        this.buf = new byte[32];
        this.bufPos = 32;
        this.digestPos = 0;
        this.blockPos = 0L;
        this.digestLength = blake2xsDigest.digestLength;
        this.hash = new Blake2sDigest(blake2xsDigest.hash);
        this.h0 = Arrays.clone(blake2xsDigest.h0);
        this.buf = Arrays.clone(blake2xsDigest.buf);
        this.bufPos = blake2xsDigest.bufPos;
        this.digestPos = blake2xsDigest.digestPos;
        this.blockPos = blake2xsDigest.blockPos;
        this.nodeOffset = blake2xsDigest.nodeOffset;
        this.purpose = blake2xsDigest.purpose;
    }
    
    @Override
    public String getAlgorithmName() {
        return "BLAKE2xs";
    }
    
    @Override
    public int getDigestSize() {
        return this.digestLength;
    }
    
    @Override
    public int getByteLength() {
        return this.hash.getByteLength();
    }
    
    public long getUnknownMaxLength() {
        return 137438953472L;
    }
    
    @Override
    public void update(final byte b) {
        this.hash.update(b);
    }
    
    @Override
    public void update(final byte[] array, final int n, final int n2) {
        this.hash.update(array, n, n2);
    }
    
    @Override
    public void reset() {
        this.hash.reset();
        this.h0 = null;
        this.bufPos = 32;
        this.digestPos = 0;
        this.blockPos = 0L;
        this.nodeOffset = this.computeNodeOffset();
    }
    
    @Override
    public int doFinal(final byte[] array, final int n) {
        return this.doFinal(array, n, this.digestLength);
    }
    
    @Override
    public int doFinal(final byte[] array, final int n, final int n2) {
        final int doOutput = this.doOutput(array, n, n2);
        this.reset();
        return doOutput;
    }
    
    @Override
    public int doOutput(final byte[] array, final int n, final int n2) {
        if (n > array.length - n2) {
            throw new OutputLengthException("output buffer too short");
        }
        if (this.h0 == null) {
            this.h0 = new byte[this.hash.getDigestSize()];
            this.hash.doFinal(this.h0, 0);
        }
        if (this.digestLength != 65535) {
            if (this.digestPos + n2 > this.digestLength) {
                throw new IllegalArgumentException("Output length is above the digest length");
            }
        }
        else if (this.blockPos << 5 >= this.getUnknownMaxLength()) {
            throw new IllegalArgumentException("Maximum length is 2^32 blocks of 32 bytes");
        }
        for (int i = 0; i < n2; ++i) {
            if (this.bufPos >= 32) {
                final Blake2sDigest blake2sDigest = new Blake2sDigest(this.computeStepLength(), 32, this.nodeOffset);
                blake2sDigest.update(this.h0, 0, this.h0.length);
                Arrays.fill(this.buf, (byte)0);
                blake2sDigest.doFinal(this.buf, 0);
                this.bufPos = 0;
                ++this.nodeOffset;
                ++this.blockPos;
            }
            array[n + i] = this.buf[this.bufPos];
            ++this.bufPos;
            ++this.digestPos;
        }
        return n2;
    }
    
    private int computeStepLength() {
        if (this.digestLength == 65535) {
            return 32;
        }
        return Math.min(32, this.digestLength - this.digestPos);
    }
    
    private long computeNodeOffset() {
        return this.digestLength * 4294967296L;
    }
}
