package meerkat.mixer.proofs.generic;

import meerkat.mixer.proofs.Concatenator;
import meerkat.mixer.proofs.SigmaProtocol;

import java.math.BigInteger;

/**
 * Generic conjuction of sigma protocols
 */
public class SigmaProtocolAnd2  {
    static public class Prover <FirstMessageOut, FirstMessageIn, FinalMessageOut,  FinalMessageIn>
            implements SigmaProtocol.Prover<FirstMessageOut, FinalMessageOut>   {
        final SigmaProtocol.Prover<FirstMessageIn, FinalMessageIn>[] provers;

        final Concatenator.Pair<FirstMessageOut, FirstMessageIn, FirstMessageIn> firstMessageConcatenator;
        final Concatenator.Pair<FinalMessageOut, FinalMessageIn, FinalMessageIn> finalMessageConcatenator;

        @SafeVarargs
        public Prover(Concatenator.Pair<FirstMessageOut, FirstMessageIn, FirstMessageIn> firstMessageConcatenator,
                      Concatenator.Pair<FinalMessageOut, FinalMessageIn, FinalMessageIn> finalMessageConcatenator,
                      SigmaProtocol.Prover<FirstMessageIn, FinalMessageIn>... provers) {
            this.firstMessageConcatenator = firstMessageConcatenator;
            this.finalMessageConcatenator = finalMessageConcatenator;
            this.provers = provers;
        }

        @Override
        public FirstMessageOut getFirstMessage() {
            return firstMessageConcatenator.concatenate(provers[0].getFirstMessage(), provers[1].getFirstMessage());
        }

        @Override
        public FinalMessageOut getFinalMessage(BigInteger challenge) {
            return finalMessageConcatenator.concatenate(provers[0].getFinalMessage(challenge), provers[1].getFinalMessage(challenge));
        }
    }

    static public class Verifier<FirstMessageOut, FirstMessageIn,
            FinalMessageOut,  FinalMessageIn>
             implements SigmaProtocol.Verifier<FirstMessageOut, FinalMessageOut>  {
        final SigmaProtocol.Verifier<FirstMessageIn, FinalMessageIn>[] verifiers;

        final Concatenator.Pair<FirstMessageOut, FirstMessageIn, FirstMessageIn> firstMessageConcatenator;
        final Concatenator.Pair<FinalMessageOut, FinalMessageIn, FinalMessageIn> finalMessageConcatenator;

        @SafeVarargs
        public Verifier(Concatenator.Pair<FirstMessageOut, FirstMessageIn, FirstMessageIn> firstMessageConcatenator,
                        Concatenator.Pair<FinalMessageOut, FinalMessageIn, FinalMessageIn> finalMessageConcatenator,
                        SigmaProtocol.Verifier<FirstMessageIn, FinalMessageIn>... verifiers) {
            this.firstMessageConcatenator = firstMessageConcatenator;
            this.finalMessageConcatenator = finalMessageConcatenator;
            this.verifiers = verifiers;
        }

        @Override
        public boolean verify(FirstMessageOut firstMessage, BigInteger challenge, FinalMessageOut finalMessage) {
            return verifiers[0].verify(firstMessageConcatenator.getMsg1(firstMessage), challenge, finalMessageConcatenator.getMsg1(finalMessage)) &&
                    verifiers[1].verify(firstMessageConcatenator.getMsg2(firstMessage), challenge, finalMessageConcatenator.getMsg2(finalMessage));
        }
    }

    static public class Simulator <FirstMessageOut, FirstMessageIn, FinalMessageOut, FinalMessageIn>
            implements SigmaProtocol.Simulator<FirstMessageOut, FinalMessageOut> {
        final SigmaProtocol.Simulator<FirstMessageIn, FinalMessageIn>[] simulators;

        final Concatenator.Pair<FirstMessageOut, FirstMessageIn, FirstMessageIn> firstMessageConcatenator;
        final Concatenator.Pair<FinalMessageOut, FinalMessageIn, FinalMessageIn> finalMessageConcatenator;

        @SafeVarargs
        public Simulator(Concatenator.Pair<FirstMessageOut, FirstMessageIn, FirstMessageIn> firstMessageConcatenator,
                         Concatenator.Pair<FinalMessageOut, FinalMessageIn, FinalMessageIn> finalMessageConcatenator,
                         SigmaProtocol.Simulator<FirstMessageIn, FinalMessageIn>... simulators) {
            this.firstMessageConcatenator = firstMessageConcatenator;
            this.finalMessageConcatenator = finalMessageConcatenator;
            this.simulators = simulators;
        }

        @Override
        public FirstMessageOut getFirstMessage(BigInteger challenge) {
            return firstMessageConcatenator.concatenate(simulators[0].getFirstMessage(challenge), simulators[1].getFirstMessage(challenge));
        }

        @Override
        public FinalMessageOut getFinalMessage() {
            return finalMessageConcatenator.concatenate(simulators[0].getFinalMessage(), simulators[1].getFinalMessage());
        }

    }

}
