#ifndef __PRSONA_BASE_HPP
#define __PRSONA_BASE_HPP

#include <vector>

#include "Curvepoint.hpp"
#include "Bipoint.hpp"
#include "Scalar.hpp"

#include "EGCiphertext.hpp"
#include "proof.hpp"

class PrsonaBase {
    public:
        static size_t MAX_ALLOWED_VOTE;
        static Twistpoint EL_GAMAL_GENERATOR;

        // SETUP FUNCTIONS
        static void init();
        static void set_server_malicious();
        static void set_client_malicious();
        static void set_lambda(size_t lambda);

        // CONST GETTERS
        static size_t get_max_allowed_vote();
        Twistpoint get_blinding_generator() const;
        Twistpoint get_blinding_generator(
            std::vector<Proof>& pi
        ) const;

        // BINARY SEARCH
        size_t binary_search(
            const std::vector<Twistpoint> list,
            const Twistpoint& index
        ) const;

    protected:
        // Essentially constants, true for both servers and clients
        static Scalar SCALAR_N;
        static Scalar DEFAULT_TALLY;
        static Scalar DEFAULT_VOTE;

        static bool SERVER_IS_MALICIOUS;
        static bool CLIENT_IS_MALICIOUS;
        static size_t LAMBDA;
        
        std::vector<Proof> elGamalBlindGeneratorProof;
        Twistpoint elGamalBlindGenerator;

        // PRIVATE ELEMENT SETTER
        bool set_EG_blind_generator(
            const std::vector<Proof>& pi,
            const Twistpoint& currGenerator,
            size_t numServers);

        // SCHNORR PROOFS
        Proof schnorr_generation(
            const Twistpoint& generator,
            const Twistpoint& commitment,
            const Scalar& log
        ) const;

        bool schnorr_verification(
            const Twistpoint& generator,
            const Twistpoint& commitment,
            const Scalar& c,
            const Scalar& z
        ) const;

        // OWNERSHIP PROOFS
        Proof generate_ownership_proof(
            const Twistpoint& generator,
            const Twistpoint& commitment,
            const Scalar& log
        ) const;

        bool verify_ownership_proof(
            const Proof& pi,
            const Twistpoint& generator,
            const Twistpoint& commitment
        ) const;

        // ITERATED SCHNORR PROOFS
        Proof add_to_generator_proof(
            const Twistpoint& currGenerator, 
            const Scalar& seed
        ) const;

        bool verify_generator_proof(
            const std::vector<Proof>& pi,
            const Twistpoint& currGenerator,
            size_t numServers
        ) const;

        // REPUTATION PROOFS
        std::vector<Proof> generate_reputation_proof(
            const Proof& ownershipProof,
            const EGCiphertext& commitment,
            const Scalar& currentScore,
            const Scalar& threshold,
            const Scalar& inverseKey,
            size_t numClients
        ) const;

        bool verify_reputation_proof(
            const std::vector<Proof>& pi,
            const Twistpoint& generator,
            const Twistpoint& owner,
            const EGCiphertext& commitment,
            const Scalar& threshold
        ) const;

        // VALID VOTE PROOFS
        std::vector<Proof> generate_vote_proof(
            const Proof& ownershipProof,
            const TwistBipoint& g,
            const TwistBipoint& h,
            const std::vector<bool>& replaces,
            const std::vector<TwistBipoint>& oldEncryptedVotes,
            const std::vector<TwistBipoint>& newEncryptedVotes,
            const std::vector<Scalar>& seeds,
            const std::vector<Scalar>& votes
        ) const;

        bool verify_vote_proof(
            const TwistBipoint& g,
            const TwistBipoint& h,
            const std::vector<Proof>& pi,
            const std::vector<TwistBipoint>& oldEncryptedVotes,
            const std::vector<TwistBipoint>& newEncryptedVotes,
            const Twistpoint& freshGenerator,
            const Twistpoint& owner
        ) const;

        // NEW USER PROOFS
        std::vector<Proof> generate_proof_of_added_user(
            const Scalar& twistBipointSeed,
            const Scalar& EGCiphertextSeed,
            const std::vector<Scalar>& curveBipointSelfSeeds,
            const std::vector<Scalar>& curveBipointOtherSeeds
        ) const;

