package meerkat.mixer.main;

import meerkat.mixer.MixerOutput;
import meerkat.mixer.proofs.Mix2nizk;
import meerkat.protobuf.Crypto;
import meerkat.mixer.necessary.AsyncBulletinBoardClient;
import meerkat.mixer.necessary.CompleteBatch;
import meerkat.mixer.MixVerifier;

import java.util.Arrays;
import java.util.List;

/**
 * Created by Tzlil on 12/17/2015.
 * Handles callback for receiving a complete mix proof table from the bulletin board.
  */
public class BatchHandler implements AsyncBulletinBoardClient.ClientCallback<CompleteBatch> {

    private MixerOutput mixerOutput;
    private boolean msgReceived;
    private Throwable t;
    private CompleteBatch msg;

    private final Mix2nizk.Verifier verifier;

    /**
     * constructor
     * @param verifier
     */
    public BatchHandler(Mix2nizk.Verifier verifier) {
        this.mixerOutput = null;
        this.msgReceived = false;
        this.t = null;
        this.verifier = verifier;
    }

    @Override
    public void handleCallback(CompleteBatch msg) {
        this.msg = msg;
        synchronized (this) {
            this.msgReceived = true;
            notifyAll();
        }
    }

    @Override
    public void handleFailure(Throwable t) {
        this.t = t;
        synchronized (this) {
            this.msgReceived = true;
            notifyAll();
        }
    }

    /**
     * return true iff msg was received
     * @return msgReceived
     */
    public boolean isMsgReceived() {
        return msgReceived;
    }

    /**
     * convert batch data to meerkat.mixProverVerifier.network output
     * @throws Exception
     */
    private void convertMessage() throws Exception {
        BatchConverter batchConverter = new BatchConverter();
        this.mixerOutput = batchConverter.BatchChunkList2MixerOutput(msg.getBatchChunkList());
    }

    /**
     * call convert message, and if succeed verify the table
     * @return return true iff the given batch message is valid meerkat.mixProverVerifier.network output
     * @throws Exception
     */
    public boolean verifyTable(boolean strict) throws Exception {
        if (mixerOutput == null) {
            convertMessage();
        }
        return MixVerifier.verifyTable(verifier, mixerOutput, strict);
    }

    /**
     * extract input for meerkat.mixProverVerifier.network from previous mixers output
     * @return last layer of encrypted votes as list
     * @throws Throwable in case if failure
     */
    public List<Crypto.RerandomizableEncryptedMessage> getInputForMixer(boolean strictVerification) throws Throwable {
        if (t != null) {
            throw t;
        }
        if(!verifyTable(strictVerification)){
            throw new Exception("invalid table");
        }
        return Arrays.asList(mixerOutput.getEncryptedMessages()[mixerOutput.getNumLayers()]); //there are layers + 1
    }
}

