//  NASD.cpp
//   Implementation of class NasdServer
//

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

extern Manager * manager;
extern NasdServer * nserver[];
extern StorageServer * storageserver;
extern unsigned server_num;
extern unsigned server_refratio;

////////////////////////////// implementation of storage server ///////////////////////

StorageServer::StorageServer()
{
  int i;
  struct CacheBlock *cb;

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

  FreeList = NULL;

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

      cb->flag = FLAG_USED;
      cb->d_num = 0; 
      cb->i_num = 0;
      cb->b_num = 0;

      cb->next = FreeList;
      FreeList = cb;
    } // end of for

  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;
  //refcount = 0;
  //remotehit = 0;
  missblock = 0;
  totalblock = 0;

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

StorageServer::StorageServer(unsigned csize)
{
  int i;
  struct CacheBlock *cb;

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

  FreeList = NULL;

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

      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;
  //refcount = 0;
  //remotehit = 0;
  missblock = 0;
  totalblock = 0;

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

StorageServer::~StorageServer()
{
  struct CacheBlock *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 StorageServer::LookUp(unsigned idev, unsigned inode, unsigned bnum)
{
  struct CacheBlock * cb;
  unsigned hv;

  totalblock ++;

  // search in the hashtable
  hv = HASHVALUE(inode, bnum);

  cb = HT[hv].down;

  while (cb != &(HT[hv]))
    {
      if (cb->flag == FLAG_USED && 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->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 StorageServer::TransferData(unsigned idev, unsigned inode, unsigned bnum, unsigned rcount,
				//unsigned &elapse, unsigned &netcount)
				TimeBD &elapse, unsigned &netcount)
{
  struct CacheBlock * cb;
  unsigned hv;

  //elapse = MEMACCESSTIME;
  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->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 CacheBlock * upblock;
      struct CacheBlock * downblock;

      cb = UsedList->prev;
      
      // remove it from LRU list
      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->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 NasdServer /////////////////////////

NasdServer::NasdServer()
{
  int i;
  struct CacheBlock *cb;

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

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

  FreeList = NULL;

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

      cb->flag = FLAG_USED;
      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 < server_refratio * DEFAULTCACHESIZE ; i ++)
    {
      cb = new CacheBlock;

      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

  for (i = 0 ; i < MAXSERVERNUM ; i ++)
    {
      busybit[i] = 0;
    } // end of for

  schememode = SERVER_SCHEME_BASE;
  serverid = 0;

  iocount = 0;
  refcount = 0;

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

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

  srandom(0);
}

NasdServer::NasdServer(unsigned char mode, unsigned sid, unsigned csize)
{
  int i;
  struct CacheBlock *cb;

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

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

  FreeList = NULL;

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

      cb->flag = FLAG_USED;
      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 < server_refratio * csize ; i ++)
    {
      cb = new CacheBlock;

      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

  for (i = 0 ; i < MAXSERVERNUM ; i ++)
    {
      busybit[i] = 0;
    } // end of for

  schememode = mode;

  serverid = sid;

  iocount = 0;
  refcount = 0;

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

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

  srandom(sid * 1000);
}

NasdServer::~NasdServer()
{
  struct CacheBlock *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 NasdServer::HintOnDest()
{
  unsigned lowest;
  unsigned dest;
  int i;

  if (server_num <= 1)
    return -1;

  // serverid could be 0
  if (serverid == 0)
    {
      lowest = busybit[1];
      dest = 1;
    }
  else
    {
      lowest = busybit[0];
      dest = 0;
    }

  for (i = 1 ; i < server_num ; i ++)
    {
      if (i != serverid && lowest > busybit[i])
	{
	  lowest = busybit[i];
	  dest = i;
	}
    } // end of for 

  return dest;
}

int NasdServer::LookUp(unsigned idev, unsigned inode, unsigned bnum)
{
  struct CacheBlock * cb;
  unsigned hv;

  //manager->reference[serverid] += 1;

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

  //  while (cb != UsedList)
  while (cb != &(HT[hv]))
    {
      if (cb->flag == FLAG_USED && cb->d_num == idev 
	  && cb->i_num == inode && cb->b_num == bnum)
	// cache hit
	{
	  if (schememode == SERVER_SCHEME_CHANCER && idev != serverid) 
	    // return to other server
	    {
	      // remove it
	      cb->prev->next = cb->next;
	      cb->next->prev = cb->prev;

	      cb->up->down = cb->down;
	      cb->down->up = cb->up;

	      // put it in free ref list
	      cb->next = FreeList;
	      FreeList = cb;
	    }
	  else
	    {
	      // 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 NasdServer::RLookUp(unsigned idev, unsigned inode, unsigned bnum)
  // check only the inode and bnum, and return device id
{
  struct CacheBlock * cb;
  unsigned hv;

  // only forward the block belonging to the server
  if ((schememode != SERVER_SCHEME_CHANCE1 &&
      schememode != SERVER_SCHEME_CHANCER &&
      schememode != SERVER_SCHEME_CHANCEH && 
      schememode != SERVER_SCHEME_CHANCEX && 
      schememode != SERVER_SCHEME_CHANCEZ) ||
      idev != serverid)
    return -1;

  hv = HASHVALUE(inode, bnum);

  cb = HT[hv].down;

  while (cb != (&HT[hv]))
    {
      // cb->d_num is the destination
      if (cb->flag == FLAG_REF && cb->i_num == inode && cb->b_num == bnum)
	// cache hit
	{
	  if (schememode == SERVER_SCHEME_CHANCER)
	    // 1 chance and return
	    {
	      // remove it
	      cb->prev->next = cb->next;
	      cb->next->prev = cb->prev;

	      cb->up->down = cb->down;
	      cb->down->up = cb->up;

	      // put it in free ref list
	      cb->next = FreeRefList;
	      FreeRefList = cb;
	    }
	  else // other 1 Chance variations
	    {
	      // 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->d_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 or forwarded
// return 0 means rejected
int NasdServer::TransferData(unsigned idev, unsigned inode, unsigned bnum, unsigned rcount,
			     //unsigned &elapse, unsigned &netcount)
			     TimeBD &elapse, unsigned &netcount)
{
  struct CacheBlock * cb;
  TimeBD time;
  unsigned ncount;
  int recid;
  unsigned hv;

  if (idev != serverid)
    busybit[idev] ++;

  //  elapse = 0;
  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->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 = MEMACCESSTIME;
      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 SERVER_SCHEME_BASE:
	  {
	    struct CacheBlock * upblock;
	    struct CacheBlock * 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->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 = MEMACCESSTIME;
	    elapse.memtime = MEMACCESSTIME;

	    return 2;
	  }

	case SERVER_SCHEME_GLOBAL:
	  {
	    struct CacheBlock * upblock;
	    struct CacheBlock * 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
	    storageserver->TransferData(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->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 SERVER_SCHEME_CHANCE1:
	case SERVER_SCHEME_CHANCER:
	case SERVER_SCHEME_CHANCEH:
	case SERVER_SCHEME_CHANCEX:
	case SERVER_SCHEME_CHANCEZ:
	  {
	    // always accept the coming block
	    struct CacheBlock * upblock;
	    struct CacheBlock * downblock;

	    // put the block
	    elapse.memtime = MEMACCESSTIME;

	    // remove the least recent used one
	    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->d_num == serverid)  // forward when this block belong to itself
	      {
		// forward the block to another server
		switch (schememode)
		  {
		  case SERVER_SCHEME_CHANCE1:
		  case SERVER_SCHEME_CHANCER:
		    recid = manager->WhoIsIdle(serverid);
		    elapse.nettime += NETROUNDTRIP; // lookup with manager
		    netcount += 2; // request and answer
		    break;
		  case SERVER_SCHEME_CHANCEZ:
		    recid = manager->WhoIsIdle(serverid);
		    break;
		  case SERVER_SCHEME_CHANCEX:
		    if (server_num <= 1)
		      recid = -1;
		    else
		      {
			recid = random() % server_num;
			if (recid == serverid)
			  recid = (recid + 1) % server_num;
		      }
		    break;
		  case SERVER_SCHEME_CHANCEH:
		    recid = HintOnDest();
		    break;
		  } // end of switch

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

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

			netcount += ncount + 1;

			if (FreeRefList)
			  {
			    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;
			  }

			busybit[recid] ++;

			rb->flag = FLAG_REF;
			rb->d_num = recid;     // !!!! the remote device id
			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
	    else
	      busybit[cb->d_num] --;

	    cb->flag = FLAG_USED;
	    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

    } // end of overflow

  return 0; // never be here
}

/*
// not used right now
int NasdServer::Unlink(unsigned idev, unsigned inode, unsigned &elapse, 
		       unsigned char statflag)
{
  // suppose one read and one write for delete
  elapse = DISKSEEKTIME + 2 * DISKTRANSFERTIME;

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

      missblock += 2;
      totalblock += 2;
    }

  // clear the cached occcupied by the file
  struct CacheBlock * 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->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 (idev == serverid)
    {
      // 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 NasdServer::Create(unsigned idev, unsigned &elapse, unsigned char statflag)
{
  // suppose two read and two write for create
  elapse = DISKSEEKTIME + 4 * DISKTRANSFERTIME;

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

      missblock += 4;
      totalblock += 4;
    }

  return 1;
}
*/

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

  //time = 0 ;
  seekflag = 0;

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

  netcount = 0;

  for (i = 0 ; i < b_count ; i ++)
    {
      if (LookUp(idev, inode, i + b_begin)) // local cache hit
	{
	  elapse.memtime += MEMACCESSTIME;
	}
      else // local cache miss
	{
	  switch (schememode)
	    {
	    case SERVER_SCHEME_BASE:
	      {
		// put it locally
		TransferData(idev, inode, i + b_begin, 1, transtime, ncount);
		//time += DISKTRANSFERTIME + transtime;
		elapse.disktime += transtime.disktime + DISKTRANSFERTIME;
		elapse.nettime += transtime.nettime;
		elapse.memtime += transtime.memtime;

		netcount += ncount;
		seekflag = 1; 
		
		if (statflag) 
		  missblock ++;

		break;
	      }

	    case SERVER_SCHEME_GLOBAL:
	      {
		// check with the global storage server
		if (storageserver->LookUp(idev, inode, i + b_begin)) 
		  // cache hit on the storage server
		  {
		    if (statflag)
		      remotehit ++;

		    netcount += 1; // 1 to storageserver, 1 to client is not calculated now

		    //time += MEMACCESSTIME + NETROUNDTRIP + NETTRANSFERTIME;
		    // the time transfer to client is not counted in
		    elapse.memtime += MEMACCESSTIME;
		    elapse.nettime += NETLATENCY;
		  }
		else
		  {
		    elapse.nettime += NETROUNDTRIP; // for lookup 
		    netcount += 2;

		    // put it locally
		    TransferData(idev, inode, i + b_begin, 1, transtime, ncount);
		    //time += DISKTRANSFERTIME + transtime;
		    elapse.memtime += transtime.memtime;
		    elapse.nettime += transtime.nettime;
		    elapse.disktime += transtime.disktime + DISKTRANSFERTIME;

		    netcount += ncount;
		    seekflag = 1; 
		    
		    if (statflag) 
		      missblock ++;
		  }

		break;
	      }

	    case SERVER_SCHEME_CHANCE1:
	    case SERVER_SCHEME_CHANCER:  // 1 Chance and return
	    case SERVER_SCHEME_CHANCEH:
	    case SERVER_SCHEME_CHANCEX:
	    case SERVER_SCHEME_CHANCEZ:
	      {
		// check with the reference list
		if (idev == serverid && (remoteid = RLookUp(idev, inode, i + b_begin)) >= 0 )
		  // cache hit on the storage server
		  {
		    // it may has been swap out
		    if (nserver[remoteid]->LookUp(idev, inode, i + b_begin))
		      // remote hit
		      {
			if (statflag)
			  remotehit ++;

			netcount += 1;

			//time += MEMACCESSTIME + NETROUNDTRIP + NETTRANSFERTIME;
			elapse.memtime += MEMACCESSTIME;
			elapse.nettime += NETLATENCY;

			if (schememode == SERVER_SCHEME_CHANCER)
			  {
			    // put it locally
			    // assume one transmission to both server and client
			    TransferData(idev, inode, i + b_begin, 1, transtime, ncount);
			    netcount += ncount;
			  }
		      }
		    else // has been swapped out
		      {
			elapse.nettime += NETROUNDTRIP;  // lookup time
			
			netcount += 2;

			// put it locally
			if (TransferData(idev, inode, i + b_begin, 1, transtime, ncount))
			  {
			    //time += DISKTRANSFERTIME + transtime;
			    elapse.disktime += transtime.disktime + DISKTRANSFERTIME;
			    elapse.memtime += transtime.memtime;
			    elapse.nettime += transtime.nettime;

			    netcount += ncount;

			    seekflag = 1; 
			    
			    if (statflag) 
			      missblock ++;
			  }
		      }
		  }
		else // remote miss
		  {
		    // put it locally
		    if (TransferData(idev, inode, i + b_begin, 1, transtime, ncount))
		      {
			//time += DISKTRANSFERTIME + transtime;
			elapse.disktime += transtime.disktime + DISKTRANSFERTIME;
			elapse.memtime += transtime.memtime;
			elapse.nettime += transtime.nettime;

			netcount += ncount;
			
			seekflag = 1; 
			
			if (statflag) 
			  missblock ++;
		      }
		  }

		break;
	      }

	    default:
	      printf("Unknown mode!!!\n");
	      return 0;
	    } // end of switch
	} // end of local cache miss

    } // end of for

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

  if (seekflag)
    {
      elapse.disktime += DISKSEEKTIME;
      
      if (statflag)
	iocount ++;
    }

  //elapse = time;

  return 1;
}

int NasdServer::WriteData(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(idev, inode, b_begin, b_count, elapse, netcount, statflag);
}
