/*
 * Copyright (C) 2008 The Trustees of Columbia University in the City of New York. 
 * All rights reserved.
 *
 * See the file LICENSE included in this distribution for details.
 */

#include "chordrtable.h"
#include "math.h"


#ifndef _WENGO
#include "gettimeofday.h"
#endif

#ifdef _WENGO
#include "p2pp/sys/gettimeofday.h"
#endif

ChordRTable::ChordRTable()
{
	
}

ChordRTable::~ChordRTable()
{
        
}

uint32 ChordRTable::setParams(uchar* peerID, uint32 lsize, uint32 lbase)
{
        BigInt rowid;
        uchar_to_bigint(peerID, lsize, m_NodeId);
        m_HASH_SIZE = lsize;
        m_Base = lbase;
        //Init the Table Map
        list<Internalpeerinfo> listP;
        ChordDist D;
        D.setKey(m_NodeId);
        D.setParams(m_Base, 8*m_HASH_SIZE);
        int iMax;
        switch(m_Base) {         
          case 2:
            iMax = m_HASH_SIZE*8;
            break;
          case 4:
            if((m_HASH_SIZE*8)%2) {
              iMax = ((m_HASH_SIZE*8)/2)+1;
            }
            else {
              iMax = ((m_HASH_SIZE*8)/2);
            }
            break;
          case 8:
            if((m_HASH_SIZE*8)%3) {
              iMax = ((m_HASH_SIZE*8)/3)+1;
            }
            else {
              iMax = (m_HASH_SIZE*8)/3;
            }
            break;
          case 16:
            if((m_HASH_SIZE*8)%4) {
              iMax = ((m_HASH_SIZE*8)/4)+1;
            }
            else {
              iMax = ((m_HASH_SIZE*8)/4);
            }
            break;
          default:
            printf("ChordRTable::setParams Incorrect base %d Terminating...\n", m_Base);
            exit(1);
            break;
       }
       int i;
       for(i=0; i<iMax; i++) {
         D.getKeyAt(i, rowid);
         RTable[rowid] = listP;
       }
       D.getKeyAt(--i, m_Lastrowid);
       return 0;
}

uint32 ChordRTable::getRow(BigInt lId, BigInt &rowid, int &finger) {	//sets finger to the rowid where lId is a candidate successor.

  map<BigInt, list<Internalpeerinfo> >::iterator mbefore_itr, mitr, reqdIndex;
  list<Internalpeerinfo>::iterator litr;
  BigInt bLow;  
  for(mitr=RTable.begin(); mitr!=RTable.end(); mitr++) {
    if(mitr->first <= lId) {
      mbefore_itr = mitr;
      mitr++;
      if(mitr == RTable.end()) {
        reqdIndex = mbefore_itr;
        break;
      }
      if(mitr->first > lId) {
        mitr--;
        reqdIndex = mitr;
        break;
      }
      mitr --;
    }
    else {
      mbefore_itr = mitr;
      mitr++;
      if(mitr == RTable.end()) {
        reqdIndex = mbefore_itr;
        break;
      }
      mitr--;
    }
  }
  rowid = reqdIndex->first;
  return 0;
}

uint32 ChordRTable::Insert(struct peerinfo lstP)         //Inserts the entry in the appropriate row.
{
	BigInt finger, bTemp;
        int t;
        struct Internalpeerinfo stI;
        stI.m_stP = lstP;
        gettimeofday( &(stI.m_lastseen), NULL);
	uchar_to_bigint((unsigned char *)lstP.stpeerid.id, m_HASH_SIZE, bTemp);	//the length is set as m_HASH_SIZE/8 (needs consideration)
	getRow(bTemp, finger, t);
        if(!isPresent(bTemp, RTable[finger])){
	  (RTable[finger]).push_front(stI);
          return 0;
        }
        else{
          return 1;
        }
}

bool ChordRTable::isPresent(BigInt bKey, list<Internalpeerinfo>& row )
{
  BigInt bTemp;
  list<Internalpeerinfo>::iterator Itr;
  for(Itr=row.begin(); Itr!=row.end(); Itr++){
     uchar_to_bigint((uchar*)Itr->m_stP.stpeerid.id, m_HASH_SIZE, bTemp);
     if(bTemp == bKey){
       return true;
     }
  }
  return false;
}

uint32 ChordRTable::getNextHop(uchar* peerid, uint32 len, struct peerinfo& stP) {
  BigInt bTemp;
  uchar_to_bigint(peerid, len, bTemp);
  return getNextHop(bTemp, stP);
}//getNextHop