        bool verify_proof_of_added_user(
            const std::vector<Proof>& pi,
            const Twistpoint& currentFreshGenerator,
            const Twistpoint& shortTermPublicKey,
            const TwistBipoint& curveG,
            const TwistBipoint& curveH,
            const CurveBipoint& twistG,
            const CurveBipoint& twistH,
            size_t selfIndex,
            const EGCiphertext& userEncryptedScore,
            const CurveBipoint& serverEncryptedScore,
            const std::vector<std::vector<TwistBipoint>> encryptedVoteMatrix
        ) const;

        // EPOCH PROOFS
        std::vector<Proof> generate_valid_permutation_proof(
            const std::vector<std::vector<Scalar>>& permutations,
            const std::vector<std::vector<Scalar>>& seeds,
            const std::vector<std::vector<Twistpoint>>& commits
        ) const;

        bool verify_valid_permutation_proof(
            const std::vector<Proof>& pi,
            const std::vector<std::vector<Twistpoint>>& commits
        ) const;

        template <typename T>
        std::vector<Proof> generate_proof_of_reordering(
            const std::vector<std::vector<Scalar>>& permutations,
            const std::vector<std::vector<Scalar>>& permutationSeeds,
            const std::vector<std::vector<Scalar>>& productSeeds,
            const std::vector<T>& oldValues,
            const std::vector<std::vector<Twistpoint>>& permutationCommits,
            const std::vector<std::vector<T>>& productCommits,
            const T& otherG,
            const T& otherH
        ) const;

        template <typename T>
        bool verify_proof_of_reordering(
            const std::vector<Proof>& pi,
            const std::vector<T>& oldValues,
            const std::vector<std::vector<Twistpoint>>& permutationCommits,
            const std::vector<std::vector<T>>& productCommits,
            const T& otherG,
            const T& otherH
        ) const;

        template <typename T>
        std::vector<Proof> generate_unbatched_proof_of_reordering(
            const std::vector<std::vector<Scalar>>& permutations,
            const std::vector<std::vector<Scalar>>& permutationSeeds,
            const std::vector<std::vector<Scalar>>& productSeeds,
            const std::vector<T>& oldValues,
            const std::vector<std::vector<Twistpoint>>& permutationCommits,
            const std::vector<std::vector<T>>& productCommits,
            const T& otherG,
            const T& otherH
        ) const;

        template <typename T>
        bool verify_unbatched_proof_of_reordering(
            const std::vector<Proof>& pi,
            const std::vector<T>& oldValues,
            const std::vector<std::vector<Twistpoint>>& permutationCommits,
            const std::vector<std::vector<T>>& productCommits,
            const T& otherG,
            const T& otherH
        ) const;

        template <typename T>
        std::vector<Proof> generate_batched_proof_of_reordering(
            const std::vector<std::vector<Scalar>>& permutations,
            const std::vector<std::vector<Scalar>>& permutationSeeds,
            const std::vector<std::vector<Scalar>>& productSeeds,
            const std::vector<T>& oldValues,
            const std::vector<std::vector<Twistpoint>>& permutationCommits,
            const std::vector<std::vector<T>>& productCommits,
            const T& otherG,
            const T& otherH
        ) const;

        template <typename T>
        bool verify_batched_proof_of_reordering(
            const std::vector<Proof>& pi,
            const std::vector<T>& oldValues,
            const std::vector<std::vector<Twistpoint>>& permutationCommits,
            const std::vector<std::vector<T>>& productCommits,
            const T& otherG,
            const T& otherH
        ) const;

        std::vector<Proof> generate_proof_of_reordering_plus_power(
            const std::vector<std::vector<Scalar>>& permutations,
            const Scalar& power,
            const std::vector<std::vector<Scalar>>& permutationSeeds,
            const std::vector<std::vector<Scalar>>& productSeeds,
            const std::vector<Twistpoint>& oldValues,
            const std::vector<std::vector<Twistpoint>>& permutationCommits,
            const std::vector<std::vector<Twistpoint>>& productCommits,
            const std::vector<std::vector<Twistpoint>>& seedCommits
        ) const;

