/*
 * 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 "chordntable.h"

ChordNTable::ChordNTable()
{
        ;
}

ChordNTable::~ChordNTable()
{
        ;
}

uint32 ChordNTable::setParams(uchar* peerID, uint32 lsize, uint32 lbase) {

  uchar_to_bigint(peerID, lsize, m_NodeId);
  m_HASH_SIZE = lsize;
  m_Base = lbase;
  return 0;
}

uint32 ChordNTable::Insert(struct peerinfo objP) {

  Insert_Pred(objP);
  Insert_Succ(objP);
  return 0;
}

uint32 ChordNTable::Insert_Pred(struct peerinfo lstP)                  //Inserts the entry in the appropriate list.
{
	Internalpeerinfo stI;
	stI.m_stP = lstP;
	gettimeofday( &(stI.m_lastseen), NULL);
        ChordDist D;
        D.setKey(m_NodeId);
        D.setParams(m_Base, m_HASH_SIZE*8);

        list<Internalpeerinfo>::iterator Itr;

        if(isPresent(stI, m_Pred_list)){
          return 0;
        }

        if( 0 ==  m_Pred_list.size()){
          m_Pred_list.push_back(stI);
          printf("First predecessor being added\n");
        }

        else if( 1 == m_Pred_list.size()){
          BigInt bIdOld, bIdNew, bDistOld, bDistNew; 
          Itr = m_Pred_list.begin();
          uchar_to_bigint(Itr->m_stP.stpeerid.id, m_HASH_SIZE, bIdOld);
          uchar_to_bigint(lstP.stpeerid.id, m_HASH_SIZE, bIdNew);
          D.getClockwise(bIdOld, bDistOld);
          D.getClockwise(bIdNew, bDistNew);
          if(bDistNew < bDistOld){
            m_Pred_list.push_back(stI);
          }
          else{
            m_Pred_list.push_front(stI);
          }
          printf("second predecessor being added\n");
        }
        else{             //size is 2.
          Itr = m_Pred_list.begin();
          BigInt bIdOld, bIdNew, bDistOld, bDistNew;
          uchar_to_bigint(Itr->m_stP.stpeerid.id, m_HASH_SIZE, bIdOld);
          uchar_to_bigint(lstP.stpeerid.id, m_HASH_SIZE, bIdNew);
          D.getClockwise(bIdOld, bDistOld);
          D.getClockwise(bIdNew, bDistNew);
          if(bDistNew > bDistOld){
            m_Pred_list.push_front(stI);
            Itr = m_Pred_list.begin();
            Itr++;
            Itr++;
            m_Pred_list.erase(Itr);
            printf("Changing a predecessor\n");
          }
          else{
            Itr = m_Pred_list.begin();
            Itr++;
            uchar_to_bigint(Itr->m_stP.stpeerid.id, m_HASH_SIZE, bIdOld);
            D.getClockwise(bIdOld, bDistOld);
            if(bDistOld < bDistNew){
              m_Pred_list.erase(Itr); 
              m_Pred_list.push_back(stI);
              printf("Changing a predecessor\n");
            }
          }
        } 
	return 0;
}

uint32 ChordNTable::Insert_Succ(struct peerinfo lstP)                  //Inserts the entry in the appropriate list.
{
        Internalpeerinfo stI;
        stI.m_stP = lstP;
	gettimeofday( &(stI.m_lastseen), NULL);
        ChordDist D;
        D.setKey(m_NodeId);
        D.setParams(m_Base, m_HASH_SIZE*8);

        list<Internalpeerinfo>::iterator Itr;

        if(isPresent(stI, m_Succ_list)){
          return 0;
        }

        if( 0 ==  m_Succ_list.size()){
          m_Succ_list.push_back(stI);
          printf("first successor being added\n");
        }

        else if( 1 == m_Succ_list.size()){
          BigInt bIdOld, bIdNew, bDistOld, bDistNew;
          Itr = m_Succ_list.begin();
          uchar_to_bigint(Itr->m_stP.stpeerid.id, m_HASH_SIZE, bIdOld);
          uchar_to_bigint(lstP.stpeerid.id, m_HASH_SIZE, bIdNew);
          D.getClockwise(bIdOld, bDistOld);
          D.getClockwise(bIdNew, bDistNew);
          if(bDistNew > bDistOld){
            m_Succ_list.push_back(stI);
          }
          else{
            m_Succ_list.push_front(stI);
          }
          printf("second sucessor being added\n");
        }
        else{             //size is 2.
          Itr = m_Succ_list.begin();
          BigInt bIdOld, bIdNew, bDistOld, bDistNew;
          uchar_to_bigint(Itr->m_stP.stpeerid.id, m_HASH_SIZE, bIdOld);
          uchar_to_bigint(lstP.stpeerid.id, m_HASH_SIZE, bIdNew);
          D.getClockwise(bIdOld, bDistOld);
          D.getClockwise(bIdNew, bDistNew);
          if(bDistNew < bDistOld){
            m_Succ_list.push_front(stI);
            Itr = m_Succ_list.begin();
            Itr++;
            Itr++;
            m_Succ_list.erase(Itr);
            printf("changing a sucessor\n");
          }
          else{
            Itr = m_Succ_list.begin();
            Itr++;
            uchar_to_bigint(Itr->m_stP.stpeerid.id, m_HASH_SIZE, bIdOld);
            D.getClockwise(bIdOld, bDistOld);
            if(bDistOld > bDistNew){
              m_Succ_list.erase(Itr);
              m_Succ_list.push_back(stI);
              printf("changing a sucessor\n");
            }
          }
        }
		
        return 0;
}


bool ChordNTable::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 ChordNTable::getNextHop(uchar* peerid, uint32 len, struct peerinfo& stNextP) {

  BigInt bPid, bMydist, bTempId, bTempDist, bMin;
  uchar_to_bigint((uchar*)peerid, len, bPid);
  list<Internalpeerinfo>::iterator Itr;

  ChordDist D;
  D.setParams(m_Base, m_HASH_SIZE*8);
  D.setKey(m_NodeId);
  D.getClockwise(bPid, bMydist);
  bMin = bMydist;
  for(Itr = m_Succ_list.begin(); Itr!=m_Succ_list.end(); Itr++){
    uchar_to_bigint((uchar*)Itr->m_stP.stpeerid.id, m_HASH_SIZE, bTempId);
    D.setKey(bTempId);
    D.getClockwise(bPid, bTempDist);
    if(bTempDist < bMin){
      bMin = bTempDist;
      stNextP = (*Itr).m_stP;
    }
  }

  if(bMin == bMydist){
    return 1;
  }
  else{
    return 0;   //stP is set to the closest neighbor.
  }
}//getNextHop

uint32 ChordNTable::getNextHop(BigInt, peerinfo&)
{
  return 0;
}

uint32 ChordNTable::RemovePeer(char* pchIP, uint32 port)
{
 
  bool removed = false;
  list<Internalpeerinfo>::iterator litr;
  for(litr = m_Pred_list.begin(); litr != m_Pred_list.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]) {
        printf("ChordNTable::RemovePeer...removing Predecessor: %s %d\n", pchIP, port);
        m_Pred_list.erase(litr);
        removed = true;
        break;
      }
    }
    if(removed) {
      break;
    }
  }
  removed = false;
  for(litr = m_Succ_list.begin(); litr != m_Succ_list.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]) {
        printf("ChordNTable::RemovePeer...removing Successor: %s %d\n", pchIP, port);
        m_Succ_list.erase(litr);
        removed = true;
        break;
      }
    }
    if(removed) {
      break;
    }
  }

  return 0;
}

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

uint32 ChordNTable::Remove_Succ(BigInt lkey)                            //Removes the peerinfo object with this key.
{
	BigInt temp;
	list<Internalpeerinfo>::iterator Itr;

	Itr = m_Succ_list.begin();				
	while(Itr != m_Succ_list.end())
	{
		uchar_to_bigint((unsigned char*)Itr->m_stP.stpeerid.id, (m_HASH_SIZE), temp);
		if( temp == lkey )
		{
			m_Succ_list.erase(Itr);
			break;
		}
		else
		{
			Itr++;
		}
	}	
	if( Itr == m_Succ_list.end() )			//Not found. return 1
	{
		return 1;
	}
	else						//found and removed. return 0.
	{
//		m_Succ_list.sort();			//maintain the list as sorted.
		return 0;
	}
}

uint32 ChordNTable::Remove_Pred(BigInt lkey)                            //Removes the peerinfo object with this key.
{
	BigInt temp;
        list<Internalpeerinfo>::iterator Itr;

        Itr = m_Pred_list.begin();        
        while(Itr != m_Pred_list.end())
        {
		uchar_to_bigint((unsigned char*)(*Itr).m_stP.stpeerid.id, (m_HASH_SIZE), temp);

                if( temp == lkey )
                {
                        m_Pred_list.erase(Itr);
                        break;
                }
                else
                {
                        Itr++;
                }
        } 
        if( Itr == m_Pred_list.end() )                    //Not found. return 1
        {
                return 1;
        }
        else                                            //found and removed. return 0.
        {
//		Pred_list.sort();
//		Pred_list.reverse();			//maintain the list as sorted in decending order.
                return 0;
        }
}

uint32 ChordNTable::DumpSuccList(list<peerinfo> &retlist)               //dump the Successor list entries to the peerinfo list.
{
        list<Internalpeerinfo>::iterator Itr;
	Itr = m_Succ_list.begin();

	while( Itr != m_Succ_list.end() )
	{
                retlist.push_back( (*Itr).m_stP );                	//store the peerinfo objects in the retlist.
                Itr++;
	}
	return 0;
}

uint32 ChordNTable::DumpPredList(list<peerinfo> &retlist)               //dump the Predecessor list entries to the peerinfo list.
{
        list<Internalpeerinfo>::iterator Itr;
        Itr = m_Pred_list.begin();

        while( Itr != m_Pred_list.end() )
        {
                retlist.push_back( (*Itr).m_stP );                        //store the peerinfo objects in the retlist.
                Itr++;
        }
        return 0;
}

void ChordNTable::Show()
{
  list<Internalpeerinfo>::iterator lItr;
  BigInt bTemp;
  cout<<"Predecessors are : ";
  for(lItr = m_Pred_list.begin(); lItr!=m_Pred_list.end(); lItr++){
    uchar_to_bigint(lItr->m_stP.stpeerid.id, m_HASH_SIZE, bTemp);
    cout<<bTemp<<" ";

    for(int i=0; i<lItr->m_stP.staddr.num; i++) {
      cout<<"("<<lItr->m_stP.staddr.addr[0]<<" "<<lItr->m_stP.staddr.port[0]<<")";
    }
    cout<<";";
  }
  cout<<"\n";
  cout<<"Successors are : ";
  for(lItr = m_Succ_list.begin(); lItr!=m_Succ_list.end(); lItr++){
    uchar_to_bigint(lItr->m_stP.stpeerid.id, m_HASH_SIZE, bTemp);
    cout<<bTemp<<" ";

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

uint32 ChordNTable::getSize() {

  return (m_Succ_list.size() + m_Pred_list.size());
}