bool ChordRTable::Iamcloser(BigInt dest, peerinfo& stP)
{
  BigInt bPid, bMydist, bPdist;
  
  uchar_to_bigint((uchar*)stP.stpeerid.id, m_HASH_SIZE, bPid);
  
  ChordDist D;
  D.setParams(m_Base, m_HASH_SIZE*8);
  D.setKey(m_NodeId);
  D.getClockwise(dest, bMydist);
  
  D.setKey(bPid);
  D.getClockwise(dest, bPdist);
  return (bMydist < bPdist);
}

uint32 ChordRTable::getNextHop(BigInt lKey, peerinfo& stNextP)	//Sets the peerinfo object to the peer which should be the next hop in the lookup for lkey.
{
  BigInt rowid;
  int t;
  if(getRow(lKey, rowid, t)) {
    printf("Problem in ChordRtable::getRow()\n");
    return 1;
  }

  BigInt bTemp, bTempDist, bRowDist;
  map<BigInt, list<Internalpeerinfo> >::iterator mitr, oldmitr, bitr;
  list<Internalpeerinfo>::iterator litr;
  mitr = RTable.find(rowid);
  oldmitr = mitr; 
  bool found = false;
  ChordDist D;
  while(mitr!=RTable.begin()) {
    if(0 < mitr->second.size()) {
 
      D.setKey(mitr->first);
      D.setParams(m_Base, m_HASH_SIZE*8);
      D.getClockwise(lKey, bRowDist);

      for(litr = mitr->second.begin(); litr!=mitr->second.end(); litr++) {
        uchar_to_bigint(litr->m_stP.stpeerid.id, m_HASH_SIZE, bTemp);
        D.getClockwise(bTemp, bTempDist);
        if(bRowDist > bTempDist) {
          found = true;
          stNextP = litr->m_stP;
          break;
        }
      }
    }
    mitr--;
  }

  if(found) {
    if(Iamcloser(lKey, stNextP)) {
      return 1;
    }
    return 0;
  }
  
  //mitr = begin.
  if(0 < mitr->second.size()) {
    D.setKey(mitr->first);
    D.setParams(m_Base, m_HASH_SIZE*8);
    D.getClockwise(lKey, bRowDist);

    for(litr = mitr->second.begin(); litr!=mitr->second.end(); litr++) {
      uchar_to_bigint(litr->m_stP.stpeerid.id, m_HASH_SIZE, bTemp);
      D.getClockwise(bTemp, bTempDist);
      if(bRowDist > bTempDist) {
        found = true;
        stNextP = litr->m_stP;
        break;
      }
    }
  }

  if(found) {
    if(Iamcloser(lKey, stNextP)) {
      return 1;
    }
    return 0;
  }

  //Wrap around.
  
  for(mitr = RTable.find(m_Lastrowid); mitr!=oldmitr; mitr--) {
    if(0 < mitr->second.size()) {
      litr = mitr->second.begin();
      found = true;
      stNextP = litr->m_stP;
      break;  
    }
    if(mitr == RTable.begin()){
      printf("Hit the begin()\n");
      break;
    }
  }
  if(found){
    if(Iamcloser(lKey, stNextP)) {
      return 1;
    }
    return 0;
  }

  return 1;
}

uint32 ChordRTable::RemovePeer(char* pchIP, uint32 port)
{
  BigInt finger;
  list<Internalpeerinfo>::iterator lItr;
  map<BigInt , list<Internalpeerinfo> >::iterator mItr;

  for(mItr = RTable.begin(); mItr != RTable.end(); mItr++) {
    for(lItr = mItr->second.begin(); lItr != mItr->second.end(); lItr++) {
      for(int j=0; j<lItr->m_stP.staddr.num && lItr->m_stP.staddr.isPresent; j++) {
        if(0 == strcmp(lItr->m_stP.staddr.addr[j], pchIP) && port == lItr->m_stP.staddr.port[j]) {
          mItr->second.erase(lItr);
          return 0;
        }
      }
    }
  }

  return 1;
}

