// lock client interface.

#ifndef lock_client_cache_h
#define lock_client_cache_h

#include <queue>
#include <string>
#include <list>
#include "lock_protocol.h"
#include "rpc.h"
#include "lock_client.h"
#include "lock_server.h"
#include "lock_server_cache.h"
#include "extent_client.h"
#include "consistent_hash.h"


// Classes that inherit lock_release_user can override dorelease so that
// that they will be called when lock_client releases a lock.
// You will not need to do anything with this class until Lab 6

class lock_release_user {
 public:
  virtual void dorelease(std::string) = 0;
  virtual void getextent(std::string , std::string) = 0;
     //gets data from cache
  virtual extent_protocol::status get(extent_protocol::extentid_t eid, std::string & buf) = 0;
  virtual extent_protocol::status getattr(extent_protocol::extentid_t eid, extent_protocol::attr & buf) = 0;
  
  virtual ~lock_release_user() {};
};

class extent_transfer : public lock_release_user {
        public:

                extent_transfer(extent_client * ec);
		void getextent(std::string addr, std::string id);
                void dorelease(std::string);
		//gets data from cache
    		extent_protocol::status get(extent_protocol::extentid_t eid, std::string & buf);
   		extent_protocol::status getattr(extent_protocol::extentid_t eid, extent_protocol::attr & buf);
		
        private:
                extent_client * ec;
		rpcc * cl;
};

class lock_client_cache : public lock_client {
 private:
    //consistent hashing library 
    class consistent_hash *ch;
    //lock server hosted by the client
    class lock_server_cache *ls;

    class lock_release_user * et;

    //client id
    int cid;

    //random port to listen on
    int rlock_port;
    //hostname of thise client
    std::string hostname;
    
    //local representation of a lock
    struct cached_lock{
        pthread_mutex_t lock_mutex;
        pthread_cond_t lock_cond;
        int state;
        std::string last_owner;
	
        enum lockstates {FREE=5504, LOCKED, ACQUIRING, RELEASING, NONE};        
    };
    
    //locks data structure and corresponding read lock
    pthread_mutex_t lock_cache_mutex;
    std::map<std::string,cached_lock*> lock_cache;
    
    //release queue and corresponding locks
    pthread_mutex_t release_mutex;
    pthread_cond_t release_cond;
    std::queue<std::string> release_queue;
    
    //function to get an id for each rpc 
    int get_new_id();

    //list of RPCs this client has every received
    rpc_lst *server_rpc_lst;

    member myself();
    //JOIN AND LEAVE
    void leave();
    void join();
    
    
 public:
    static int last_port;
    lock_client_cache(sockaddr_in xdst);
    lock_client_cache(sockaddr_in xdst, int cid);
    virtual ~lock_client_cache() {leave();};
    virtual lock_protocol::status acquire(std::string);
    virtual lock_protocol::status acquire(std::string, std::string &);
    virtual lock_protocol::status release(std::string);
    virtual lock_protocol::status revoke(int, std::string, int &);
    virtual lock_protocol::status grant(int, std::string, std::string, int &);
    void releaser();
    lock_protocol::status nr_locks(int, int &r ) { r = ls->nr_locks(); return lock_protocol::OK;};
    void set_extent_transfer(extent_transfer * _et) {et = _et; }

    void stats();
   

   //gets data from cache
    extent_protocol::status get(extent_protocol::extentid_t eid, std::string & buf);
    extent_protocol::status getattr(extent_protocol::extentid_t eid, extent_protocol::attr & buf);


    metadata_protocol::status join_member(member m, int &);
    metadata_protocol::status remove_member(member m, int &);
};
#endif


