// lock client interface.

#ifndef lock_client_cache_h

#define lock_client_cache_h

#include <string>
#include "lock_protocol.h"
#include "rpc.h"
#include "lock_client.h"
#include "extent_client.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 ~lock_release_user() {};
};

class yfs_lock_release_user : public lock_release_user {
	public:
		
		yfs_lock_release_user(extent_client * ec);
		void dorelease(std::string);
		
	private: 
		extent_client * ec;
};

// SUGGESTED LOCK CACHING IMPLEMENTATION PLAN:
//
// to work correctly for lab 7, this design has most of the complexity
// on the client.  all the requests on the server run till 
// completion and threads wait on condition variables on the client to
// wait for a lock.  this allows the server to be replicated using the
// replicated state machine approach.
//
// On the client a lock can be in several states:
//  - free: client owns the lock and no thread has it
//  - locked: client owns the lock and a thread has it
//  - acquiring: the client is acquiring ownership
//  - releasing: the client is releasing ownership
//
// in the state acquiring and locked there may be several threads
// waiting for the lock, but the first thread in the list interacts
// with the server and wakes up the threads when its done (released
// the lock).  a thread in the list is identified by its thread id
// (tid).
//
// a thread is in charge of getting a lock: if the server cannot grant
// it the lock, the thread will receive a retry reply.  at some point
// later, the server may send the thread a grant RPC, which is a hint
// that the lock may be available and the thread should retry its
// request.  
//
// once a thread has acquired a lock, the client caches the lock. it
// cannot grant the lock to other threads on the client without
// interacting with the server. the server must send the client a
// revoke request to get the lock back.  this request is a hint to the
// client that the current holder should send the lock back to the
// server when it releases the lock or right now if no thread on the
// client is holding the lock.  when receiving a revoke request, the
// client adds to a list and wakes up a revoker thread, which returns
// the lock the server as soon it is free.
//
// a challenge in the implementation is that grant and revoke requests
// can be out of order with the acquire and release requests.  that
// is, a client may receive a revoke request before it has received
// the positive acknowledgement on its acquire request.  similarly, a
// client may receive a grant before it has received a response on its
// initial acquire request.  a flag field is used to record if a grant
// has been received.
//
// lost, duplicated, and delayed requests raises yet another set of
// challenges, similar to the ones in lab.  we use the same solution
// as in lab 1.
//
// the actual releasing is done in a separate a thread to avoid
// deadlocks and to ensure that revoke and grant RPCs from the server
// run to completion (i.e., the revoke RPC cannot do the release when
// the lock is free.
// 

typedef struct info {
	int type;
	pthread_t tid;
	int nonce;
} ;

typedef struct rev_info { //for the rev_list and rev_vector
	std::string name;
	int nonce;
};

class lock_client_cache : public lock_client {
 
	
private:
  class lock_release_user *lu;
  int rlock_port;
  std::string hostname;
  std::map<std::string, info> data;
  int nonce;
  int cid;
  
  const static int FREE = 0; //a thread from this client released the lock, and lock stays on client and is free
  const static int LOCKED = 1; //a thread is holding the lock on this client
  const static int NONE = 2; // this client does not have the lock
  const static int ACQUIRING = 3; //this client is in process of acquiring the lock
  const static int RELEASING = 4; // ??? NOT USED
  
  const static int DEBUG = 0;
  
  pthread_mutex_t data_lock; //data and nonce
  pthread_cond_t lock_released_cond; //when a lock is released by a thread of the client id or when a grant was received to it
  
  std::vector<rev_info> *rev_vector;
  int rev_index; //, rev_end;
  std::list<rev_info> *rev_list;
  
  bool isfree(std::string);  
  bool isnone(std::string);
  bool islocked(std::string);
  bool isacquiring(std::string name);
  bool exists(std::string name);
  

 public:
  static int last_port;
  lock_client_cache(sockaddr_in xdst, class lock_release_user *l = 0);
  virtual ~lock_client_cache() {};
  lock_protocol::status acquire(std::string);
  lock_protocol::status release(std::string);
  lock_protocol::status grant_handler(std::string name, int nonce, int & r);
  lock_protocol::status revoke_handler(std::string name, int nonce,  int & r);
  void c(std::string s);
  void c(int s);
  void releaser();
};
#endif


