/*******************************************************************
 *                  Light Weight Chord Library 1.0                 *
 *  CopyRight(C) Yoshihide Matsumoto IDEON WorkingGroup 2003,2004  *
 *      matsumoto333@yahoo.co.jp (http://www.matchan.mydns.jp)     *
 *                                                                 *
 * Implementation of chord (Distributed Hash Table)                *
 * This program is distributed under GPL                           *
 * Light Weight Chord Library comes with ABSOLUTELY NO WARRANTY.   *
 * This is free software, and you are welcome to redistribute it   *
 * under certain conditions; read `COPYING' for details.           *
 *******************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <stdarg.h>

#include "main.h"
#include "mchord.h"
#include "protocol.h"
#include "misc.h"
#include "id_list.h"
#include "file.h"
#include "define.h"
#include "sock.h"

#define DEFAULT_PORT      5555
#define DEFAULT_TTL       64
#define ALLOW_FIRST_NODE  1 //1,-1=True 0=False
#define ID_DELETE         0
#define MCID_BIT          32
#define SEND_BUFF         1024
#define RETRANS_MAX       5
#define TIMER_PERIOD      3000 //msec
#define DEFAULT_CACHE_DIR "cache"

extern MChord   *pMChord;
extern Protocol *pProtocol;


MChord::MChord(int port, char *pcache_dir, int log_flg){
     
  int n;

  mTaskList = NULL;

  if(*pcache_dir == '\0'){
    strcpy(mCacheDir, DEFAULT_CACHE_DIR);
  }else{
    strcpy(mCacheDir, pcache_dir);
  }

  if(port == 0){
    mPort = DEFAULT_PORT;
  }else{
    mPort = port;
  }
  fprintf(stderr, "ListenPort = %d\n", mPort);
  fprintf(stderr, "CacheDir = ./%s\n", mCacheDir);
  
  //initialize random seed
  init_random();

  //make BeginId
  mBeginId = get_random(); 
  fprintf(stderr, "BeginId = %u\n", mBeginId);

  mEndId = mBeginId - 1;
  fprintf(stderr, "EndId = %u\n",  mEndId);

  //set STATE
  mState = READY;
  
  //set MyIPAddr
  mIp = str_to_ip(GetMyIp());
  fprintf(stderr, "IP = %s\n",GetMyIp());

  mNext.Ip   = mIp;
  mNext.Port = mPort;
  mPrev.Ip   = mIp;
  mPrev.Port = mPort;


  //Timer msec
  mTimer = TIMER_PERIOD;

  //RoutingTable
  for(n = 0; n < MCID_BIT; n++){
    mRoutingTable[n].McId = 0;
    mRoutingTable[n].Ip   = mIp;
    mRoutingTable[n].Port = mPort;
  }
  
  //IdList
  mIdList = NULL;
  
  
  //SequenceNo
  mSeqNo = 0;
  
  //mFlag:RecvFile
  mFlag = 0;
  
  //mFP
  mFP = NULL;



  //Log FP
  if(log_flg == 0){
    
    char file_name[50];
    sprintf(file_name, "%d.log", port);
    mLogFP = fopen(file_name, "w");
    fclose(mLogFP);

  }

  mLogFP = NULL;  
  
  //log_flg
  mLogFlg = log_flg;
  fprintf(stderr, "log_flg = %d\n", mLogFlg);
  
  //mFileSeqNo
  mFileSeqNo = 0;
  
  //RetransmitMax
  mRetransmitMax = RETRANS_MAX;
  
  //Read Id list from file to memory
  ReadFile("idlist.txt", (IDLIST **)&mIdList, mCacheDir);

  //unused: need port
  //id_print((IDLIST **)&mIdList);
  
  for(n = 0; n < STATISTICS_MAX; n++){
    mSendCount[n] = 0;
    mRecvCount[n] = 0;
  }

  mTmpSessIdLogin = 0;
  mTmpInsert = 0;
}

//Call by GUI
//ex. Join("192.168.0.1:4649")
void MChord::Join(char *addr){

  char *port;
  ADDRESS Address;  

  port = strchr(addr,':');
  
  if(port == NULL){
    Address.Port = DEFAULT_PORT;
  }else{
    *port = '\0';
    Address.Port = atoi(&port[1]);
  }
  
  Address.Ip = str_to_ip(addr);
  
  if(Address.Ip != mIp || Address.Port != mPort){
    SendJoin(Address);
  }
}


void MChord::SendJoin(ADDRESS Address){
  
  int  sessid;
  TASKLIST *ptask;
  
  mState = CONNECTING;
  
  //sess_id is generated by new node.
  //it is used belong NEW node finish joining old node.
  sessid = get_random();

  //backup sessid to prevent malicious
  mTmpSessId = sessid;

  //set task
  ptask = SetTaskOfData(Address, M_JOIN, sessid, Address, 0, NONE, NONE);
  
  //send task
  SendTask(ptask);
  
}


//call by Protocol
void MChord::RecvJoin(ADDRESS Address, int mcid, int sess_id, int seq_no){
  
  IDLIST   *idlist_local;
  idlist_local = mIdList;
  TASKLIST *ptask;
  ADDRESS AddressTo;
  ADDRESS AddressSrc;
  
  mRecvCount[F_JOIN] ++;

  //NOT IMPLEMENTED
  //if(mState != READY && (mState == NONE || ALLOW_FIRST_NODE )){
  //    SendBusy();
  //    message("Receive Join : State is not READY\n");
  //    return; 
  //}
  

  //Send Ack
  mSendCount[F_ACK] ++;
  pProtocol->SendAck(Address, seq_no);
  
  message("begin=%u, mcid=%u, end=%u\n", mBeginId, mcid, mEndId);
  
  
  if((mBeginId < mcid && mcid <= mEndId) || 
     (mEndId < mBeginId && ( mcid < mEndId || mBeginId < mcid))){
    
    
    message("State = UPDATING\n");
    mState = UPDATING;
    
    
    //back up BeginId of new node.
    //changed by RecvAck_JOIN_ACCEPT
    mTmpMcId      = mcid;
    mTmpNext.Ip   = Address.Ip;
    mTmpNext.Port = Address.Port;
    
    message("sending File %u->%u sess_id=%d\n",mcid, mEndId, sess_id);

    while(idlist_local != NULL){
      
      //drop all packet only if one packet related with sess_id drop
      if((mcid <= idlist_local->id && idlist_local->id <= mEndId )||
	 (mEndId < mcid && 
	  ( idlist_local->id < mEndId || mcid < idlist_local->id))){
	
	message("mcid=%u, id=%u, mEndId=%u, sess_id=%d\n", 
		mcid, idlist_local->id, mEndId, sess_id);
	
	SetTaskOfFile(Address, idlist_local->id, sess_id);
	
      }
      
      idlist_local = idlist_local->next;
    }
    
    //Add queue with same sess_id.
    //Tell my EndId to the new node.
    SetTaskOfData(Address, M_JOIN_ACCEPT, sess_id, mNext, NONE, NONE, NONE);
    
    //Add callback task called after all task is done
    SetTaskOfEvent(Address, M_UPDATE_RT, sess_id);


    message("send task+\n");
    task_print((TASKLIST **)&mTaskList);
    message("send task-\n");

    //execute first task which has same sess_id
    ptask = LookupTaskBySession(sess_id);
    SendTask(ptask);
    //SendTaskBySession(sess_id);
    // is incliment retrans count
    
    
  }else{
    
    
    //SendOutOfRange
    log(NOTICE, "out of mcid range\n");

    //lookup address
    AddressTo = LookupRT(mcid);
    AddressSrc.Ip   = mIp;
    AddressSrc.Port = mPort;

    //Lookup mcid
    ptask = SetTaskOfData(AddressTo, M_LOOKUP, sess_id, AddressSrc, 
			  mcid, NONE, NONE);
    SendTask(ptask);

    //TODO:SendJoinRedirect(Address, seq_no, ip, port);
    
  }
  
}



//call by Protocol
void MChord::RecvJoinRedirect(ADDRESS Address, int sess_id, int seq_no, 
			      int ip, int port){

  if(sess_id == mTmpSessId){
    message("JoinRedirect\n");
    SendJoin(Address);
  }
  
}


//Call by Event
//Connected by Client and Finish join
void MChord::DeleteIdList(int mcid){
  
  IDLIST *pidlist;
  pidlist = mIdList;
  char fname[100];
  
  if(mState == UPDATING){
    mState = RING_UPDATING;
  }
  
  //mcid is first Id of the new node.
  while(pidlist != NULL){
    
    if(( mBeginId < mEndId && mcid <= pidlist->id && pidlist->id < mEndId ) || 
       (mEndId < mBeginId && mBeginId < mcid && mEndId < mcid && mcid < pidlist->id) ||
       (mEndId < mBeginId && mcid < mEndId && pidlist->id < mcid)){ 
      
      //delete unused id
      sprintf(fname, "./%s/%d",mCacheDir, pidlist->id);
      message("rm %s\n",fname);
      if(ID_DELETE){
	file_delete(fname);
      }
      
      //return next list
      pidlist = id_delete_key((IDLIST **)&mIdList, mcid);
      
    }else{
      pidlist = pidlist->next;
    }
    
  }
  

  //recalc routing table, because last Id is change.
  //change STATE to READY
  if(mState == RING_UPDATING){
    mState = READY;
  }else{
    message("State CONNECTING Error\n"); 
  }
  
}


// usually return  0
int MChord::UpdateTaskOfFile(TASKLIST *task){

  char *buff;
  int size;
  
  if(task == NULL){
    log(WARNING, "UpdateTaskOfFile\n");
    return -1;
  }
  
  if(   (task->Size != 0 && task->Data == NULL)
     || (task->Size == 0 && task->Data != NULL) ){
    log(WARNING, "UpdateTaskOfFile : mem error\n");
  }

  //first
  if(task->Size != 0 && task->Data != NULL){
    message("free buffer of FileSend %d\n", task->SeqNo);
    //free(task->Data);
  }

  //last flag
  //task should be delete in RecvAck
  if(task->Flag == 3 || task->Flag == 4){
    log(WARNING,"UpdateTaskOfFile Flag. Call by TimerFunc\n");
    return -1;
  }
  
  buff = (char *)malloc(SEND_BUFF);
  
  size = fread(&buff[16 + 22], 1, SEND_BUFF - 16 - 22, task->Fp);
  message("read size from file = %d\n", size);
  
  if(size == 0){
    log(WARNING, "Read File Size = 0, %d\n", task->McId);
    return 0;
  }
  
  
  if(task->Flag == 0){
    //first time
    task->FileSeqNo = 0;
    
    if(size == (SEND_BUFF - 16 - 22)){
      //start and continue
      task->Flag = 1;
    }else{
      //start and end
      task->Flag = 4;
    }
    
  }else{
    //not first
    //task->Flag == 1,2,3,4
    task->FileSeqNo++;
    
    if(size == (SEND_BUFF - 16 - 22)){
      //continue
      task->Flag = 2;
    }else{
      //end
      task->Flag = 3;
    }      
    
  }


  //headderのsequencenoを書き換える
  //1 filetaskは1レコード
  task->SeqNo = ++ mSeqNo;
  task->RetransmitCount = 0;
  task->Size = size + 16 + 22;
  task->Data = buff;


  message("Send File seq_no[%d],sessid[%d],mcid[%u],flag[%d],size[%d]\n",
	  task->SeqNo, task->SessId, task->McId, task->Flag, task->Size);

  return 0;
}

//packet has same dest is order as FIFO
//add timer/retransmit job list
void MChord::SetTaskOfFile(ADDRESS Address, int mcid, int sess_id){

  char fname[256];
  FILE *fp;
  TASKLIST *task;
  int  size;
  int  fileseqno;
  int  flag;

  mSeqNo ++;
  
  //open file
  //  if(task->Fp != NULL){
    sprintf(fname,"./%s/%d",mCacheDir, mcid);
    
    fp = fopen(fname,"r");
    if(fp == NULL){
      log(WARNING, "Cannot Open Read File[%s]\n", fname);
      return;
    }
    //  }
  
  //add task list
  task = task_insert((TASKLIST **)&mTaskList, Address);
  task->Type   = T_FILE;
  task->Size   = 0;
  task->Func   = M_FILE;
  task->Fp     = fp;
  task->FileSeqNo = 0;
  task->Data   = NULL;
  task->McId   = mcid;
  task->SessId = sess_id;
  task->Flag   = 0;
  task->SeqNo  = mSeqNo;
  
}


void MChord::RecvFile(ADDRESS Address, int seq_no, int size, int sessid, int mcid, int fileseqno, int flag, char *buff){

  char fname[100];


  mRecvCount[F_FILE] ++;
  
  if(mTmpInsert == 2){
    mSendCount[F_ACK] ++;
    pProtocol->SendAck(Address, seq_no);      
    
    log(WARNING,"Receive file on insert\n");
    return;
  }
  

  // I might neet filtering by sequence no.
  switch(flag){
    
  case 1:


    //first packet
    if(mFlag == 0){
      sprintf(fname, "./%s/%d",mCacheDir, mcid);
      mFP = fopen(fname,"w");
      if(mFP == NULL){
	log(WARNING, "Cannot Open Write File\n");
	return;
      }
      
      fwrite(&buff[38], size - 16, 1, mFP);
      mFlag = 1;
      mFileSeqNo = fileseqno;
      mSendCount[F_ACK] ++;
      pProtocol->SendAck(Address, seq_no);      
      
    }else{
      log(WARNING, "state error mFlag = %d\n", mFlag);
      
    }
    break;

    
  case 2:
    //middle packet
    if(mFlag == 1 && ++ mFileSeqNo == fileseqno){
      fwrite(&buff[38], size - 16, 1, mFP);
      mSendCount[F_ACK] ++;
      pProtocol->SendAck(Address, seq_no);
    }else{
      log(WARNING, "fileseqno error\n");
    }
    break;

    
  case 3:
    //last packet
    if(mFlag == 1 && ++mFileSeqNo == fileseqno){
      fwrite(&buff[38], size - 16, 1, mFP);
      fclose(mFP);
      //insert ID LIST

      if(id_search(mIdList, mcid) == NULL){	
	id_insert((IDLIST **)&mIdList, mcid);
      }
      mFlag = 0;
      mSendCount[F_ACK] ++;
      pProtocol->SendAck(Address, seq_no);
    }else{
      log(WARNING, "error\n");
    }
    break;
    

  case 4:
    //1 packet only
    if(mFlag == 0){
      sprintf(fname, "./%s/%d", mCacheDir,mcid);
      mFP = fopen(fname,"w");
      if(mFP == NULL){
	log(WARNING, "Cannot Open Write File flag=4[%s]\n",fname);
	return;
      }
      
      fwrite(&buff[38], size - 16, 1, mFP);
      mFlag = 0;
      fclose(mFP);
      
      message("mcid=%u\n",mcid);
      id_print((IDLIST **)&mIdList);
      if(id_search(mIdList, mcid) == NULL){
	id_insert((IDLIST **)&mIdList, mcid);
      }
      mSendCount[F_ACK] ++;
      pProtocol->SendAck(Address, seq_no);
      
    }else{
      log(WARNING, "state error flag = %d\n", mFlag);  
    }
    break;
    
    
  default:
    log(WARNING, "unknown flag=%d\n", flag);
    break;
  }

}

//Call by Task
//1周回るかも
//切り詰めて再計算
void MChord::UpdateRT(){

  int cnt;

  //ルーティングテーブルを以下の様に変えるだけ
  //32 own
  //31 own
  //.
  //.
  //2  next(new node)
  //1  next(new node)
  for(cnt = 0; cnt < MCID_BIT; cnt++){
    if((mBeginId < mEndId && mBeginId < (mEndId + pow(2, cnt)) && 
	(mEndId + pow(2, cnt)) < mEndId) || 
       (mEndId < mBeginId && 
	(mEndId + pow(2, cnt) <= mEndId || mBeginId <= (mEndId + pow(2,cnt))))
       ){
      //own range
      // mBeginId-----mEndId-1-2--4----8----------16--
      // ^-BroadcastId
      mRoutingTable[cnt].McId = mBeginId;
      mRoutingTable[cnt].Ip   = mIp;
      mRoutingTable[cnt].Port = mPort;

    }else{
      //other range
      //set next ip
      
      //TODO
      mRoutingTable[cnt].McId = mEndId + 1;
      mRoutingTable[cnt].Ip   = mNext.Ip;
      mRoutingTable[cnt].Port = mNext.Port;
      message("other range %u<%u \n", mBeginId, mEndId + pow(2, cnt) + mEndId);
    }
  }
  
  for(int n = 0; n < 32; n++){
    message("%d mcid=%d(RT_IP=%d,Port=%d)(myIP=%d,Port=%d)\n", n, 
	    mRoutingTable[n].McId, mRoutingTable[n].Ip, mRoutingTable[n].Port,
	    mIp, mPort);
  }
  
  for(cnt = 0; cnt <= MCID_BIT; cnt++){
    SendLookupKey((int)pow(2,cnt) + mEndId);
  }

}





// Call by Protocol
void MChord::RecvJoinAccept(ADDRESS Address, int SessId, ADDRESS Next,
			    int EndId, int seq_no){

  mRecvCount[F_JOIN_ACCEPT] ++;

  //Send Ack
  mSendCount[F_ACK] ++;
  pProtocol->SendAck(Address, seq_no);
  
  //check the same node using bufferd sess_id
  if(SessId == mTmpSessId){
    
    //upate my EndId to new one
    mEndId     = EndId;
    mNext.Ip   = Next.Ip;
    mNext.Port = Next.Port;
    mPrev.Ip   = Address.Ip;
    mPrev.Port = Address.Port;
    
  }
  
  UpdateRT();

  if(mState == NONE){
    message("State = NONE\n");
  }
  
  if(mState == CONNECTING){
    mState = READY;
    message("State = READY\n");
  }else{
    message("State RING_UPDATING Error\n");
  }
  
  message("write idlist\n");
  WriteFile("idlist.txt",(IDLIST **)&mIdList, mCacheDir);

}


//Call by GUI
void MChord::SendLookupData(char *data){
  
  int key;

  key = mhash(data,strlen(data));
  mTmpSessIdLogin = 1;//login
  message("Send Lookup Data key = %u\n", key);
  SendLookupKey(key);

}


//Call by GUI
void MChord::SendLookupKey(int key){
  
  ADDRESS Address;
  ADDRESS AddressSrc;
  int sessid;
  TASKLIST *ptask;
  
  Address = LookupRT(key);
  sessid  = get_random();
  mTmpSessId = sessid;
  AddressSrc.Ip = mIp;
  AddressSrc.Port = mPort;
  //mTmpMcId   = key;

  message("Send Lookup Key Id = %u ", key);
  print_ip(Address.Ip, Address.Port);
  
  ptask = SetTaskOfData(Address, M_LOOKUP, sessid, AddressSrc, key, NONE, NONE);
  SendTask(ptask);
  
}


//Call by Protocol
void MChord::RecvLookup(ADDRESS Address, int SessId, int McIdTo, int McIdFrom, 
			int Ttl, int SeqNo, ADDRESS AddressSrc){
  
  mRecvCount[F_LOOKUP] ++;
  
  //drop packet if packet's distination is me.
  //add filter
  
  int send_flg;
  IDLIST *idlist_local;
  TASKLIST *ptask;
  ADDRESS AddressMe;
  AddressMe.Ip = mIp;
  AddressMe.Port = mPort;

  //Send Ack
  mSendCount[F_ACK] ++;
  pProtocol->SendAck(Address, SeqNo);
  
  idlist_local = mIdList;  
  send_flg = 0;  
  
  if((mBeginId < mEndId   && mBeginId < McIdTo  && McIdTo < mEndId) ||
     (mEndId   < mBeginId && (McIdTo < mBeginId || mEndId < McIdTo)) ){
    
    
    //my area. check id list
    while(idlist_local != NULL && send_flg == 0){
      if(idlist_local->id == McIdTo){
	message("I have %u\n", McIdTo);
	if(AddressSrc.Ip == mIp && AddressSrc.Port == mPort){

	  log(WARNING, "TODO: send me file\n");
	}else{

	  //send file
	  message("send file\n");
	  SetTaskOfFile(AddressSrc, idlist_local->id, SessId);
	}
	//return I have
	ptask = SetTaskOfData(AddressSrc, M_LOOKUP_RESPONSE, SessId, 
			      AddressMe, McIdTo, FIND, Ttl);
	
	SendTaskBySession(SessId);
	send_flg = 1;
	
      }
      idlist_local = idlist_local->next;
    }
    
    //return I don't have
    if(send_flg == 0){
      message("I don't have %u\n", McIdTo);
      ptask = SetTaskOfData(AddressSrc, M_LOOKUP_RESPONSE, SessId, 
			    AddressMe, McIdTo, NOT_FIND, Ttl);
      SendTask(ptask);
    }
    
    
  }else{

    //other area
    //transfer next node.
    message("other mcid range\n");

    //decliment TTL
    //if TTL = 1 then drop packet
    Ttl --;
    
    if(0 < Ttl){
      Address = LookupRT(McIdTo);
      
      ptask = SetTaskOfData(Address, M_LOOKUP, SessId, AddressSrc, McIdTo, 
			    NONE, NONE);
      SendTask(ptask);
      
    } 
    
  }

}




//Call by Protocol
void MChord::RecvLookupResponse(ADDRESS Address, int SessId, int McId,
				int SeqNo, int Ans, int Ttl, int McId2){
  
  int n;
  int sessid;

  mRecvCount[F_LOOKUP_RESPONSE] ++;
  message("Recv Lookup Response mcid[%u],sessid[%d], mEndId=%u\n", 
	  McId, SessId, mEndId);
  
  //Send Ack
  mSendCount[F_ACK] ++;
  pProtocol->SendAck(Address, SeqNo);
  
  
  //Recv JoinRedirectResponse
  if(mState == CONNECTING && mTmpSessId == SessId){
    message("JoinRedirect\n");
    SendJoin(Address);
    return;
  }
  
  
  //Update RoutingTable if mcid matched
  for(n = 0; n < MCID_BIT; n++){
    if((mEndId + pow(2, n)) == McId){
      mRoutingTable[n].Ip   = Address.Ip;
      mRoutingTable[n].Port = Address.Port;
      mRoutingTable[n].McId = McId2;
    }
  }
  
  message("TODO---------------debug = %d %u %u\n",
	  mTmpSessIdLogin, SessId, mTmpSessId);

  if(mTmpSessId == SessId){
    
    // Insert Lookup
    if(mTmpInsert == 2){

      //Send File
      if(Address.Ip == mIp && Address.Port == mPort){

	log(WARNING, "send me file on insert\n");
      }else{

	message("send file-----to port%d\n", Address.Port);
	SetTaskOfFile(Address, McId, SessId);
	SendTaskBySession(SessId);
      }
      mTmpInsert = 0;
    }
    
    //Login Lookup 
    if(mTmpSessIdLogin == 1){
      message("Recv Member\n");
      recv_lookup_response(McId);
      mTmpSessIdLogin = 0;
    }





  }else{
    
    log(NOTICE,"different id %u:%u\n", mTmpMcId, mTmpSessId);
    
  }
  
}




//Call by GUID
void MChord::SendInsert(int key){
  
  ADDRESS Address;
  ADDRESS AddressSrc;
  TASKLIST *ptask;  
  int mcid;
  int sess_id;

  //Join Room
  if(mState != NONE && mState != CONNECTING){
    
    message("SendInsert Key=[%u]\n", key);
    
    //Lookup RT
    Address = LookupRT(key);
    message("TTTTTTTTTTTTT%d[%d]\n",Address.Ip, Address.Port);
    mSendCount[F_INSERT] ++;
    
    sess_id = get_random();

    mTmpSessId = sess_id;
    mTmpInsert = 2;
    
    AddressSrc.Ip   = mIp;
    AddressSrc.Port = mPort;
    
    ptask = SetTaskOfData(Address, M_LOOKUP, sess_id, AddressSrc, key, 
			  NONE, NONE);
    SendTask(ptask);

  }else{
    log(WARNING, "Cannot Send while CONNECTING\n");
  }

}


//Call by Protocol 
void MChord::RecvInsert(ADDRESS Address, char *data, int size, int mcid){
  
  mRecvCount[F_INSERT] ++;
  Data2File(mcid, data, size, mCacheDir);
  
  //free(data);
  //id_insert((IDLIST **)&mIdList, mcid);
  
}


//Call by GUI
void MChord::SendLeave(ADDRESS Address){

  IDLIST *idlist_local;
  ADDRESS next;
  int sessid;
  
  mState = DANGEROUS;
  next.Ip   = mIp;
  next.Port = mPort;
  
  idlist_local = mIdList;
  sessid = get_random();

  //send to previd about leave from your node.
  mSendCount[F_LEAVE] ++;
  
  //send leave
  SetTaskOfData(Address, M_LEAVE, sessid, next, NONE, NONE, NONE);
  
  //send file to prevID
  message("sending File mBeginId = %u sess_id = %d\n",mBeginId, sessid);

  while(idlist_local != NULL){
    
    //drop related packet only if packet has same sess_id is drop
    //send all data
    message("mBeginId=%u,id=%u\n", mBeginId, idlist_local->id);
    
    SetTaskOfFile(Address, idlist_local->id, sessid);

    idlist_local = idlist_local->next;
  }

  //finish leave
  SetTaskOfData(Address, M_LEAVE_END, sessid, Address, NONE, NONE, NONE);
  
  SendTaskBySession(sessid);

}


//Call by Protocol
void MChord::RecvLeave(ADDRESS Address, int sessid, ADDRESS next, int BeginId, 
		       int EndId, int SeqNo){
  
  mRecvCount[F_LEAVE] ++;
  message("Recv Leave mBeginId[%u], mEndId=%u, SessId =%d\n", 
	  mBeginId, mEndId, sessid);
  
  //Send Ack
  mSendCount[F_ACK] ++;
  pProtocol->SendAck(Address, SeqNo);
  
  
  //protocolにnextを追加しなければならない
  if(BeginId == (mEndId + 1)){
    mState     = UPDATING;
    mNext.Ip   = next.Ip;
    mNext.Port = next.Port;
    mEndId     = EndId;
    mTmpSessId = sessid;

  }else{
    message("receive different id\n");
  }

}


//Call by Protocol
void MChord::RecvLeaveEnd(ADDRESS Address, int sessid, int SeqNo){

  mRecvCount[F_LEAVE_END] ++;

  //Send Ack
  mSendCount[F_ACK] ++;
  pProtocol->SendAck(Address, SeqNo);
  
  message("Recv Leave End sessid = %d\n", sessid);

}


//Call by Protocol
unsigned int MChord::getBeginId(){
  return mBeginId;
}


int MChord::getPort(){

  return mPort;
}


int MChord::setPort(int port){

  mPort = port;

}


ADDRESS MChord::LookupRT(int mcid){
  
  int cnt;
  int ret;
  ADDRESS Address;

  ret = 0;

  for(cnt = 0; cnt < MCID_BIT; cnt++){    

    //message("|%d\n",mRoutingTable[cnt].Port);
    if((mRoutingTable[cnt].McId) <= mcid){

    //if(( mEndId + pow(2, cnt) ) <= mcid){
      ret = cnt;
    }

  }
  
  Address.Ip   = mRoutingTable[ret].Ip;
  Address.Port = mRoutingTable[ret].Port;
  
  //TODO
  //task wait listができてから削除する
  //RoutingTableに誤差が生じているため補正する
  //if(  (mBeginId < mEndId  && mBeginId <= mcid && mcid <= mEndId)
  //   || mEndId  < mBeginId && (mcid <= mEndId || mBeginId <= mcid) ){
  // Address.Ip = mIp;
  // Address.Port = mPort;
  // }

  return Address;

}


TASKLIST *MChord::LookupTaskBySession(int sess_id ){
  
  TASKLIST *ptask = mTaskList;
  while(ptask != NULL && ptask->SessId != sess_id){
    ptask = ptask->next;
  }
  
  return(ptask);
  
}


TASKLIST *MChord::SendTask(TASKLIST *task){
  
  
  if(task == NULL){
    log(WARNING, "SendTask: task is NULL. Do you set task of own address?\n");
    return NULL;
  }


  switch(task->Func){

  case M_JOIN:
    message("M_JOIN sess_id = [%d]\n", task->SessId);
    mSendCount[F_JOIN] ++;
    pProtocol->SendJoin(task->Address, task->SessId, mBeginId, task->SeqNo);
    break;

  case M_JOIN_ACCEPT:
    message("M_JOIN_ACCEPT\n");
    mSendCount[F_JOIN_ACCEPT] ++;
    pProtocol->SendJoinAccept(task->Address, task->SessId, task->Next, mEndId, 
			      task->SeqNo);
    break;
    
  case M_UPDATE_RT:
    message("UpdateRT\n");
    mSendCount[F_UPDATE_RT] ++;
    task = task_delete((TASKLIST **)&mTaskList, task);
    if(task != NULL){log(WARNING, "TASK IS NOT NULL\n");}
    UpdateRT();
    break;
    
    
  case M_FILE:
    message("M_FILE\n");
    mSendCount[F_FILE] ++;
    
    UpdateTaskOfFile(task);
    pProtocol->SendFile(task->Address, task->Data, task->Size, task->SeqNo, 
			task->SessId, task->McId, task->FileSeqNo, task->Flag);    
    break;

  case M_LOOKUP:
    message("M_LOOKUP\n");
    mSendCount[F_LOOKUP] ++;
    pProtocol->SendLookup(task->Address, task->SessId, task->McId, mBeginId, 
			  DEFAULT_TTL, task->SeqNo, task->Next);//Next=src
    break;
    
  case M_LOOKUP_RESPONSE:
    message("M_LOOKUP_RESPONSE\n");
    mSendCount[F_LOOKUP_RESPONSE] ++;
    pProtocol->SendLookupResponse(task->Address, task->SessId, task->McId, 
				  task->SeqNo, task->Ttl, task->Ans, mEndId);
    break;
    
    
  case M_LEAVE:
    message("M_LEAVE sess_id = [%d]\n", task->SessId);
    mSendCount[F_LEAVE] ++;
    pProtocol->SendLeave(task->Address, task->SessId, task->Next, mBeginId, 
			 mEndId, task->SeqNo);

    break;

  case M_LEAVE_END:
    message("M_LEAVE_END sess_id = [%d]\n", task->SessId);
    mSendCount[F_LEAVE_END] ++;
    pProtocol->SendLeaveEnd(task->Address, task->SessId, task->SeqNo);
    break;


  default:
    message("unkown func type %d\n", task->Func);
    
    break;
    
  }

  return task;
}




TASKLIST *MChord::SetTaskOfData(ADDRESS Address, int Func, int SessId, ADDRESS Next, int McId, int Ans, int Ttl){

  TASKLIST *ptask;

  //check whether task is invalid
  //  if(Address.Ip == mIp && Address.Port == mPort){
  //    log(NOTICE, "return NULL in SetTaskOfData\n");
  //    return NULL;
  //  }
  
  mSeqNo ++;
  
  // add task list
  ptask = task_insert((TASKLIST **)&mTaskList, Address);
  ptask->Type   = T_DATA;
  ptask->Size   = 0;
  ptask->Func   = Func;

  ptask->SessId = SessId;
  ptask->SeqNo  = mSeqNo;
  ptask->Next   = Next;  //JOIN_ACCEPT,LOOKUP_SRC
  ptask->McId   = McId;  //LOOKUP
  ptask->Ans    = Ans;   //LOOKUP_RESPONSE
  ptask->Ttl    = Ttl;   //LOOKUP_RESPONSE

  return ptask;
  
}


void MChord::SetTaskOfEvent(ADDRESS Address, int func, int sess_id){

  TASKLIST *ptask;
  mSeqNo ++;

  //add task list
  ptask = task_insert((TASKLIST **)&mTaskList, Address);
  ptask->Type = T_EVENT;
  ptask->Func = func;
  ptask->RetransmitCount = 0;
  ptask->SeqNo = mSeqNo;
  ptask->SessId = sess_id;

}


//delete task which session is same
TASKLIST *MChord::CancelSession(int session){

  TASKLIST *ptask = mTaskList;


  while(ptask != NULL){
    if(ptask->SessId == session){
    
      //T_DATA
      if(ptask->Type == T_DATA){
	free(ptask->Data);	
      }
      
      //M_FILE
      if(ptask->Type == T_FILE){
	fclose(ptask->Fp);
      }
      
      //return next ptask
      ptask = task_delete((TASKLIST **)&mTaskList, ptask);

    }else{

      ptask = ptask->next;

    }

  }
  
  return ptask;
}



void MChord::SendTaskBySession(int sess_id){
  
  TASKLIST *ptask;
  ptask = LookupTaskBySession(sess_id);
  
  SendTask(ptask);
  
  ptask = Retransmit(ptask);//move next
  
  return;
}


/*
  call by timer
  execute task
*/
void MChord::TimerFunc(){
  
  ADDRESS address_local;
  TASKLIST *ptask = mTaskList;


  //debug
  PrintStatistics();
  

  if(ptask == NULL){
    message("TimerFunc : call by timer : No tasks\n");
  }else{
    message("TimerFunc : call by timer\n");   
  }

  
  //Don't send same Address
  while(ptask != NULL){
    
    message("TimerFunc : send task is %d count = %d.\n", 
	    ptask->SeqNo, ptask->RetransmitCount);
    
    if(address_local.Ip   != ptask->Address.Ip  || 
       address_local.Port != ptask->Address.Port){      
      
      if(ptask->RetransmitCount != 0){
	SendTask(ptask);
      }
      
      //make backup
      if(ptask != NULL && ptask->Type == T_DATA){
	address_local.Ip   = ptask->Address.Ip;
	address_local.Port = ptask->Address.Port; 
      }
      
      //move next
      ptask = Retransmit(ptask);
      
    }else{
      ptask = ptask->next;
    }
    
  }//while
  
  return;

}


