/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.cryptolib.primitives.messagedigest.factory;

import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;

import com.scytl.cryptolib.api.exceptions.CryptoLibException;
import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.commons.configuration.Provider;
import com.scytl.cryptolib.primitives.messagedigest.configuration.MessageDigestPolicy;

/**
 * A generator of message digests.
 * <P>
 * Instances of this class are immutable.
 */
public class CryptoMessageDigest {

    private MessageDigest _messageDigestGenerator;

    /**
     * Creates an instance of message digest generator with the provided policy.
     * 
     * @param messageDigestPolicy
     *            Policy for generating message digests.
     */
    CryptoMessageDigest(final MessageDigestPolicy messageDigestPolicy) {

		try {
            if (Provider.DEFAULT == messageDigestPolicy
                .getMessageDigestAlgorithmAndProvider().getProvider()) {
                _messageDigestGenerator =
                    MessageDigest.getInstance(messageDigestPolicy
                        .getMessageDigestAlgorithmAndProvider()
                        .getAlgorithm());
            } else {
                _messageDigestGenerator =
                    MessageDigest.getInstance(messageDigestPolicy
                        .getMessageDigestAlgorithmAndProvider()
                        .getAlgorithm(), messageDigestPolicy
                        .getMessageDigestAlgorithmAndProvider()
                        .getProvider().getProviderName());
            }
        } catch (GeneralSecurityException e) {
            throw new CryptoLibException(
                "Failed to create message digest generator in this environment. Attempted to use the provider: "
                    + messageDigestPolicy
                        .getMessageDigestAlgorithmAndProvider()
                        .getProvider().getProviderName()
                    + ", and the algorithm: "
                    + messageDigestPolicy
                        .getMessageDigestAlgorithmAndProvider()
                        .getAlgorithm()
                    + ". Error message was "
                    + e.getMessage(), e);
        }
    }

    /**
     * Generate a message digest for the given data.
     * 
     * @param data
     *            the input data for the message digest.
     * @return The byte[] representing the generated message digest.
     */
    public byte[] generate(final byte[] data) {
        return _messageDigestGenerator.digest(data);
    }

    /**
     * Generate a message digest for the data readable from the received input
     * stream.
     * 
     * @param in
     *            the {@link InputStream} from which to read data.
     * @return the byte[] representing the generated message digest.
     * @throws GeneralCryptoLibException
     *             if there are any problems reading data from {@code in}.
     */
    public byte[] generate(final InputStream in)
            throws GeneralCryptoLibException {

        byte[] buf = new byte[4096];
        int len;
        try {
            while ((len = in.read(buf)) >= 0) {
                _messageDigestGenerator.update(buf, 0, len);
            }
        } catch (IOException e) {
            throw new CryptoLibException(
                "Exception while generating message digest", e);
        }

        return _messageDigestGenerator.digest();
    }

    /**
     * @return the length of the digest in bytes.
     */
    public int getDigestLength() {
        return _messageDigestGenerator.getDigestLength();
    }

    /**
     * Method for returning a raw {@link MessageDigest} with the 
     * appropriate policies set for working on more low-level functionalities.
     * @return The raw {@link MessageDigest}.
     */
    public MessageDigest getRawMessageDigest() {
    	return _messageDigestGenerator;
    }
}
