#ifndef lock_server_cache_h
#define lock_server_cache_h

#include <queue>
#include <string>
#include "lock_protocol.h"
#include "lock_client.h"
#include "rpc/rpc.h"
#include "lock_server.h"
#include "rsm_state_transfer.h"
#include "consistent_hash.h"

class lock_server_cache : rsm_state_transfer, lock_server {    
    
 private:
    rpcc cl;
    rpcs *rs;
    consistent_hash *ch; 
    struct sockaddr_in dst;
    std::string hostname;
    int cid;
    pthread_mutex_t rpc_ticker_mutex;
    int rpc_ticker;

    static const int DEBUG = 1;
        
    class cached_lock : rsm_state_transfer{
    public:
        pthread_mutex_t lock_mutex;
        bool taken;
        std::string owner;
        std::string last_owner;
        int times_acquired;
        std::deque<std::string> requesters;
        cached_lock(std::string);
        std::string marshal_state();
        void unmarshal_state(std::string);
    };

    //revoke queue
    pthread_mutex_t revoke_mutex;
    pthread_cond_t revoke_cond;
    std::queue<std::string> revokes;

    //grant queue
    pthread_mutex_t grant_mutex;
    pthread_cond_t grant_cond;
    std::queue<std::string> grants;

    //locks data structure
    pthread_mutex_t locks_mutex;
    std::map<std::string,cached_lock*> locks;
    //now clients are mapped from hostname to serviced RPCs list
    std::map<std::string,rpc_lst*> hostname_rpc_lst;
    member myself();
    int get_new_id();
 public:
    lock_server_cache(rpcs*,consistent_hash*, sockaddr_in, std::string, int);
    lock_server_cache();
    lock_protocol::status stat(std::string, int &);
    void revoker();
    void granter();
    lock_protocol::status acquire(std::string, int, std::string, std::string &);
    lock_protocol::status release(std::string, int, std::string, int &);
    

    int nr_locks();
  
    void freeze_state();
    void unfreeze_state();

    // STATE TRANSFERS
    // PRECONDITION: locks_mutex mutex is held
    void get_metadata_state(member m); //get the metadata a joining clients is a manager from for the previous in the ring
    metadata_protocol::status divide_metadata_state(member m, std::string & state); // sends the specific metadata to a new client in the ring
    void yield_metadata_state(); //sends all the metadata it has to the previous in the ring
    metadata_protocol::status receive_metadata_state(std::string state, int &); //handler called on a client to receive metadata sent by a leaving client
    std::string marshal_lock_state(std::map<std::string, cached_lock*>);
    void unmarshal_lock_state(std::string, std::map<std::string,cached_lock*> &);

    std::string marshal_state();
    void unmarshal_state(std::string);
};

#endif