/*
  If ptask is restransmit max count then delete task.
  return : next task.
*/
TASKLIST *MChord::Retransmit(TASKLIST *ptask){
  
  int sessid_local;
  
  if (ptask == NULL){
    return NULL;
  }
  
  //delete task if rt_count = max
  if(ptask->RetransmitCount >= (mRetransmitMax - 1)){

    message("delete task. sessid = %d\n", ptask->SessId);

    //delete task which sess_id is same
    ptask = CancelSession(ptask->SessId);

    //return next task
    return ptask;
    
  }

  ptask->RetransmitCount ++;

  //return next task.
  return ptask->next;
}


void MChord::RecvAck(ADDRESS Address, int seqno){
  
  TASKLIST *ptask;
  int sessid_org;
  
  mRecvCount[F_ACK]++;
  message("Recv ACK seq_no[%d] on mchord\n", seqno);
  
  ptask = task_search_sequenceno(mTaskList, seqno);

  if(ptask == NULL){
    log(WARNING, "RecvAck : SeqNo Error\n");
    return;
  }
  
  sessid_org = ptask->SessId;
  
  if(ptask->Func == M_JOIN_ACCEPT){
    DeleteIdList(mTmpMcId);
    mEndId = mTmpMcId - 1;
    message("RECV M_JOIN_ACCEPT ACK\n");
    mNext.Ip   = mTmpNext.Ip;
    mNext.Port = mTmpNext.Port;
  }
  
  //send next chunk file
  if(ptask->Func == M_FILE){

    if ( ptask->Flag == 3 || ptask->Flag == 4){
      message("LAST FILE SEND\n");
      
      task_print((TASKLIST **)&mTaskList);
      message("delete\n");
      
      fclose(ptask->Fp);
      ptask = task_delete((TASKLIST **)&mTaskList, ptask);
      
      task_print((TASKLIST **)&mTaskList);
      message("deleted\n");

      if(ptask != NULL && ptask->SessId == sessid_org){

	message("fast exe\n");
	SendTaskBySession(sessid_org);
      }
      
    }else{
      message("Send File of Next Seq\n");
      SendTask(ptask);
    }
    
  }else{

    
    //T_DATA
    message("Del  seq_no[%d], after list is below\n", ptask->SeqNo);

    //    task_print((TASKLIST **)&mTaskList);
    message("delete\n");

    free(ptask->Data);
    ptask = task_delete((TASKLIST **)&mTaskList, ptask);

    //    task_print((TASKLIST **)&mTaskList);
    message("deleted\n");
    
    
    if(ptask != NULL && ptask->SessId == sessid_org){
      message("fast exe\n");
      SendTaskBySession(sessid_org);
    }
  }


}

