//  Client.cpp
//    Implementation of class Client
//

#include "stdio.h"
#include "Manager.h"
#include "NASD.h"
#include "client.h"

extern Manager * manager;
extern NasdServer * nserver[];
extern Client * nclient[];
extern ClientServer * clientserver;
extern unsigned client_refratio;
extern unsigned network_ratio;

////////////////////////////// implementation of clientserver ///////////////////////

ClientServer::ClientServer()
{
  int i;
  struct ClientBlock *cb;

  UsedList = new ClientBlock;
  UsedList->next = UsedList;
  UsedList->prev = UsedList;

  FreeList = NULL;

  for (i = 0 ; i < DEFAULTCACHESIZE ; i ++)
    {
      cb = new ClientBlock;

      cb->c_num = 0;
      cb->d_num = 0; 
      cb->i_num = 0;
      cb->b_num = 0;

      cb->next = FreeList;
      FreeList = cb;
    }

  for (i = 0 ; i < HASHTABLESIZE ; i ++)
    {
      HT[i].prev = NULL;
      HT[i].next = NULL;
      HT[i].up = &(HT[i]);
      HT[i].down = &(HT[i]);
    } // end of for

  putcount = 0;
  missblock = 0;
  totalblock = 0;

  cachesize = DEFAULTCACHESIZE;
  globalused = 0;
}

ClientServer::ClientServer(unsigned csize)
{
  int i;
  struct ClientBlock *cb;

  UsedList = new ClientBlock;
  UsedList->next = UsedList;
  UsedList->prev = UsedList;

  FreeList = NULL;

  for (i = 0 ; i < csize ; i ++)
    {
      cb = new ClientBlock;

      cb->c_num = 0;
      cb->d_num = 0; 
      cb->i_num = 0;
      cb->b_num = 0;

      cb->next = FreeList;
      FreeList = cb;
    }

  for (i = 0 ; i < HASHTABLESIZE ; i ++)
    {
      HT[i].prev = NULL;
      HT[i].next = NULL;
      HT[i].up = &(HT[i]);
      HT[i].down = &(HT[i]);
    } // end of for

  putcount = 0;
  missblock = 0;
  totalblock = 0;

  cachesize = csize;
  globalused = 0;
}

ClientServer::~ClientServer()
{
  struct ClientBlock *cb;

  // free the used list
  cb = UsedList->next;

  while (cb != UsedList)
    {
      UsedList->next = cb->next;
      cb->next->prev = UsedList;

      delete cb;

      cb = UsedList->next;
    } // end of while

  delete UsedList;

  // free the FreeList
  while (FreeList)
    {
      cb = FreeList->next;

      delete FreeList;
      FreeList = cb;
    } // end of while
}

int ClientServer::LookUp(unsigned cid, unsigned idev, unsigned inode, unsigned bnum)
{
  struct ClientBlock * cb;
  unsigned hv;

  totalblock ++;

  //cb = UsedList->next;
  hv = HASHVALUE(inode, bnum);
  cb = HT[hv].down;

  //  while (cb != UsedList)
  while (cb != &(HT[hv]))
    {
      if (cb->flag == FLAG_USED && cb->c_num == cid && cb->d_num == idev 
	  && cb->i_num == inode && cb->b_num == bnum)
	// cache hit
	{
	  // move cb to the front
	  cb->prev->next = cb->next;
	  cb->next->prev = cb->prev;

	  cb->next = UsedList->next;
	  cb->prev = UsedList;

	  UsedList->next->prev = cb;
	  UsedList->next = cb;

	  return 1;
	}

      //cb = cb->next;
      cb = cb->down;
    } // end of while

  missblock ++;

  return 0;
}