        bool verify_proof_of_reordering_plus_power(
            const std::vector<Proof>& pi,
            const std::vector<Twistpoint>& oldValues,
            const std::vector<std::vector<Twistpoint>>& permutationCommits,
            const std::vector<std::vector<Twistpoint>>& productCommits,
            const std::vector<std::vector<Twistpoint>>& seedCommits
        ) const;

        std::vector<Proof> generate_unbatched_proof_of_reordering_plus_power(
            const std::vector<std::vector<Scalar>>& permutations,
            const Scalar& power,
            const std::vector<std::vector<Scalar>>& permutationSeeds,
            const std::vector<std::vector<Scalar>>& productSeeds,
            const std::vector<Twistpoint>& oldValues,
            const std::vector<std::vector<Twistpoint>>& permutationCommits,
            const std::vector<std::vector<Twistpoint>>& productCommits,
            const std::vector<std::vector<Twistpoint>>& seedCommits
        ) const;

        bool verify_unbatched_proof_of_reordering_plus_power(
            const std::vector<Proof>& pi,
            const std::vector<Twistpoint>& oldValues,
            const std::vector<std::vector<Twistpoint>>& permutationCommits,
            const std::vector<std::vector<Twistpoint>>& productCommits,
            const std::vector<std::vector<Twistpoint>>& seedCommits
        ) const;

        std::vector<Proof> generate_batched_proof_of_reordering_plus_power(
            const std::vector<std::vector<Scalar>>& permutations,
            const Scalar& power,
            const std::vector<std::vector<Scalar>>& permutationSeeds,
            const std::vector<std::vector<Scalar>>& productSeeds,
            const std::vector<Twistpoint>& oldValues,
            const std::vector<std::vector<Twistpoint>>& permutationCommits,
            const std::vector<std::vector<Twistpoint>>& productCommits,
            const std::vector<std::vector<Twistpoint>>& seedCommits
        ) const;

        bool verify_batched_proof_of_reordering_plus_power(
            const std::vector<Proof>& pi,
            const std::vector<Twistpoint>& oldValues,
            const std::vector<std::vector<Twistpoint>>& permutationCommits,
            const std::vector<std::vector<Twistpoint>>& productCommits,
            const std::vector<std::vector<Twistpoint>>& seedCommits
        ) const;

        std::vector<Proof> generate_user_tally_proofs(
            const std::vector<std::vector<Scalar>>& permutations,
            const Scalar& power,
            const Twistpoint& nextGenerator,
            const std::vector<std::vector<Scalar>>& permutationSeeds,
            const std::vector<std::vector<Scalar>>& userTallySeeds,
            const std::vector<Twistpoint>& currPseudonyms,
            const std::vector<Twistpoint>& userTallyMasks,
            const std::vector<Twistpoint>& userTallyMessages,
            const std::vector<std::vector<Twistpoint>>& permutationCommits,
            const std::vector<std::vector<Twistpoint>>& userTallyMaskCommits,
            const std::vector<std::vector<Twistpoint>>& userTallyMessageCommits,
            const std::vector<std::vector<Twistpoint>>& userTallySeedCommits
        ) const;

        bool verify_user_tally_proofs(
            const std::vector<Proof>& pi,
            const Twistpoint& nextGenerator,
            const std::vector<Twistpoint>& currPseudonyms,
            const std::vector<Twistpoint>& userTallyMasks,
            const std::vector<Twistpoint>& userTallyMessages,
            const std::vector<std::vector<Twistpoint>>& permutationCommits,
            const std::vector<std::vector<Twistpoint>>& userTallyMaskCommits,
            const std::vector<std::vector<Twistpoint>>& userTallyMessageCommits,
            const std::vector<std::vector<Twistpoint>>& userTallySeedCommits
        ) const;

