/*
 This file is part of GNUnet.
 (C) 2014 Christian Grothoff (and other contributing authors)

 GNUnet is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published
 by the Free Software Foundation; either version 3, or (at your
 option) any later version.

 GNUnet is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with GNUnet; see the file COPYING.  If not, write to the
 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.
 */

package org.gnunet.voting;

import org.gnunet.construct.Construct;
import org.gnunet.secretsharing.*;
import org.gnunet.testing.TestingFixture;
import org.gnunet.testing.TestingSubsystem;
import org.gnunet.util.*;
import org.gnunet.util.crypto.EcdsaPrivateKey;
import org.gnunet.util.crypto.EcdsaPublicKey;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

import java.math.BigInteger;
import java.util.Arrays;

public class TestVotingCrypto extends TestingFixture {

    static BigInteger[] generators2;

    @BeforeClass
    public static void generateGenerators() {
        generators2 = Parameters.generateGenerators(2);
    }

    @Test
    public void testEmptyTally() {
        final Wrapper<Boolean> success = new Wrapper<Boolean>();
        Configuration armConf = new Configuration();
        armConf.setValueString("arm", "DEFAULTSERVICES", "consensus set secretsharing");
        Program.configureLogging("DEBUG");
        // for testing, we vote with the same voter identity multiple times,
        // does not matter here ...
        final EcdsaPrivateKey privateKey = EcdsaPrivateKey.createRandom();
        final EcdsaPublicKey publicKey = privateKey.getPublicKey();
        final TestingSubsystem ts = new TestingSubsystem("arm", armConf.writeTemp().getAbsolutePath());
        KeyGeneration kg = new KeyGeneration(ts.getConfiguration(), new PeerIdentity[0],
                HashCode.random(), AbsoluteTime.now(), AbsoluteTime.now().add(RelativeTime.fromSeconds(5)),
                1, new SecretReadyCallback() {
            @Override
            public void onSecretReady(Share share) {
                Assert.assertNotNull(share);
                Ciphertext sum = Ciphertext.identity();
                Decryption decryption = new Decryption(ts.getConfiguration(), share, sum,
                        AbsoluteTime.now(), AbsoluteTime.now().add(RelativeTime.fromSeconds(5)), new DecryptCallback() {
                    @Override
                    public void onResult(Plaintext plaintext) {
                        Assert.assertNotNull(plaintext);
                        System.out.println("Plaintext: " + plaintext);
                        long x[] = plaintext.bruteForceDiscreteLog(0, new BigInteger[0]);
                        Assert.assertNotNull(x);
                        success.set(true);
                    }
                });
            }
        });
        Scheduler.run();
        Assert.assertTrue(success.get());
    }


    @Test
    public void testVoteTally() {
        final Wrapper<Boolean> success = new Wrapper<Boolean>();
        Configuration armConf = new Configuration();
        armConf.setValueString("arm", "DEFAULTSERVICES", "consensus set secretsharing");
        Program.configureLogging("DEBUG");
        // for testing, we vote with the same voter identity multiple times,
        // does not matter here ...
        final EcdsaPrivateKey privateKey = EcdsaPrivateKey.createRandom();
        final EcdsaPublicKey publicKey = privateKey.getPublicKey();
        final TestingSubsystem ts = new TestingSubsystem("arm", armConf.writeTemp().getAbsolutePath());
        KeyGeneration kg = new KeyGeneration(ts.getConfiguration(), new PeerIdentity[0],
                HashCode.random(), AbsoluteTime.now(), AbsoluteTime.now().add(RelativeTime.fromSeconds(5)),
                1, new SecretReadyCallback() {
            @Override
            public void onSecretReady(Share share) {
                Assert.assertNotNull(share);
                EncryptedVote encryptedVote1 = EncryptedVote.fromChoice(1, share.publicKey, publicKey, generators2);
                EncryptedVote encryptedVote2 = EncryptedVote.fromChoice(0, share.publicKey, publicKey, generators2);
                EncryptedVote encryptedVote3 = EncryptedVote.fromChoice(0, share.publicKey, publicKey, generators2);

                Ciphertext sum = Ciphertext.identity().multiply(encryptedVote1.v.multiply(encryptedVote2.v).multiply(encryptedVote3.v));

                Decryption decryption = new Decryption(ts.getConfiguration(), share, sum,
                        AbsoluteTime.now(), AbsoluteTime.now().add(RelativeTime.fromSeconds(5)), new DecryptCallback() {

                    @Override
                    public void onResult(Plaintext plaintext) {
                        Assert.assertNotNull(plaintext);
                        long[] t = plaintext.bruteForceDiscreteLog(3, generators2);
                        Assert.assertNotNull(t);
                        Assert.assertEquals(t[0], 2);
                        Assert.assertEquals(t[1], 1);
                        success.set(true);
                    }
                });
            }
        });
        Scheduler.run();
        Assert.assertTrue(success.get());
    }


    @Test
    public void testSerialization() {
        final Wrapper<Boolean> success = new Wrapper<Boolean>();
        Configuration armConf = new Configuration();
        armConf.setValueString("arm", "DEFAULTSERVICES", "consensus set secretsharing");
        Program.configureLogging("DEBUG");
        final TestingSubsystem ts = new TestingSubsystem("arm", armConf.writeTemp().getAbsolutePath());
        final EcdsaPrivateKey privateKey = EcdsaPrivateKey.createRandom();
        final EcdsaPublicKey publicKey = privateKey.getPublicKey();
        final KeyGeneration kg = new KeyGeneration(ts.getConfiguration(), new PeerIdentity[0],
                HashCode.random(), AbsoluteTime.now(), AbsoluteTime.now().add(RelativeTime.fromSeconds(5)),
                1, new SecretReadyCallback() {
            @Override
            public void onSecretReady(Share share) {
                Assert.assertNotNull(share);
                EncryptedVote encryptedVote1 = EncryptedVote.fromChoice(1, share.publicKey, publicKey, generators2);
                Assert.assertTrue(encryptedVote1.disjunctionZkp.verifyChallenge());
                Configuration c = new Configuration();
                encryptedVote1.writeToConfiguration(c);
                System.out.println("Challenge:");
                System.out.println(Arrays.toString(encryptedVote1.disjunctionZkp.challenge_c));
                System.out.println("ZKP:");
                System.out.println(Arrays.toString(Construct.toBinary(encryptedVote1.disjunctionZkp)));
                System.out.println("configuration: ");
                System.out.println(c.serialize());
                EncryptedVote encryptedVote2 = EncryptedVote.parseFromConfiguration(c, publicKey);
                Assert.assertTrue(encryptedVote1.disjunctionZkp.verifyChallenge());
                Assert.assertEquals(encryptedVote1.v, encryptedVote2.v);
                success.set(true);
            }

        });
        Scheduler.run();
        Assert.assertTrue(success.get());
    }

}