// put data to cache
// return 1 means there is free block, return 2 means one block is replaced
int ClientServer::TransferData(unsigned cid, unsigned idev, unsigned inode, unsigned bnum, 
			       unsigned rcount, TimeBD &elapse, unsigned &netcount)
{
  struct ClientBlock * cb;
  unsigned hv;

  elapse.disktime = 0;
  elapse.nettime = 0;
  elapse.memtime = MEMACCESSTIME;

  putcount ++; // means the number of tranfers for storage server
  netcount = 0;

  hv = HASHVALUE(inode, bnum);

  if (FreeList) // not empty
    {
      cb = FreeList;
      FreeList = cb->next;
      
      cb->flag = FLAG_USED;
      cb->c_num = cid;
      cb->d_num = idev;
      cb->i_num = inode;
      cb->b_num = bnum;
      cb->refcount = rcount;

      // insert at the front of UsedList
      cb->prev = UsedList;
      cb->next = UsedList->next;
      UsedList->next->prev = cb;
      UsedList->next = cb;

      // insert into the hash table
      cb->up = &(HT[hv]);
      cb->down = HT[hv].down;
      HT[hv].down->up = cb;
      HT[hv].down = cb;

      globalused ++;

      return 1;
    }
  else // replace the block at the end of UsedList
    {
      struct ClientBlock * upblock;
      struct ClientBlock * downblock;

      cb = UsedList->prev;
      
      // remove it
      UsedList->prev = cb->prev;
      cb->prev->next = UsedList;

      // remove it from hash table
      upblock = cb->up;
      downblock = cb->down;
      upblock->down = downblock;
      downblock->up = upblock;

      cb->flag = FLAG_USED;
      cb->c_num = cid;
      cb->d_num = idev;
      cb->i_num = inode;
      cb->b_num = bnum;
      cb->refcount = rcount;

      // insert at the front of UsedList
      cb->prev = UsedList;
      cb->next = UsedList->next;
      UsedList->next->prev = cb;
      UsedList->next = cb;

      // insert into the hash table
      cb->up = &(HT[hv]);
      cb->down = HT[hv].down;
      HT[hv].down->up = cb;
      HT[hv].down = cb;

      return 2;
    }

  return 0;
}

//////////////////////// implementation of Client /////////////////////////////

Client::Client()
{
  int i;
  struct ClientBlock *cb;

  UsedList = new ClientBlock;
  UsedList->next = UsedList;
  UsedList->prev = UsedList;

  RefList = new ClientBlock;
  RefList->next = RefList;
  RefList->prev = RefList;

  FreeList = NULL;

  for (i = 0 ; i < DEFAULTCACHESIZE ; i ++)
    {
      cb = new ClientBlock;

      cb->c_num = 0;
      cb->d_num = 0; 
      cb->i_num = 0;
      cb->b_num = 0;
      cb->refcount = 0;

      cb->next = FreeList;
      FreeList = cb;
    }

  FreeRefList = NULL;

  for (i = 0 ; i < client_refratio * DEFAULTCACHESIZE ; i ++)
    {
      cb = new ClientBlock;

      cb->flag = FLAG_REF;
      cb->d_num = 0; 
      cb->i_num = 0;
      cb->b_num = 0;
      cb->refcount = 0;

      cb->next = FreeRefList;
      FreeRefList = cb;
    }

  for (i = 0 ; i < HASHTABLESIZE ; i ++)
    {
      HT[i].prev = NULL;
      HT[i].next = NULL;
      HT[i].up = &(HT[i]);
      HT[i].down = &(HT[i]);
    } // end of for

  schememode = CLIENT_SCHEME_BASE;
  clientid = 0;

  iocount = 0;
  refcount = 0;

  remotehit = 0;
  missblock = 0;
  totalblock = 0;

  cachesize = DEFAULTCACHESIZE;
  globalused = 0;
  localused = 0;
}

