package meerkat.crypto.concrete;

import com.google.protobuf.ByteString;
import com.google.protobuf.Message;
import meerkat.crypto.Digest;
import meerkat.protobuf.BulletinBoardApi.MessageID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * Created by talm on 11/9/15.
 */
public class SHA256Digest implements Digest {
    final Logger logger = LoggerFactory.getLogger(getClass());
    public static final String SHA256 = "SHA-256";

    MessageDigest hash;

    /**
     * Used to convert length to bytes in proper order.
     */
    ByteBuffer lenBuf = ByteBuffer.allocate(4);

    /**
     * Instantiate with a specified algorithm.
     * @param algorithm
     * @throws NoSuchAlgorithmException
     */
    public SHA256Digest(String algorithm) throws NoSuchAlgorithmException {
        hash = MessageDigest.getInstance(algorithm);
    }

    /**
     * Instantiate with the default (SHA-256) algorithm
     */
    public SHA256Digest() { this(true); }


    /**SHA
     * Instantiate with the default (SHA-256) algorithm,
     * or create an empty class (for cloning)
     */
    private SHA256Digest(boolean initHash) {
        if (initHash) {
            try {
                hash = MessageDigest.getInstance(SHA256);
            } catch (NoSuchAlgorithmException e) {
                // Should never happen!
                logger.error("Couldn't find default {} algorhtm: {}", SHA256, e);
                assert false;
            }
        }
    }

    @Override
    public byte[] digest() {
        return hash.digest();
    }

    @Override
    public MessageID digestAsMessageID() {
        return MessageID.newBuilder().setID(ByteString.copyFrom(digest())).build();
    }

    @Override
    public void update(Message msg) {

        lenBuf.clear();
        lenBuf.putInt(msg.getSerializedSize());
        lenBuf.flip();
        hash.update(lenBuf);
        hash.update(msg.toByteString().asReadOnlyByteBuffer());
    }

    final public void update(ByteString msg) {
        hash.update(msg.asReadOnlyByteBuffer());
    }

    @Override
    final public void update(byte[] msg) {
        hash.update(msg);
    }

    final public void update(ByteBuffer msg) {
        hash.update(msg);
    }

    @Override
    public void reset() {
        hash.reset();
    }

    @Override
    public SHA256Digest clone() throws CloneNotSupportedException {
        SHA256Digest copy = new SHA256Digest(false);
        copy.hash = (MessageDigest) hash.clone();
        return copy;
    }
}
