#ifndef __PRSONA_CLIENT_HPP
#define __PRSONA_CLIENT_HPP

#include <unordered_map>
#include <vector>

#include "Curvepoint.hpp"
#include "Scalar.hpp"
#include "BGN.hpp"

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

class PrsonaClient : public PrsonaBase {
    public:
        // CONSTRUCTORS
        PrsonaClient(
            const std::vector<Proof>& generatorProof,
            const Twistpoint& elGamalBlindGenerator,
            const BGNPublicKey& serverPublicKey,
            size_t numServers);

        // BASIC PUBLIC SYSTEM INFO GETTERS
        Twistpoint get_short_term_public_key() const;
        Twistpoint get_short_term_public_key(
            Proof &pi
        ) const;

        // SERVER INTERACTIONS
        std::vector<TwistBipoint> make_votes(
            std::vector<Proof>& validVoteProof,
            const std::vector<Proof>& serverProof,
            const std::vector<TwistBipoint>& oldEncryptedVotes,
            const std::vector<Scalar>& votes,
            const std::vector<bool>& replaces
        ) const;
        bool receive_fresh_generator(
            const std::vector<Proof>& pi,
            const Twistpoint& freshGenerator);
        bool receive_vote_tally(
            const std::vector<Proof>& pi,
            const EGCiphertext& score);
        bool receive_new_user_data(
            const std::vector<Proof>& mainProof,
            const std::vector<Proof>& serverEncryptedScoreProof,
            const CurveBipoint& serverEncryptedScore,
            const std::vector<Proof>& userEncryptedScoreProof,
            const EGCiphertext& userEncryptedScore,
            const std::vector<Proof>& voteMatrixProof,
            const std::vector<std::vector<TwistBipoint>>& encryptedVoteMatrix,
            const std::vector<Proof>& pseudonymsProof,
            const std::vector<Twistpoint>& currentPseudonyms);

        // REPUTATION PROOFS
        std::vector<Proof> generate_reputation_proof(
            const Scalar& threshold,
            size_t numClients
        ) const;
        bool verify_reputation_proof(
            const std::vector<Proof>& pi,
            const Twistpoint& shortTermPublicKey,
            const Scalar& threshold,
            const std::vector<Proof>& encryptedScoreProof,
            const EGCiphertext& encryptedScore
        ) const;

        // NEEDED FOR TESTING PROOFS
        Scalar get_score() const;

    private:
        // Things bound to the servers permanently
        const BGNPublicKey serverPublicKey;
        const size_t numServers;

        // Things bound to the servers (but change regularly)
        Twistpoint currentFreshGenerator;

        // Things bound to this user permanently
        Scalar longTermPrivateKey;
        Scalar inversePrivateKey;

        // Things bound to this user (but change regularly)
        EGCiphertext currentEncryptedScore;
        Scalar currentScore;

        // Things related to making decryption more efficient
        std::unordered_map<Twistpoint, Scalar, TwistpointHash>
            decryption_memoizer;
        Scalar max_checked;

        // SCORE DECRYPTION
        Scalar decrypt_score(
            const EGCiphertext& score);

        // OWNERSHIP OF STPK PROOFS
        Proof generate_ownership_proof() const;

        // VALID VOTE PROOFS
        std::vector<Proof> generate_vote_proof(
            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;
}; 

#endif