package meerkat.voting.encryptor;

import meerkat.crypto.*;
import meerkat.protobuf.Crypto.*;
import meerkat.protobuf.Voting.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.security.SignatureException;
import java.util.Random;

/**
 * A basic implementation of the VBCryptoManager interface
 */
public class VBCryptoManagerImpl implements VBCryptoManager {

    protected final static Logger logger = LoggerFactory.getLogger(VBCryptoManagerImpl.class);

    private final Random random;  //TODO: Random object should be more cryptographycally secure
    private final Encryption encryption;
    private final DigitalSignature digitalSignature;


    public VBCryptoManagerImpl (Random rand, Encryption encryption, DigitalSignature digitalSignature) {
        this.random = rand;
        this.encryption = encryption;
        this.digitalSignature = digitalSignature;
    }


    @Override
    public EncryptionAndSecrets encrypt(PlaintextBallot plaintextBallot) throws SignatureException, IOException {

        // TODO: do we seed the random here?

        try {
            EncryptionRandomness encryptionRandomness = encryption.generateRandomness(random);
            BallotSecrets secrets = BallotSecrets.newBuilder()
                    .setPlaintextBallot(plaintextBallot)
                    .setEncryptionRandomness(encryptionRandomness)
                    .build();
            RerandomizableEncryptedMessage encryptedMessage = encryption.encrypt(plaintextBallot, encryptionRandomness);
            EncryptedBallot encBallot = EncryptedBallot.newBuilder()
                    .setSerialNumber(plaintextBallot.getSerialNumber())
                    .setData(encryptedMessage)
                    .build();
            digitalSignature.updateContent(encBallot);

            SignedEncryptedBallot signedEncryptedBallot = SignedEncryptedBallot.newBuilder()
                    .setEncryptedBallot(encBallot)
                    .setSignature(digitalSignature.sign())
                    .build();

            // TODO: still has to supply RandomnessGenerationProof as well

            return new EncryptionAndSecrets(signedEncryptedBallot, secrets);
        }
        catch (IOException e) {
            logger.error("encrypt: the encryption component has thrown an exception: " + e);
            throw e;
        }
        catch (SignatureException e) {
            logger.error("encrypt: the signature component has thrown an exception: " + e);
            throw e;
        }
    }
}
