package meerkat.voting.output;

import com.google.protobuf.ByteString;
import meerkat.protobuf.Crypto.*;
import meerkat.protobuf.Voting.*;
import meerkat.voting.output.outputcommands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A toy OutputDevice class
 * outputs everything simply to the System console
 */
public class SystemConsoleOutputDevice extends AsyncRunnableOutputDevice {

    private static final Logger logger = LoggerFactory.getLogger(SystemConsoleOutputDevice.class);

    public SystemConsoleOutputDevice () {
        super();
        logger.info("A SystemConsoleOutputDevice is constructed");
    }

    /**
     * Committing to the ballot.
     * Simply prints to the output stream all the details in the CommitOutputCommand.
     * @param command details to commit to, and the callback to call when finished
     */
    public void doCommitToBallot(CommitOutputCommand command) {
        logger.debug("entered method doCommitToBallot");
        PlaintextBallot plaintextBallot = command.getPlaintext();
        long plaintextSerialNumber = plaintextBallot.getSerialNumber();
        System.out.println("Commitment of Ballot #" + plaintextSerialNumber);
        System.out.println("(channel): ");
        System.out.println(bytesToString(command.getChannelIdentifierByteString()));
        System.out.println("(plaintext): ");
        System.out.println(plaintextBallot);
        SignedEncryptedBallot signedEncryptedBallot = command.getSignedEncryptedBallot();
        long encryptedSerialNumber = signedEncryptedBallot.getEncryptedBallot().getSerialNumber();
        System.out.println("Commitment of Ballot #" + encryptedSerialNumber + " (ciphertext):");
        if (plaintextSerialNumber != encryptedSerialNumber) {
            logger.error("plaintext and encryption serial numbers do not match!! plaintext# = " +
                    plaintextSerialNumber + ", ciphertext# = " + encryptedSerialNumber);
        }
        ByteString encryptedData = signedEncryptedBallot.getEncryptedBallot().getData().getData();
        System.out.println(bytesToString(encryptedData));
        command.getCallback().onSuccess(null);
    }


    /**
     * auditing the ballot.
     * prints to the output stream the ballot secrets (the encryption randomness and its proof of random generation)
     * @param command An auditing command with the callback to finally call
     */
    public void doAudit(AuditOutputCommand command) {
        logger.debug("entered method doAudit");
        System.out.println("Auditing");
        BallotSecrets ballotSecrets = command.getBallotSecrets();
        printEncryptionRandomness(ballotSecrets.getEncryptionRandomness());
        printRandomnessGenerationProof (ballotSecrets.getProof());
        command.getCallback().onSuccess(null);
    }

    /**
     * Casting the ballot (actually does nothing new)
     * @param command a CastOutputCommand with the details and the callback
     */
    public void doCastBallot(CastOutputCommand command) {
        logger.debug("entered method doCastBallot");
        System.out.println("Ballot finalized for casting!");
        command.getCallback().onSuccess(null);
    }


    /**
     * Canceling the ballot (actually does nothing new)
     * @param command a CancelOutputCommand with the details and the callback
     */
    public void doCancel(CancelOutputCommand command) {
        logger.debug("entered method doCancel");
        System.out.println("Ballot cancelled!");
        command.getCallback().onSuccess(null);
    }


    private void printEncryptionRandomness (EncryptionRandomness encryptionRandomness) {
        System.out.println("Encryption Randomness = ");
        ByteString data = encryptionRandomness.getData();
        System.out.println(bytesToString(data));
    }

    private void printRandomnessGenerationProof (RandomnessGenerationProof proof) {
        System.out.println("Proof of randomness generation:");
        ByteString data = proof.getData();
        System.out.println(bytesToString(data));
    }


    /*
     * Returns the UTF8 decoding of byte-string data
     */
    private static String bytesToString(ByteString data) {
        return data.toStringUtf8();
    }

}