Client::Client(unsigned char mode, unsigned cid, unsigned csize)
{
  int i;
  struct ClientBlock *cb;

  UsedList = new ClientBlock;
  UsedList->next = UsedList;
  UsedList->prev = UsedList;

  RefList = new ClientBlock;
  RefList->next = RefList;
  RefList->prev = RefList;

  FreeList = NULL;

  for (i = 0 ; i < csize ; i ++)
    {
      cb = new ClientBlock;

      cb->c_num = 0;
      cb->d_num = 0; 
      cb->i_num = 0;
      cb->b_num = 0;
      cb->refcount = 0;

      cb->next = FreeList;
      FreeList = cb;
    }

  FreeRefList = NULL;

  for (i = 0 ; i < client_refratio * csize ; i ++)
    {
      cb = new ClientBlock;

      cb->flag = FLAG_REF;
      cb->d_num = 0; 
      cb->i_num = 0;
      cb->b_num = 0;
      cb->refcount = 0;

      cb->next = FreeRefList;
      FreeRefList = cb;
    }

  for (i = 0 ; i < HASHTABLESIZE ; i ++)
    {
      HT[i].prev = NULL;
      HT[i].next = NULL;
      HT[i].up = &(HT[i]);
      HT[i].down = &(HT[i]);
    } // end of for

  schememode = mode;
  clientid = cid;

  iocount = 0;
  refcount = 0;

  remotehit = 0;
  missblock = 0;
  totalblock = 0;

  cachesize = csize;
  globalused = 0;
  localused = 0;  
}

Client::~Client()
{
  struct ClientBlock *cb;

  // free the used list
  cb = UsedList->next;

  while (cb != UsedList)
    {
      UsedList->next = cb->next;
      cb->next->prev = UsedList;

      delete cb;

      cb = UsedList->next;
    } // end of while

  delete UsedList;

  // free the reference list
  cb = RefList->next;

  while (cb != RefList)
    {
      RefList->next = cb->next;
      cb->next->prev = RefList;

      delete cb;

      cb = RefList->next;
    } // end of while

  delete RefList;

  // free the FreeList
  while (FreeList)
    {
      cb = FreeList->next;

      delete FreeList;
      FreeList = cb;
    } // end of while

  // free the FreeRefList
  while (FreeRefList)
    {
      cb = FreeRefList->next;

      delete FreeRefList;
      FreeRefList = cb;
    } // end of while
}

int Client::LookUp(unsigned cid, unsigned idev, unsigned inode, unsigned bnum)
{
  struct ClientBlock * cb;
  unsigned hv;

  cb = UsedList->next;

  // search hash table
  hv = HASHVALUE(inode, bnum);
  cb = HT[hv].down;

  while (cb != &(HT[hv]))
    {
      if (cb->flag == FLAG_USED && cb->c_num == cid && cb->d_num == idev 
	  && cb->i_num == inode && cb->b_num == bnum)
	// cache hit
	{
	  // increase the reference count
	  cb->refcount ++;

	  // move cb to the front
	  cb->prev->next = cb->next;
	  cb->next->prev = cb->prev;

	  cb->next = UsedList->next;
	  cb->prev = UsedList;

	  UsedList->next->prev = cb;
	  UsedList->next = cb;

	  return 1;
	}

      cb = cb->down;
    } // end of while

  return 0;
}

// USED ONLY IN 1 CHANCE SCHEME
int Client::RLookUp(unsigned cid, unsigned idev, unsigned inode, unsigned bnum)
  // check only the inode and bnum, and return device id
{
  struct ClientBlock * cb;
  unsigned hv;

  if ((schememode != CLIENT_SCHEME_CHANCE1 &&
      schememode != CLIENT_SCHEME_CHANCER) || cid != clientid)
    return -1;

  hv = HASHVALUE(inode, bnum);
  cb = HT[hv].down;

  while (cb != (&HT[hv]))
    {
      // cb->c_num is the destination client
      if (cb->flag == FLAG_REF && cb->d_num == idev && cb->i_num == inode && cb->b_num == bnum)
	// cache hit
	{
	  // move cb to the front
	  cb->prev->next = cb->next;
	  cb->next->prev = cb->prev;

	  cb->next = RefList->next;
	  cb->prev = RefList;

	  RefList->next->prev = cb;
	  RefList->next = cb;

	  return cb->c_num;
	}

      cb = cb->down;
    } // end of while

  return -1;
}

