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

package org.bouncycastle.crypto.digests;

import org.bouncycastle.util.Strings;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.CryptoServicePurpose;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.Xof;

public class ParallelHash implements Xof, Digest
{
    private static final byte[] N_PARALLEL_HASH;
    private final CSHAKEDigest cshake;
    private final CSHAKEDigest compressor;
    private final int bitLength;
    private final int outputLength;
    private final int B;
    private final byte[] buffer;
    private final byte[] compressorBuffer;
    private boolean firstOutput;
    private int nCount;
    private int bufOff;
    private final CryptoServicePurpose purpose;
    
    public ParallelHash(final int n, final byte[] array, final int n2) {
        this(n, array, n2, n * 2, CryptoServicePurpose.ANY);
    }
    
    public ParallelHash(final int n, final byte[] array, final int n2, final int n3) {
        this(n, array, n2, n3, CryptoServicePurpose.ANY);
    }
    
    public ParallelHash(final int bitLength, final byte[] array, final int b, final int n, final CryptoServicePurpose purpose) {
        if (b <= 0) {
            throw new IllegalArgumentException("block size should be greater than 0");
        }
        this.cshake = new CSHAKEDigest(bitLength, ParallelHash.N_PARALLEL_HASH, array);
        this.compressor = new CSHAKEDigest(bitLength, new byte[0], new byte[0]);
        this.bitLength = bitLength;
        this.B = b;
        this.outputLength = (n + 7) / 8;
        this.buffer = new byte[b];
        this.compressorBuffer = new byte[bitLength * 2 / 8];
        this.purpose = purpose;
        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties(this, bitLength, purpose));
        this.reset();
    }
    
    public ParallelHash(final ParallelHash parallelHash) {
        this.cshake = new CSHAKEDigest(parallelHash.cshake);
        this.compressor = new CSHAKEDigest(parallelHash.compressor);
        this.bitLength = parallelHash.bitLength;
        this.B = parallelHash.B;
        this.outputLength = parallelHash.outputLength;
        this.buffer = Arrays.clone(parallelHash.buffer);
        this.compressorBuffer = Arrays.clone(parallelHash.compressorBuffer);
        this.purpose = parallelHash.purpose;
        this.firstOutput = parallelHash.firstOutput;
        this.nCount = parallelHash.nCount;
        this.bufOff = parallelHash.bufOff;
        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties(this, this.bitLength, this.purpose));
    }
    
    @Override
    public String getAlgorithmName() {
        return "ParallelHash" + this.cshake.getAlgorithmName().substring(6);
    }
    
    @Override
    public int getByteLength() {
        return this.cshake.getByteLength();
    }
    
    @Override
    public int getDigestSize() {
        return this.outputLength;
    }
    
    @Override
    public void update(final byte b) throws IllegalStateException {
        this.buffer[this.bufOff++] = b;
        if (this.bufOff == this.buffer.length) {
            this.compress();
        }
    }
    
    @Override
    public void update(final byte[] array, final int n, int max) throws DataLengthException, IllegalStateException {
        max = Math.max(0, max);
        int i = 0;
        if (this.bufOff != 0) {
            while (i < max && this.bufOff != this.buffer.length) {
                this.buffer[this.bufOff++] = array[n + i++];
            }
            if (this.bufOff == this.buffer.length) {
                this.compress();
            }
        }
        if (i < max) {
            while (max - i >= this.B) {
                this.compress(array, n + i, this.B);
                i += this.B;
            }
        }
        while (i < max) {
            this.update(array[n + i++]);
        }
    }
    
    private void compress() {
        this.compress(this.buffer, 0, this.bufOff);
        this.bufOff = 0;
    }
    
    private void compress(final byte[] array, final int n, final int n2) {
        this.compressor.update(array, n, n2);
        this.compressor.doFinal(this.compressorBuffer, 0, this.compressorBuffer.length);
        this.cshake.update(this.compressorBuffer, 0, this.compressorBuffer.length);
        ++this.nCount;
    }
    
    private void wrapUp(final int n) {
        if (this.bufOff != 0) {
            this.compress();
        }
        final byte[] rightEncode = XofUtils.rightEncode(this.nCount);
        final byte[] rightEncode2 = XofUtils.rightEncode(n * 8);
        this.cshake.update(rightEncode, 0, rightEncode.length);
        this.cshake.update(rightEncode2, 0, rightEncode2.length);
        this.firstOutput = false;
    }
    
    @Override
    public int doFinal(final byte[] array, final int n) throws DataLengthException, IllegalStateException {
        if (this.firstOutput) {
            this.wrapUp(this.outputLength);
        }
        final int doFinal = this.cshake.doFinal(array, n, this.getDigestSize());
        this.reset();
        return doFinal;
    }
    
    @Override
    public int doFinal(final byte[] array, final int n, final int n2) {
        if (this.firstOutput) {
            this.wrapUp(this.outputLength);
        }
        final int doFinal = this.cshake.doFinal(array, n, n2);
        this.reset();
        return doFinal;
    }
    
    @Override
    public int doOutput(final byte[] array, final int n, final int n2) {
        if (this.firstOutput) {
            this.wrapUp(0);
        }
        return this.cshake.doOutput(array, n, n2);
    }
    
    @Override
    public void reset() {
        this.cshake.reset();
        Arrays.clear(this.buffer);
        final byte[] leftEncode = XofUtils.leftEncode(this.B);
        this.cshake.update(leftEncode, 0, leftEncode.length);
        this.nCount = 0;
        this.bufOff = 0;
        this.firstOutput = true;
    }
    
    static {
        N_PARALLEL_HASH = Strings.toByteArray("ParallelHash");
    }
}
