package meerkat.mixer.main;

import meerkat.mixer.MixerOutput;
import meerkat.protobuf.BulletinBoardApi;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.Mixing;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Tzlil on 12/17/2015.
 * provide convert operation from batch data to meerkat.mixProverVerifier.network output and backwards
 */
public class BatchConverter {
    /**
     * convert meerkat.mixProverVerifier.network output to batch data
     * @param mixerOutput
     * @return meerkat.mixProverVerifier.network output as list of batch data
     */
    public List<BulletinBoardApi.BatchChunk> MixerOutput2BatchChunk(MixerOutput mixerOutput) {

        List<BulletinBoardApi.BatchChunk> result = new ArrayList<BulletinBoardApi.BatchChunk>();

        Mixing.MixBatchHeader header = Mixing.MixBatchHeader.newBuilder()
                .setLogN(mixerOutput.getLogN())
                .setLayers(mixerOutput.getNumLayers())
                .build();

        result.add(BulletinBoardApi.BatchChunk.newBuilder()
                .setData(header.toByteString())
                .build());

        for (Mixing.Mix2Proof[] zkpLayer : mixerOutput.getProofs()) {
            for (Mixing.Mix2Proof zkp : zkpLayer) {
                result.add(BulletinBoardApi.BatchChunk.newBuilder()
                        .setData(zkp.toByteString())
                        .build());
            }
        }
        for (Crypto.RerandomizableEncryptedMessage[] encryptionLayer : mixerOutput.getEncryptedMessages()) {
            for (Crypto.RerandomizableEncryptedMessage encryption : encryptionLayer) {
                result.add(BulletinBoardApi.BatchChunk.newBuilder()
                        .setData(encryption.toByteString())
                        .build());
            }
        }
        return result;
    }

    /**
     * convert batch data list to meerkat.mixProverVerifier.network output
     * @param batchChunkList
     * @return batch data list as MixerOutput
     * @throws Exception
     */
    public MixerOutput BatchChunkList2MixerOutput
    (List<BulletinBoardApi.BatchChunk> batchChunkList) throws Exception {

        Mixing.MixBatchHeader header = Mixing.MixBatchHeader.parseFrom(batchChunkList.remove(0).getData());

        int logN = header.getLogN();
        int n = 1 << logN;
        int layers = header.getLayers();
        int nDiv2 = n >>1;
        Mixing.Mix2Proof[][] proofs = new Mixing.Mix2Proof[layers][nDiv2];
        for (int layer = 0; layer < layers; layer++)
        {
            for (int proofIndex = 0 ; proofIndex < nDiv2 ; proofIndex ++)
            {
                proofs[layer][proofIndex] = Mixing.Mix2Proof.parseFrom(batchChunkList.remove(0).getData());
            }
        }

        Crypto.RerandomizableEncryptedMessage[][] encryptions
                = new Crypto.RerandomizableEncryptedMessage[layers + 1][n];
        for (int layer = 0; layer < layers + 1; layer++)
        {
            for (int encryptionIndex = 0 ; encryptionIndex < n ; encryptionIndex ++)
            {
                encryptions[layer][encryptionIndex] = Crypto.RerandomizableEncryptedMessage
                        .parseFrom(batchChunkList.remove(0).getData());
            }
        }

        return new meerkat.mixer.MixerOutput(logN, proofs,encryptions);

    }

}