// put data to cache
// BASE: put locally, if full, replace one block.
// GLOBAL: if full, forward one block to the global server
// 1 CHANCE: if full, forward one to one peer.
//
// return 1 means there is a free block, return 2 means one block is replaced, 
// return 0 means rejected
int Client::TransferData(unsigned cid, unsigned idev, unsigned inode, unsigned bnum, unsigned rcount,
			 TimeBD &elapse, unsigned &netcount)
{
  struct ClientBlock * cb;
  TimeBD time;
  unsigned ncount;
  int recid;
  unsigned hv;

  elapse.disktime = 0;
  elapse.nettime = 0;
  elapse.memtime = 0;

  netcount = 0;

  hv = HASHVALUE(inode, bnum);

  if (FreeList) // not empty
    {
      cb = FreeList;
      FreeList = cb->next;

      cb->flag = FLAG_USED;
      cb->c_num = cid;
      cb->d_num = idev;
      cb->i_num = inode;
      cb->b_num = bnum;
      cb->refcount = rcount;
      
      // insert at the front of UsedList
      cb->prev = UsedList;
      cb->next = UsedList->next;
      UsedList->next->prev = cb;
      UsedList->next = cb;

      // insert into the hashtable
      cb->down = HT[hv].down;
      cb->up = &(HT[hv]);
      HT[hv].down->up = cb;
      HT[hv].down = cb;

      elapse.memtime = MEMACCESSTIME;
      localused ++;

      return 1;
    }
  else // replace the block at the end of UsedList
    {
      // the least recent used one
      cb = UsedList->prev;

      switch (schememode)
	{
	case CLIENT_SCHEME_BASE:
	  {
	    struct ClientBlock * upblock;
	    struct ClientBlock * downblock;

	    // remove it
	    UsedList->prev = cb->prev;
	    cb->prev->next = UsedList;

	    // remove it from hash table
	    upblock = cb->up;
	    downblock = cb->down;
	    upblock->down = downblock;
	    downblock->up = upblock;

	    cb->flag = FLAG_USED;
	    cb->c_num = cid;
	    cb->d_num = idev;
	    cb->i_num = inode;
	    cb->b_num = bnum;
	    cb->refcount = rcount;
	    
	    // insert at the front of UsedList
	    cb->prev = UsedList;
	    cb->next = UsedList->next;
	    UsedList->next->prev = cb;
	    UsedList->next = cb;
	    
	    // insert into the hash table
	    cb->up = &(HT[hv]);
	    cb->down = HT[hv].down;
	    HT[hv].down->up = cb;
	    HT[hv].down = cb;

	    elapse.memtime = MEMACCESSTIME;

	    return 2;
	  }

	case CLIENT_SCHEME_GLOBAL:
	  {
	    struct ClientBlock * upblock;
	    struct ClientBlock * downblock;

	    // remove it
	    UsedList->prev = cb->prev;
	    cb->prev->next = UsedList;
	    
	    // remove it from hash table
	    upblock = cb->up;
	    downblock = cb->down;
	    upblock->down = downblock;
	    downblock->up = upblock;

	    elapse.memtime = MEMACCESSTIME;

	    // put the victim onto storage server
	    clientserver->TransferData(cb->c_num, cb->d_num, cb->i_num, cb->b_num, 
				       cb->refcount, time, ncount);

	    // put into storage + read victim block + network
	    //	    elapse += time + MEMACCESSTIME + NETROUNDTRIP;
	    elapse.memtime += time.memtime + MEMACCESSTIME;
	    elapse.nettime += time.nettime + NETLATENCY + NETTRANSFERTIME;
	    elapse.disktime += time.disktime;

	    netcount += ncount + 1;

	    cb->flag = FLAG_USED;
	    cb->c_num = cid;
	    cb->d_num = idev;
	    cb->i_num = inode;
	    cb->b_num = bnum;
	    cb->refcount = rcount;

	    // insert at the front of UsedList
	    cb->prev = UsedList;
	    cb->next = UsedList->next;
	    UsedList->next->prev = cb;
	    UsedList->next = cb;

	    // insert into the hash table
	    cb->up = &(HT[hv]);
	    cb->down = HT[hv].down;
	    HT[hv].down->up = cb;
	    HT[hv].down = cb;

	    return 2;
	  }

	case CLIENT_SCHEME_CHANCE1:
	  {
	    struct ClientBlock * upblock;
	    struct ClientBlock * downblock;

	    elapse.memtime = MEMACCESSTIME;

	    // remove it
	    UsedList->prev = cb->prev;
	    cb->prev->next = UsedList;

	    // remove it from hash table
	    upblock = cb->up;
	    downblock = cb->down;
	    upblock->down = downblock;
	    downblock->up = upblock;

	    if (cb->c_num == clientid)  // forward when this block belong to itself
	      {
		// forward the block to another server
		recid = manager->IdleClient(clientid);
		elapse.nettime += NETROUNDTRIP; // lookup with manager
	  
		netcount += 2;

		if (recid >= 0)
		  {
		    struct ClientBlock * rb;

		    if (nclient[recid]->TransferData(cb->c_num, cb->d_num, cb->i_num, cb->b_num, 
						     cb->refcount, time, ncount))
		      { // not rejected
			//elapse += time + MEMACCESSTIME + NETROUNDTRIP;
			elapse.disktime += time.disktime;
			elapse.memtime += time.memtime + MEMACCESSTIME;
			elapse.nettime += time.nettime + NETLATENCY + NETTRANSFERTIME;

			netcount += ncount + 1;

			if (FreeRefList) // FREE REF LIST IS NOT EMPTY
			  {
			    rb = FreeRefList;
			    FreeRefList = rb->next;
			  }
			else // reflist is full
			  {
			    // the least recent used one
			    rb = RefList->prev;

			    // remove it from reflist
			    RefList->prev = rb->prev;
			    rb->prev->next = RefList;

			    // remove it from hash table
			    upblock = rb->up;
			    downblock = rb->down;
			    upblock->down = downblock;
			    downblock->up = upblock;
			  }
			
			rb->flag = FLAG_REF;
			rb->c_num = recid;   // remote client id
			rb->d_num = cb->d_num;  
			rb->i_num = cb->i_num; // the local inode
			rb->b_num = cb->b_num;
			rb->refcount = cb->refcount;

			// insert at the front of RefList
			rb->next = RefList->next;
			rb->prev = RefList;
			RefList->next->prev = rb;
			RefList->next = rb;

			// insert into the hashtable
			unsigned nhv;

			nhv = HASHVALUE(rb->i_num, rb->b_num);
			rb->down = HT[nhv].down;
			rb->up = &(HT[nhv]);
			HT[nhv].down->up = rb;
			HT[nhv].down = rb;
		      }
		  } // end of if recid > 0
	      } // end of if dnum == serverid

	    cb->flag = FLAG_USED;
	    cb->c_num = cid;
	    cb->d_num = idev;
	    cb->i_num = inode;
	    cb->b_num = bnum;
	    cb->refcount = rcount;
     
	    // insert at the front of UsedList
	    cb->prev = UsedList;
	    cb->next = UsedList->next;
	    UsedList->next->prev = cb;
	    UsedList->next = cb;
	    
	    // insert into the hash table
	    cb->up = &(HT[hv]);
	    cb->down = HT[hv].down;
	    HT[hv].down->up = cb;
	    HT[hv].down = cb;

	    return 2;
	  }

	default:
	  printf("Unknow mode!!!\n");
	  return 0;
	} // end of switch
    }

  return 0;
}