void MChord::PrintStatistics(void){

  int n;

#ifdef DEBUG
  return;
#endif DEBUG

  message("STATISTICS(Send/Recv)\n");
  
  for(n = 0; n < STATISTICS_MAX; n++){
    message("%d ", mSendCount[n]);
  }
  
  message("\n");
  
  for(n = 0; n < STATISTICS_MAX; n++){
    message("%d ", mRecvCount[n]);
  }
  
  message("\n");
  message("Begin=%u, End=%u, Next=%u, Prev=%u\n",
	  mBeginId, mEndId, mNext.Port, mPrev.Port);
  
  for(int cnt = 0; cnt < MCID_BIT; cnt++){    
    message("|%d\n",mRoutingTable[cnt].Port);
  }
  


}

int MChord::GetPort(){
  return mPort;
}

int MChord::GetTimer(){
  return mTimer;
}

char *MChord::GetCacheDir(){
  return mCacheDir;
}

int MChord::GetIp(){
  return mIp;
}

int MChord::GetLogFlg(){
  return mLogFlg;
}


//////////////////////
//matchat extension///
//////////////////////

//Call by GUI
void MChord::SendMessage(ADDRESS Address, char *data){

  int sessid;

  sessid = get_random();
  
  pProtocol->SendMessage(Address, sessid, data);
  
}

void MChord::RecvMessage(ADDRESS Address, int SeqNo, char *data){
 

  //Send Ack
  mSendCount[F_ACK] ++;
  pProtocol->SendAck(Address, SeqNo);
  
  message("Recv Message %s\n",data);
  
  //Call main
  recv_data(data);

}

void MChord::message(char *fmt,...){
  
  char file_name[50];
  FILE *fp;
  va_list ap;
  va_start(ap, fmt);
  
#ifdef DEBUG
  //  fprintf(stderr, "%d:", mPort);
  //  vfprintf(stderr, fmt, ap);
#endif DEBUG
  
  if(mLogFlg == 1){ //1 set l flat
    fprintf(stderr, "%d:", mPort);
    vfprintf(stderr, fmt, ap);
    
  }else{
    
    sprintf(file_name,"./%d.log", mPort);
    fp = fopen(file_name,"a");
    if(fp == NULL){
      fprintf(stderr, "mchord: cannot open file = %s\n",file_name);
      return;
    }
    fprintf(fp, "%d:", mPort);
    vfprintf(fp, fmt, ap);
    fclose(fp);
  }

}

void MChord::Quit(){
  
  //close(mLogFP);
  
}