uint32 ChordRTable::Remove(BigInt lKey)				 //Removes the peerinfo object with this key.
{
	BigInt finger, temp;
        int t;
	getRow(lKey, finger, t);					//determine the location of this peerinfo object if it exists. 
	list<Internalpeerinfo>::iterator Itr;
	Itr = (RTable[finger]).begin();
	while( Itr!= (RTable[finger]).end() )
	{
		uchar_to_bigint((unsigned char*)(*Itr).m_stP.stpeerid.id, m_HASH_SIZE, temp);
		if(temp!=lKey)
		{
			Itr++;
		}
		else
		{
			break;
		}
	}
	if( Itr == (RTable[finger]).end() )			//not found
	{
		return 1;			
	}
	else							//found. remove it from the list.
	{
		(RTable[finger]).erase(Itr);
		return 0;
	}
}

uint32 ChordRTable::DumpTable(list<peerinfo> &retList)		//dump the Routing table entries to the peerinfo list.
{
	list<Internalpeerinfo>::iterator lItr;

        map<BigInt , list<Internalpeerinfo> >::iterator mItr;
        for(mItr = RTable.begin(); mItr != RTable.end(); mItr++) {
          for(lItr = mItr->second.begin(); lItr != mItr->second.end(); lItr++) {
            retList.push_back(lItr->m_stP);
          }
        }
        return 0;
}

uint32 ChordRTable::getNumRow(uint32 row)                           //Return the number of peerinfo objects for this row. 
{
	BigInt finger;
        ChordDist D;
        D.setKey(m_NodeId);                                       //set the base key for computing distances.
        D.setParams(m_Base, m_HASH_SIZE*8);

	D.getKeyAt(row, finger);

	return (RTable[finger]).size();
}

uint32 ChordRTable::getSize()                                   //Return the total number of peerinfo objects in the table.
{
	uint32 retsize;
	BigInt finger;
	retsize = 0;

        map<BigInt, list<Internalpeerinfo> >::iterator mitr;
        for(mitr = RTable.begin(); mitr!=RTable.end(); mitr++) {                              // for all the rows
          retsize += mitr->second.size();
        }
	return retsize;
}

uint32 ChordRTable::getRowEntries(uint32 row, list<peerinfo>& retlist)     //Set the list to contain the list of peerinfo objects in this row. 
{
	BigInt finger;
        ChordDist D;
        D.setKey(m_NodeId);                                       //set the base key for computing distances.
        D.setParams(m_Base, (m_HASH_SIZE*8));

        D.getKeyAt(row, finger);
	list<Internalpeerinfo>::iterator Itr;	
	
	Itr = (RTable[finger]).begin();				//to iterate over the entries of the row.
	while( Itr != (RTable[finger]).end() )
	{
		retlist.push_back( (*Itr).m_stP );		//store the peerinfo objects in the retlist.
		Itr++;
	}
	return 0;
}

void ChordRTable::Show()
{

  ChordDist D;
  D.setKey(m_NodeId);
  D.setParams(m_Base, (m_HASH_SIZE*8));
  BigInt finger, lpeerid;
  list<Internalpeerinfo>::iterator Itr;
  int iMax;
  switch(m_Base) {
    case 2:
      iMax = m_HASH_SIZE*8;
      break;
    case 4:
      if((m_HASH_SIZE*8)%2) {
        iMax = ((m_HASH_SIZE*8)/2)+1;
      }
      else {
        iMax = ((m_HASH_SIZE*8)/2);
      }
      break;
    case 8:
      if((m_HASH_SIZE*8)%3) {
        iMax = ((m_HASH_SIZE*8)/3)+1;
      }
      else {
        iMax = ((m_HASH_SIZE*8)/3);
      }
      break;
    case 16:
      if((m_HASH_SIZE*8)%4) {
        iMax = ((m_HASH_SIZE*8)/4)+1;
      }
      else {
        iMax = ((m_HASH_SIZE*8)/4);
      }
      break;
    default:
      printf("ChordRTable::setParams Incorrect base %d Terminating...\n", m_Base);
      exit(1);
      break;
  }

  for(int i=0; i< iMax; i++){
    D.getKeyAt(i, finger);
    cout<<"Finger "<<i<<" RowId: "<<finger<<"\t | ";
    for(Itr = RTable[finger].begin(); Itr!=RTable[finger].end(); Itr++){
      uchar_to_bigint(Itr->m_stP.stpeerid.id, m_HASH_SIZE, lpeerid);
      cout<<lpeerid<<" ";

      for(int i=0; i<Itr->m_stP.staddr.num; i++) {
        cout<<"("<<Itr->m_stP.staddr.addr[0]<<" "<<Itr->m_stP.staddr.port[0]<<")";
      }
      cout<<";";
    }
    printf("\n");
  }
}