/*
int Client::Unlink(unsigned cid, unsigned idev, unsigned inode, unsigned &elapse, 
		   unsigned &netcount, unsigned char statflag)
{

  // suppose one read and one write for delete
  nserver[idev]->Unlink(idev, inode, elapse, statflag);

  netcount = 1;

  if (statflag)
    {
      iocount ++;
      refcount ++;

      //      missblock += 2;
      //totalblock += 2;
    }

  // clear the cached occcupied by the file
  struct ClientBlock * cb, * next;

  cb = UsedList->next;

  while (cb != UsedList)
    {
      if (cb->d_num == idev && cb->i_num == inode)
	{
	  // free block to free list
	  next = cb->next;

	  cb->prev->next = cb->next;
	  cb->next->prev = cb->prev;

	  cb->c_num = 0;
	  cb->d_num = 0;
	  cb->i_num = 0;
	  cb->b_num = 0;
	  cb->refcount = 0;
	  
	  cb->next = FreeList;
	  cb->prev = NULL;
	  FreeList = cb;

	  cb = next;
	  localused --;
	}
      else
	cb = cb->next;

    } // end of while

  if (cid == clientid)
    {
      // clean the reference list
      cb = RefList->next;

      while (cb != RefList)
	{
	  if (cb->i_num == inode)
	    {
	      // free block to free list
	      next = cb->next;
	      
	      cb->prev->next = cb->next;
	      cb->next->prev = cb->prev;
	      
	      delete cb;

	      cb = next;
	    }
	  else
	    cb = cb->next;

	} // end of while
    }

  return 1;
}

// not used right now
int Client::Create(unsigned cid, unsigned idev, unsigned &elapse, 
		   unsigned &netcount, unsigned char statflag)
{
  nserver[idev]->Create(idev, elapse, statflag);
  // suppose two read and two write for create
  //  elapse = DISKSEEKTIME + 4 * DISKTRANSFERTIME;

  netcount = 1;

  if (statflag)
    {
      iocount ++;
      refcount ++;

      //      missblock += 4;
      //      totalblock += 4;
    }

  return 1;
}
*/

