#ifndef __PRSONA_SERVER_ENTITY_HPP
#define __PRSONA_SERVER_ENTITY_HPP

#include <vector>

#include "client.hpp"
#include "server.hpp"
#include "proof.hpp"

class PrsonaServerEntity {
    public:
        // CONSTRUCTORS
        PrsonaServerEntity(size_t numServers);

        // BASIC PUBLIC SYSTEM INFO GETTERS
        BGNPublicKey get_bgn_public_key() const;
        BGNPublicKey get_bgn_public_key(
            size_t which
        ) const;
        Twistpoint get_blinding_generator() const;
        Twistpoint get_blinding_generator(
            size_t which
        ) const;
        Twistpoint get_blinding_generator(
            std::vector<Proof>& pi
        ) const;
        Twistpoint get_blinding_generator(
            std::vector<Proof>& pi,
            size_t which
        ) const;
        Twistpoint get_fresh_generator() const;
        Twistpoint get_fresh_generator(
            size_t which
        ) const;
        Twistpoint get_fresh_generator(
            std::vector<Proof>& pi
        ) const;
        Twistpoint get_fresh_generator(
            std::vector<Proof>& pi,
            size_t which
        ) const;
        size_t get_num_clients() const;
        size_t get_num_clients(
            size_t which
        ) const;
        size_t get_num_servers() const;
        size_t get_num_servers(
            size_t which
        ) const;

        // ENCRYPTED DATA GETTERS
        std::vector<TwistBipoint> get_current_votes_by(
            Proof& pi,
            const Twistpoint& shortTermPublicKey
        ) const;
        std::vector<TwistBipoint> get_current_votes_by(
            Proof& pi,
            const Twistpoint& shortTermPublicKey,
            size_t which
        ) const;
        std::vector<std::vector<TwistBipoint>> get_all_current_votes(
            Proof& pi
        ) const;
        std::vector<std::vector<TwistBipoint>> get_all_current_votes(
            Proof& pi,
            size_t which
        ) const;
        EGCiphertext get_current_user_encrypted_tally(
            Proof& pi,
            const Twistpoint& shortTermPublicKey
        ) const;
        EGCiphertext get_current_user_encrypted_tally(
            Proof& pi,
            const Twistpoint& shortTermPublicKey,
            size_t which
        ) const;
        CurveBipoint get_current_server_encrypted_tally(
            Proof& pi,
            const Twistpoint& shortTermPublicKey
        ) const;
        CurveBipoint get_current_server_encrypted_tally(
            Proof& pi,
            const Twistpoint& shortTermPublicKey,
            size_t which
        ) const;
        std::vector<Twistpoint> get_current_pseudonyms(
            Proof& pi
        ) const;
        std::vector<Twistpoint> get_current_pseudonyms(
            Proof& pi,
            size_t which
        ) const;

        // PROOF COMMITMENT GETTERS
        void get_other_vote_row_commitments(
            std::vector<Proof>& pi,
            const Twistpoint& request
        ) const;
        void get_other_vote_row_commitments(
            std::vector<Proof>& pi,
            const Twistpoint& request,
            size_t whichNot
        ) const;
        void get_other_vote_matrix_commitments(
            std::vector<Proof>& pi
        ) const;
        void get_other_vote_matrix_commitments(
            std::vector<Proof>& pi,
            size_t whichNot
        ) const;
        void get_other_user_tally_commitments(
            std::vector<Proof>& pi,
            const Twistpoint& request
        ) const;
        void get_other_user_tally_commitments(
            std::vector<Proof>& pi,
            const Twistpoint& request,
            size_t whichNot
        ) const;
        void get_other_server_tally_commitments(
            std::vector<Proof>& pi,
            const Twistpoint& request
        ) const;
        void get_other_server_tally_commitments(
            std::vector<Proof>& pi,
            const Twistpoint& request,
            size_t whichNot)
        const;
        void get_other_pseudonyms_commitments(
            std::vector<Proof>& pi
        ) const;
        void get_other_pseudonyms_commitments(
            std::vector<Proof>& pi,
            size_t whichNot
        ) const;

        // CLIENT INTERACTIONS
        void add_new_client(
            PrsonaClient& newUser);
        void add_new_client(
            PrsonaClient& newUser,
            size_t which);
        bool receive_vote(
            const std::vector<Proof>& pi,
            const std::vector<TwistBipoint>& newVotes,
            const Twistpoint& shortTermPublicKey);
        bool receive_vote(
            const std::vector<Proof>& pi,
            const std::vector<TwistBipoint>& newVotes,
            const Twistpoint& shortTermPublicKey,
            size_t which);
        void transmit_new_user_data(
            const std::vector<Proof>& pi,
            PrsonaClient& newUser
        ) const;
        void transmit_new_user_data(
            const std::vector<Proof>& pi,
            PrsonaClient& newUser,
            size_t which
        ) const;
        void transmit_updates(
            PrsonaClient& currUser
        ) const;
        void transmit_updates(
            PrsonaClient& currUser,
            size_t which
        ) const;

        // EPOCH
        void epoch();
        void epoch(
            size_t which);

        void print_scores() const;
        void print_votes() const;
        void print_current_votes_by(
            const Twistpoint& index
        ) const;

    private:
        std::vector<PrsonaServer> servers;

        // SCORE TALLYING
        void tally_scores(
            const Twistpoint& nextGenerator,
            std::vector<EGCiphertext>& userTallyScores,
            std::vector<CurveBipoint>& serverTallyScores,
            size_t which);
        void distribute_tallied_scores(
            const std::vector<EGCiphertext>& userScores,
            const std::vector<CurveBipoint>& serverScores);
        
        // BINARY SEARCH
        size_t binary_search(
            const Twistpoint& shortTermPublicKey,
            size_t which
        ) const;
}; 

#endif