#ifndef __SPIR_HPP__
#define __SPIR_HPP__

#include <string>
#include <stdint.h>

using std::string; 

class SPIR {
public:
    typedef uint64_t DBEntry;  // The type of each DB entry (64 bits)

    static void init(uint32_t nthreads);  // Call this once at startup
};

class SPIR_Client {
public:
    // constructor and destructor
    SPIR_Client(uint8_t r, string &pub_params); // 2^r records in the database; pub_params will be _filled in_
    ~SPIR_Client();

    // preprocessing
    string preproc(uint32_t num_pirs); // returns the string to send to the server

    void preproc_finish(const string &server_preproc);

    // SPIR query for index idx
    string query(size_t idx); // returns the string to send to the server

    // process the server's response to yield the server's db[(idx + rot)%N] + blind
    // where N=2^r, idx is provided by the client above, and
    // db, rot, and blind are provided by the server below
    SPIR::DBEntry query_finish(const string &server_reply);

private:
    void *client;
    SPIR_Client() = default;
    SPIR_Client(const SPIR_Client&) = delete;
    SPIR_Client& operator=(const SPIR_Client&) = delete;
};

class SPIR_Server {
public:
    // constructor and destructor
    SPIR_Server(uint8_t r, const string &client_pub_params);
    ~SPIR_Server();

    // preprocessing
    string preproc_process(const string &client_preproc); // returns the string to reply to the client
  
    // SPIR query on the given database of N=2^r records, each of type DBEntry
    // rotate the database by rot, and blind each entry in the database additively with blind
    // returns the string to reply to the client
    string query_process(const string &client_query, const SPIR::DBEntry *db,
        size_t rot, SPIR::DBEntry blind);

private:
    void *server;
    SPIR_Server() = default;
    SPIR_Server(const SPIR_Server&) = delete;
    SPIR_Server& operator=(const SPIR_Server&) = delete;
};

#endif