// read operation on the server
// return 1 on success, else return 0
int Client::ReadData(unsigned cid, unsigned idev, unsigned inode, unsigned b_begin, unsigned b_count, 
		     TimeBD &elapse, unsigned &netcount, unsigned char statflag)
{
  //unsigned time;
  TimeBD remotetime;
  unsigned ncount;
  int i;
  int remoteid;
  unsigned char remoteflag;

  elapse.disktime = 0;
  elapse.nettime = 0;
  elapse.memtime = 0;

  remoteflag = 0;

  //  elapse = 0;
  netcount = 0;

  for (i = 0 ; i < b_count ; i ++)
    {
      if (LookUp(cid, idev, inode, i + b_begin)) // cache hit
	{
	  elapse.memtime += MEMACCESSTIME;
	}
      else // local cache miss
	{
	  switch (schememode)
	    {
	    case CLIENT_SCHEME_BASE:
	      {
		// get it from server
		manager->GetCap(cid, idev, inode, i + b_begin, 1);
	   
		nserver[idev]->ReadData(idev, inode, i + b_begin, 1, remotetime, ncount, statflag);
		netcount += ncount + 2; // 1 for request and 1 for answer
		//time += remotetime + NETROUNDTRIP + NETTRANSFERTIME;
		elapse.memtime += remotetime.memtime;
		elapse.disktime += remotetime.disktime;
		elapse.nettime += remotetime.nettime + NETROUNDTRIP * network_ratio 
		  + NETTRANSFERTIME * network_ratio;
		
		// put it locally
		TransferData(cid, idev, inode, i + b_begin, 1, remotetime, ncount);
		netcount += ncount;
		elapse.memtime += remotetime.memtime;
		elapse.disktime += remotetime.disktime;
		elapse.nettime += remotetime.nettime;

		remoteflag = 1;
	   
		if (statflag)
		  missblock ++;

		break;
	      }

	    case CLIENT_SCHEME_GLOBAL:
	      {
		if (clientserver->LookUp(cid, idev, inode, i + b_begin)) // remote hit
		  {
		    if (statflag)
		      remotehit ++;

		    netcount += 2;
		    //time += MEMACCESSTIME + NETROUNDTRIP + NETTRANSFERTIME;
		    elapse.memtime += MEMACCESSTIME;
		    elapse.nettime += NETROUNDTRIP + NETTRANSFERTIME;
		  }
		else // remote miss
		  {
		    elapse.nettime += NETROUNDTRIP; // for lookup 
		    
		    // get it from server
		    manager->GetCap(cid, idev, inode, i + b_begin, 1);
	   
		    nserver[idev]->ReadData(idev, inode, i + b_begin, 1, remotetime, ncount, statflag);
		    netcount += ncount + 2;
		    //time += remotetime + NETROUNDTRIP + NETTRANSFERTIME;
		    elapse.memtime += remotetime.memtime;
		    elapse.disktime += remotetime.disktime;
		    elapse.nettime += remotetime.nettime + NETROUNDTRIP * network_ratio 
		      + NETTRANSFERTIME * network_ratio;
		
		    // put it locally
		    TransferData(cid, idev, inode, i + b_begin, 1, remotetime, ncount);
		    netcount += ncount;
		    //time += remotetime;
		    elapse.memtime += remotetime.memtime;
		    elapse.disktime += remotetime.disktime;
		    elapse.nettime += remotetime.nettime;

		    remoteflag = 1;
	   
		    if (statflag)
		      missblock ++;
		  }

		break;
	      }

	    case CLIENT_SCHEME_CHANCE1:
	      {
		// check with the reference list
		if (cid == clientid && (remoteid = RLookUp(cid, idev, inode, i + b_begin)) >= 0 )
		  {
		    // it may has been swap out
		    if (nclient[remoteid]->LookUp(cid, idev, inode, i + b_begin))
		      // remote hit
		      {
			if (statflag)
			  remotehit ++;

			netcount += 2;

			//time += MEMACCESSTIME + NETROUNDTRIP + NETTRANSFERTIME;
			elapse.memtime += MEMACCESSTIME;
			elapse.nettime += NETROUNDTRIP + NETTRANSFERTIME;
		      }
		    else // remote miss
		      {
			elapse.nettime += NETROUNDTRIP;  // lookup time
			
			netcount += 2;

			// get it from server
			manager->GetCap(cid, idev, inode, i + b_begin, 1);
			
			nserver[idev]->ReadData(idev, inode, i + b_begin, 1, remotetime, ncount, 
						statflag);
			netcount += ncount + 2;
			//time += remotetime + NETROUNDTRIP + NETTRANSFERTIME;
			elapse.memtime += remotetime.memtime;
			elapse.nettime += remotetime.nettime + NETROUNDTRIP * network_ratio 
			  + NETTRANSFERTIME * network_ratio;
			elapse.disktime += remotetime.disktime;
			
			// put it locally
			TransferData(cid, idev, inode, i + b_begin, 1, remotetime, ncount);
			netcount += ncount;
			elapse.memtime += remotetime.memtime;
			elapse.nettime += remotetime.nettime;
			elapse.disktime += remotetime.disktime;
			//			time += remotetime;

			remoteflag = 1;
	   
			if (statflag)
			  missblock ++;
		      }
		  }
		else // remote miss
		  {
		    // get it from server
		    manager->GetCap(cid, idev, inode, i + b_begin, 1);
	   
		    nserver[idev]->ReadData(idev, inode, i + b_begin, 1, remotetime, ncount, statflag);
		    netcount += ncount + 2;
		    //time += remotetime + NETROUNDTRIP + NETTRANSFERTIME;
		    elapse.memtime += remotetime.memtime;
		    elapse.nettime += remotetime.nettime + NETROUNDTRIP * network_ratio 
		      + NETTRANSFERTIME * network_ratio;
		    elapse.disktime += remotetime.disktime;

		    // put it locally
		    TransferData(cid, idev, inode, i + b_begin, 1, remotetime, ncount);
		    netcount += ncount;
		    //		    time += remotetime;
		    elapse.memtime += remotetime.memtime;
		    elapse.nettime += remotetime.nettime;
		    elapse.disktime += remotetime.disktime;
		  
		    remoteflag = 1;
	   
		    if (statflag)
		      missblock ++;
		  }
		break;
	      } 

	    } // end of switch
	} // end of local miss
    } // end of for

  if (statflag)
    {
      totalblock += b_count;
      refcount ++;
    }

  if (remoteflag)
    {      
      if (statflag)
	iocount ++;
    }

  //elapse = time;

  return 1;
}

int Client::WriteData(unsigned cid, unsigned idev, unsigned inode, unsigned b_begin, unsigned b_count,
		      TimeBD &elapse, unsigned &netcount, unsigned char statflag)
{
  // so far, read and write are the same
  return ReadData(cid, idev, inode, b_begin, b_count, elapse, netcount, statflag);
}