        std::vector<Proof> generate_unbatched_user_tally_proofs(
            const std::vector<std::vector<Scalar>>& permutations,
            const Scalar& power,
            const Twistpoint& nextGenerator,
            const std::vector<std::vector<Scalar>>& permutationSeeds,
            const std::vector<std::vector<Scalar>>& userTallySeeds,
            const std::vector<Twistpoint>& currPseudonyms,
            const std::vector<Twistpoint>& userTallyMasks,
            const std::vector<Twistpoint>& userTallyMessages,
            const std::vector<std::vector<Twistpoint>>& permutationCommits,
            const std::vector<std::vector<Twistpoint>>& userTallyMaskCommits,
            const std::vector<std::vector<Twistpoint>>& userTallyMessageCommits,
            const std::vector<std::vector<Twistpoint>>& userTallySeedCommits
        ) const;

        bool verify_unbatched_user_tally_proofs(
            const std::vector<Proof>& pi,
            const Twistpoint& nextGenerator,
            const std::vector<Twistpoint>& currPseudonyms,
            const std::vector<Twistpoint>& userTallyMasks,
            const std::vector<Twistpoint>& userTallyMessages,
            const std::vector<std::vector<Twistpoint>>& permutationCommits,
            const std::vector<std::vector<Twistpoint>>& userTallyMaskCommits,
            const std::vector<std::vector<Twistpoint>>& userTallyMessageCommits,
            const std::vector<std::vector<Twistpoint>>& userTallySeedCommits
        ) const;

        std::vector<Proof> generate_batched_user_tally_proofs(
            const std::vector<std::vector<Scalar>>& permutations,
            const Scalar& power,
            const Twistpoint& nextGenerator,
            const std::vector<std::vector<Scalar>>& permutationSeeds,
            const std::vector<std::vector<Scalar>>& userTallySeeds,
            const std::vector<Twistpoint>& currPseudonyms,
            const std::vector<Twistpoint>& userTallyMasks,
            const std::vector<Twistpoint>& userTallyMessages,
            const std::vector<std::vector<Twistpoint>>& permutationCommits,
            const std::vector<std::vector<Twistpoint>>& userTallyMaskCommits,
            const std::vector<std::vector<Twistpoint>>& userTallyMessageCommits,
            const std::vector<std::vector<Twistpoint>>& userTallySeedCommits
        ) const;

        bool verify_batched_user_tally_proofs(
            const std::vector<Proof>& pi,
            const Twistpoint& nextGenerator,
            const std::vector<Twistpoint>& currPseudonyms,
            const std::vector<Twistpoint>& userTallyMasks,
            const std::vector<Twistpoint>& userTallyMessages,
            const std::vector<std::vector<Twistpoint>>& permutationCommits,
            const std::vector<std::vector<Twistpoint>>& userTallyMaskCommits,
            const std::vector<std::vector<Twistpoint>>& userTallyMessageCommits,
            const std::vector<std::vector<Twistpoint>>& userTallySeedCommits
        ) const;

        // SERVER AGREEMENT PROOFS
        Proof generate_valid_vote_row_proof(
            const std::vector<TwistBipoint>& commitment
        ) const;
        Proof generate_valid_vote_matrix_proof(
            const std::vector<std::vector<TwistBipoint>>& commitment
        ) const;
        Proof generate_valid_user_tally_proof(
            const EGCiphertext& commitment
        ) const;
        Proof generate_valid_server_tally_proof(
            const CurveBipoint& commitment
        ) const;
        Proof generate_valid_pseudonyms_proof(
            const std::vector<Twistpoint>& commitment
        ) const;
        
        bool verify_valid_vote_row_proof(
            const std::vector<Proof>& pi,
            const std::vector<TwistBipoint>& commitment
        ) const;
        bool verify_valid_vote_matrix_proof(
            const std::vector<Proof>& pi,
            const std::vector<std::vector<TwistBipoint>>& commitment
        ) const;
        bool verify_valid_user_tally_proof(
            const std::vector<Proof>& pi,
            const EGCiphertext& commitment
        ) const;
        bool verify_valid_server_tally_proof(
            const std::vector<Proof>& pi,
            const CurveBipoint& commitment
        ) const;
        bool verify_valid_pseudonyms_proof(
            const std::vector<Proof>& pi,
            const std::vector<Twistpoint>& commitment
        ) const;
};

#endif