/*
 * 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 "peer.h"
#include "ilhelper.h"

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

#ifdef _WENGO
#include "p2pp/utils/genhash.h"
#include "p2pp/common/trace.h"
#include "p2pp/sys/gettimeofday.h"
#endif

//#include <unistd.h>

ILObject::ILObject() {
  pApp = NULL;
  callback=NULL;
  pchKey = NULL;
  kLen=0;
  pchValue=NULL;
  vLen=0;
  Conference = NULL;
  uiTransId=0;
  
}//ILObject


ILObject::~ILObject() {

  //free pchKey
  if(NULL != pchKey ) {
    free(pchKey);
  }

  //free pchValue
  if(NULL != pchValue ) {
    free(pchValue);
  }
  
}//ILObject



ILHelper::ILHelper() {
  
  
}//ILHelper



ILHelper::~ILHelper() {
  
}//ILHelper



void ILHelper::setParams(Peer* pPeer) {
  
  m_pPeer = pPeer;
  
}//setParams



int32 ILHelper::PublishUserInfo(char8* pchUser, int32 len) {

  struct peerinfo stP;
  ILObject* pIL = new ILObject;
  int32 ret;

  TRACE(4, "ILHelper::PublishUserInfo...Begins\n", "");

  
  //Key
  pIL->pchKey = (char*)malloc(kLen+1);
  memcpy(pIL->pchKey, pchKey, kLen);
  *((pIL->pchKey)+kLen)='\0';
  pIL->kLen = kLen;

  //Value
  pIL->pchValue = (char*)malloc(vLen+1);
  memcpy(pIL->pchValue, pchValue, vLen);
  *((pIL->pchValue)+vLen)='\0';
  pIL->vLen = vLen;

  //Callback function
  pIL->callback = callback;

  //App pointer
  pIL->pApp = pApp;
  
  //Compute the hash of the Key
  getHash((uchar*)pchKey, kLen, (uchar*)pIL->pchHash, vLen, m_pPeer->m_HASH_ALG);


  printf("Insert id is %s hash is: 0x", pchKey);
  for(int i=0; i<idlen; i++) {
    printf("%x", pIL->pchHash[i]);
  }
  printf("\n");
  
  
  //getNextHop
  ret=m_pPeer->getNextHop(pIL->pchHash, m_pPeer->m_HASH_SIZE, stP );

  //where to send
  if(0 == ret) {
    //TRACE(3, "ILHelper::Insert...We are the Insert Node. No need to send\n", "");
    printf( "ILHelper::Insert...We are the Insert Node. No need to send\n");
    InsertObject(pIL->pchHash, pchValue, vLen, 3600);
    pIL->callback(pIL->pApp, 200);
    delete pIL;

  } else {
    TRACE(3, "ILHelper::Insert...Send it to next node\n", "");
    //send the put message
    m_pPeer->ComposeInsert(stP, pIL);
  }

  TRACE(3, "ILHelper::Insert...transid: %ld\n", pIL->uiTransId);

  //Insert the whole structure with transaction id as the key
  m_IL[pIL->uiTransId] = pIL;
  */  
  
  
}//PublishUserInfo



int32 ILHelper::Insert(char8* pchKey, int32 kLen, char8* pchValue, int32 vLen, void* (*callback)(void*, int32), void* pApp ) {

  /*  
  struct peerinfo stP;
  ILObject* pIL = new ILObject;
  int32 ret;

  TRACE(4, "ILHelper::Insert...Begins\n", "");

  
  //Key
  pIL->pchKey = (char*)malloc(kLen+1);
  memcpy(pIL->pchKey, pchKey, kLen);
  *((pIL->pchKey)+kLen)='\0';
  pIL->kLen = kLen;

  //Value
  pIL->pchValue = (char*)malloc(vLen+1);
  memcpy(pIL->pchValue, pchValue, vLen);
  *((pIL->pchValue)+vLen)='\0';
  pIL->vLen = vLen;

  //Callback function
  pIL->callback = callback;

  //App pointer
  pIL->pApp = pApp;
  
  //Compute the hash of the Key
  getHash((uchar*)pchKey, kLen, (uchar*)pIL->pchHash, vLen, m_pPeer->m_HASH_ALG);


  printf("Insert id is %s hash is: 0x", pchKey);
  for(int i=0; i<idlen; i++) {
    printf("%x", pIL->pchHash[i]);
  }
  printf("\n");
  
  
  //getNextHop
  ret=m_pPeer->getNextHop(pIL->pchHash, m_pPeer->m_HASH_SIZE, stP );

  //where to send
  if(0 == ret) {
    //TRACE(3, "ILHelper::Insert...We are the Insert Node. No need to send\n", "");
    printf( "ILHelper::Insert...We are the Insert Node. No need to send\n");
    InsertObject(pIL->pchHash, pchValue, vLen, 3600);
    pIL->callback(pIL->pApp, 200);
    delete pIL;

  } else {
    TRACE(3, "ILHelper::Insert...Send it to next node\n", "");
    //send the put message
    m_pPeer->ComposeInsert(stP, pIL);
  }

  TRACE(3, "ILHelper::Insert...transid: %ld\n", pIL->uiTransId);

  //Insert the whole structure with transaction id as the key
  m_IL[pIL->uiTransId] = pIL;
  */

  return 1;
}//Insert



int32 ILHelper::RLookup(char8* pchKey, int32 kLen, void* (*callback)(void*, int32), void* pApp , void* Conference) {
  struct peerinfo stP;
  ILObject* pIL = new ILObject;
  int32 ret;
  map<BigInt, InternalResourceObject>::iterator itr;
  BigInt bKey;
  int32 found=0;

  /*

  TRACE(4, "ILHelper::RLookup...Begins\n", "");
  
  //Key
  pIL->pchKey = (char*)malloc(kLen+1);
  memcpy(pIL->pchKey, pchKey, kLen);
  *((pIL->pchKey)+kLen)='\0';
  pIL->kLen = kLen;

  //Callback function
  pIL->callback = callback;

  //App pointer
  pIL->pApp = pApp;

  //Conf pointer
  pIL->Conference = Conference;

  //Compute the hash of the Key
  getHash((uchar*)pchKey, kLen, (uchar*)pIL->pchHash, m_pPeer->m_HASH_SIZE, m_pPeer->m_HASH_ALG);


  printf("RLookup id is %s hash is: 0x", pchKey);
  for(int i=0; i<idlen; i++) {
    printf("%x", pIL->pchHash[i]);
  }
  printf("\n");
  
  
  //getNextHop
  ret=m_pPeer->getNextHop(pIL->pchHash, m_pPeer->m_HASH_SIZE, stP );


  //check if we have the resource by any chance?  
  uchar_to_bigint(pIL->pchHash, m_pPeer->m_HASH_SIZE, bKey);
  
  itr=m_objRO.find(bKey);
  if(itr != m_objRO.end() ) {
    TRACE(4, "ILHelper::RLookup...we have the object...no need to send request\n", "");

    pIL->callback( (void*)(&*itr), 200);
    delete pIL;
    return 1;
  }

  //where to send
  if(0 == ret) {
    //TRACE(3, "ILHelper::Insert...We are the Insert Node. No need to send\n", "");
    
    printf( "ILHelper::RLookup...Looking at self...Not found\n");
    pIL->callback(pIL, 404);
    delete pIL;

  } else {
    TRACE(3, "ILHelper::RLookup...Send it to next node\n", "");
    //send the put message
    m_pPeer->ComposeRLookup(stP, pIL);
  }

  TRACE(3, "ILHelper::RLookup...transid: %ld\n", pIL->uiTransId);

  //Insert the whole structure with transaction id as the key
  m_IL[pIL->uiTransId] = pIL;

  */

  return 1;

}//RLookup




int32 ILHelper::PLookup(char8* pchKey, int32 kLen, void* (*callback)(void*, int32), void* pApp , void* Conference) {

  /*
  
  struct peerinfo stP;
  ILObject* pIL = new ILObject;
  int32 ret;
  int32 different=0;

  TRACE(4, "ILHelper::PLookup...Begins\n", "");
  
  //Key
  pIL->pchKey = (char*)malloc(kLen+1);
  memcpy(pIL->pchKey, pchKey, kLen);
  *((pIL->pchKey)+kLen)='\0';
  pIL->kLen = kLen;

  //Callback function
  pIL->callback = callback;

  //App pointer
  pIL->pApp = pApp;

  //Conf pointer
  pIL->Conference = Conference;

  //Compute the hash of the Key
  getHash((uchar*)pchKey, kLen, (uchar*)pIL->pchHash, m_pPeer->m_HASH_SIZE, m_pPeer->m_HASH_ALG);


  printf("PLookup id is %s hash is: 0x", pchKey);
  for(int i=0; i<idlen; i++) {
    printf("%x", pIL->pchHash[i]);
  }
  printf("\n");
  
  
  //getNextHop
  ret=m_pPeer->getNextHop(pIL->pchHash, m_pPeer->m_HASH_SIZE, stP );

  //where to send
  if(0 == ret) {
    //TRACE(3, "ILHelper::Insert...We are the Insert Node. No need to send\n", "");    

    for(int i=0; i<m_pPeer->m_HASH_SIZE; i++) {
      if(pIL->pchHash[i] != m_pPeer->m_ID[i]) {
        different=1;
        break;
      }
    }//end for

    if(1 == different) {
      printf( "ILHelper::PLookup...Looking at self...Not found\n");
      pIL->callback(pIL, 404);
      delete pIL;

    } else {
      printf( "ILHelper::PLookup...Looking for self\n");
      pIL->callback(pIL, 200);
      delete pIL;
    }

  } else {
    TRACE(3, "ILHelper::PLookup...Send it to next node\n", "");
    //send the put message
    m_pPeer->ComposePLookup(stP, pIL);
  }

  TRACE(3, "ILHelper::PLookup...transid: %ld\n", pIL->uiTransId);

  //Insert the whole structure with transaction id as the key
  m_IL[pIL->uiTransId] = pIL;

  */

  return 1;
}//PLookup



int32 ILHelper::OnInsertRequest(struct message& msg) {
  
  
  return 1;
}//OnInsertRequest




int32 ILHelper::OnPLookupRequest(struct message& msg, char* pchIP, int32 port, transtype tt) {
  /*

  struct peerinfo stPSelf, stPNext;
  int32 respCode=0;

  m_pPeer->getSelfPeer(stPSelf);

  //get next hop
  if(0 == m_pPeer->getNextHop(msg.stplookup.peerid, m_pPeer->m_HASH_SIZE, stPNext)) {

    respCode = 200;

    //Compare Lookup peer id to stPNext.peerid
    for(int i=0; i<m_pPeer->m_HASH_SIZE; i++) {
      if(msg.stplookup.peerid[i] != stPSelf.peerid[i]) {
        respCode = 404;
        break;
      }          
    }//end for

    printf("LOOKUPPEER sending respcode %d %s %d isaddr: %d\n", respCode, stPNext.staddr.addr[0], stPNext.staddr.port[0], stPNext.isPresent);
        
  } else {
    respCode = 302;

    printf("LOOKUPPEER sending respcode %d %s %d isaddr: %d\n", respCode, stPNext.staddr.addr[0], stPNext.staddr.port[0], stPNext.isPresent);
  }

  //Send the response
  m_pPeer->ComposePLookupResp(respCode, stPSelf, stPNext, pchIP, port, tt);

  */
  return 1;
}//OnPLookupRequest




int32 ILHelper::OnRLookupRequest(struct message& msg, char* pchIP, int32 port, transtype tt) {
  /*

  struct peerinfo stPSelf, stPNext;
  int32 respCode=0;
  BigInt bKey;
  int32 found=0;
  InternalResourceObject objIRO;
  map<BigInt, InternalResourceObject>::iterator itr;


  //getSelf address
  m_pPeer->getSelfPeer(stPSelf);

  //check if we have the resource.
  uchar_to_bigint(msg.stro.resid, m_pPeer->m_HASH_SIZE, bKey);
  
  itr=m_objRO.find(bKey);
  if(itr != m_objRO.end() ) {
    TRACE(4, "ILHelper::OnRLookupRequest...we have the object...yeh %s\n", itr->second.pchValue);
    respCode=200;
    found=1;
  }

  //get next hop
  if(0 == m_pPeer->getNextHop(msg.stro.resid, m_pPeer->m_HASH_SIZE, stPNext)) {

    //we are the hop but we do not have the object!!!
    if(1 != found) {
      TRACE(4, "ILHelper::OnRLookupRequest...we are the hop but we dont have the object\n", "");
      respCode = 404;
    }
  } else {
    respCode = 302;

    printf("LOOKUPRESOURCE sending respcode %d %s %d isaddr: %d\n", respCode, stPNext.staddr.addr[0], stPNext.staddr.port[0], stPNext.isPresent);
  }

  //Send the response
  if(404 == respCode) {    
    m_pPeer->ComposeRLookupResp(respCode, stPSelf, stPNext, pchIP, port, tt, objIRO);
  } else {
    m_pPeer->ComposeRLookupResp(respCode, stPSelf, stPNext, pchIP, port, tt, itr->second);
  }
  
  */

  return 1;
}//OnRLookupRequest



int32 ILHelper::OnInsertResponse(struct message& msg) {
  map<uint32, ILObject*>::iterator itr;
  list<peerinfo>::iterator litr;

  ILObject* pIL;
  
  TRACE(2, "ILHelper::OnInsertResponse...m_IL map size: %d\n", m_IL.size() );

  /*
  //Find the object corresponding to this transaction
  if( (itr=m_IL.find(msg.stcmn_hdr.transid)) != m_IL.end() ) {
    pIL=itr->second;    
  } else {
    TRACE(2, "ILHelper::OnInsertResponse...Insert response nothing in map_IL respcode: %d transID: %d\n", msg.stcmn_hdr.respcode, msg.stcmn_hdr.transid);
    return 0;
  }

  //Noew remove the old transaction from map
  m_IL.erase(itr);

  if(302 == msg.stcmn_hdr.respcode) {
    TRACE(2, "ILHelper::OnInsertRequest...302 for Insert...send Insert\n", "");

    //if there is a 302 present
    if(msg.stlpeerinfo.size() > 0) {
      litr=msg.stlpeerinfo.begin();
   
      m_pPeer->ComposeInsert(*litr, pIL);
      
      //Update the object according in new map
      m_IL[pIL->uiTransId] = pIL;
      
    } else {
      printf("Error in Insert...There was no peerinfo object in 302\n");
    }
    
    printf("Insert: 302 response %s %d\n", msg.stpeerinfo.staddr.addr[0], msg.stpeerinfo.staddr.port[0]);
    
  } else if(200 == msg.stcmn_hdr.respcode) {
    TRACE(2, "ILHelper::OnResponse...200 for Insert\n", "");

    printf("insert 200 response\n");
    //call the application function or send the response to client
    pIL->callback(pIL->pApp, 200);

    delete pIL;
  }

  */

  return 1;

}//OnInsertResponse





int32 ILHelper::OnPLookupResponse(struct message& msg) {
  map<uint32, ILObject*>::iterator itr;
  list<peerinfo>::iterator litr;

  ILObject* pIL;
  
  TRACE(2, "ILHelper::OnPLookupResponse...m_IL map size: %d\n", m_IL.size() );

  /*
  //Find the object corresponding to this transaction
  if( (itr=m_IL.find(msg.stcmn_hdr.transid)) != m_IL.end() ) {
    pIL=itr->second;    
  } else {
    TRACE(2, "ILHelper::OnPLookupResponse...PLookup response nothing in map_IL respcode: %d transID: %d\n", msg.stcmn_hdr.respcode, msg.stcmn_hdr.transid);
    return 0;
  }
  
  //Noew remove the old transaction from map
  m_IL.erase(itr);
  
  
  if(302 == msg.stcmn_hdr.respcode) {
    TRACE(2, "ILHelper::OnPLookupRequest...302 for PLookup...send PLookup\n", "");

    //if there is a 302 present
    if(msg.stlpeerinfo.size() > 0) {
      litr=msg.stlpeerinfo.begin();
   
      m_pPeer->ComposePLookup(*litr, pIL);
      
      //Update the object according in new map
      m_IL[pIL->uiTransId] = pIL;
      
    } else {
      printf("Error in PLookup...There was no peerinfo object in 302\n");
    }
    
    printf("PLookup: 302 response %s %d\n", msg.stpeerinfo.staddr.addr[0], msg.stpeerinfo.staddr.port[0]);
    
  } else if(200 == msg.stcmn_hdr.respcode || 404 == msg.stcmn_hdr.respcode) {
    TRACE(2, "ILHelper::OnPLookupResponse...%d\n", "");

    printf("plookup %d response\n", msg.stcmn_hdr.respcode);
    if(200 == msg.stcmn_hdr.respcode) {
      printf("Peer found. %s : %d\n", msg.stpeerinfo.staddr.addr[0], msg.stpeerinfo.staddr.port[0]);
      strcpy(pIL->retIP, msg.stpeerinfo.staddr.addr[0]);
    }
    //call the application function or send the response to client
    //pIL->callback(pIL->pApp, msg.stcmn_hdr.respcode);
    pIL->callback(pIL, msg.stcmn_hdr.respcode);

    delete pIL;
  }

  */
  return 1;

}//OnPLookupResponse




int32 ILHelper::OnRLookupResponse(struct message& msg) {
  map<uint32, ILObject*>::iterator itr;
  list<peerinfo>::iterator litr;

  ILObject* pIL;
  
  TRACE(2, "ILHelper::OnRLookupResponse...m_IL map size: %d\n", m_IL.size() );

  /*
  //Find the object corresponding to this transaction
  if( (itr=m_IL.find(msg.stcmn_hdr.transid)) != m_IL.end() ) {
    pIL=itr->second;    
  } else {
    TRACE(2, "ILHelper::OnRLookupResponse...RLookup response nothing in map_IL respcode: %d transID: %d\n", msg.stcmn_hdr.respcode, msg.stcmn_hdr.transid);
    return 0;
  }
  
  //Noew remove the old transaction from map
  m_IL.erase(itr);
  
  
  if(302 == msg.stcmn_hdr.respcode) {
    TRACE(2, "ILHelper::OnRLookupRequest...302 for RLookup...send PLookup\n", "");

    //if there is a 302 present
    if(msg.stlpeerinfo.size() > 0) {
      litr=msg.stlpeerinfo.begin();
   
      m_pPeer->ComposeRLookup(*litr, pIL);
      
      //Update the object according in new map
      m_IL[pIL->uiTransId] = pIL;
      
    } else {
      printf("Error in LookupResource...There was no peerinfo object in 302\n");
    }
    
    printf("LookupResource: 302 response %s %d\n", msg.stpeerinfo.staddr.addr[0], msg.stpeerinfo.staddr.port[0]);
    
  } else if(200 == msg.stcmn_hdr.respcode || 404 == msg.stcmn_hdr.respcode) {
    TRACE(2, "ILHelper::OnRLookupResponse...%d\n", "");

    printf("rlookup %d response\n", msg.stcmn_hdr.respcode);
    if(200 == msg.stcmn_hdr.respcode) {      
      
      pIL->pchValue = (char*)malloc(msg.stro.resobjlen+1);
      memcpy(pIL->pchValue, msg.stro.resobj, msg.stro.resobjlen);
      pIL->pchValue[msg.stro.resobjlen]='\0';

      printf("Resource found. %s : %d len: %d value: %s\n", msg.stpeerinfo.staddr.addr[0], msg.stpeerinfo.staddr.port[0], msg.stro.resobjlen, pIL->pchValue);
    }
    //call the application function or send the response to client

    pIL->callback(pIL, msg.stcmn_hdr.respcode);
    delete pIL;
  }

  */
  return 1;

}//OnRLookupResponse




int32 ILHelper::OnTimer() {
  
  return 1;
}//OnTimer

int32 ILHelper::OnTransactionTimeout(uint32 uiTransId, char* pchIP, int32 port) {
  
  return 1;
}//OnTransactionTimeout


int32 ILHelper::InsertObject(char* pchUserID, int32 len) {
  
  
}//InsertObject



int32 ILHelper::InsertObject(struct message& msg, uint32 uiExpires) {
  BigInt bKey;
  struct InternalResourceObject iro;

  TRACE(3, "ILHelper::InsertObject...called\n", "");

  /*
  //If resource object not present
  if(!msg.stro.isPresent)
    return 0;

  //If insert not present
  if(!msg.stro.I)
    return 0;
  
  //Convert resource id to bigint
  uchar_to_bigint(msg.stro.resid, m_pPeer->m_HASH_SIZE, bKey);
  
  TRACE(3, "0x", "");
  for(int i=0; i<m_pPeer->m_HASH_SIZE; i++) {
    TRACE(3, "%x", msg.stro.resid[i]);
  }
  TRACE(3, "\n", "");

  //resource object length
  iro.vLen = msg.stro.resobjlen;
  
  //resource object value
  memcpy(iro.pchValue, msg.stro.resobj, iro.vLen);

  //gettimeofday
  gettimeofday(&iro.tExpires, NULL);

  //addtimeval
  iro.tExpires = addtimeval(iro.tExpires, 3600, 0);
  
  //insert the object in the map
  m_objRO[bKey] = iro;

  printf("key in map is: %s\n", bKey.ToHex().c_str() );

  */

  return 1;
}//InsertObject


int32 ILHelper::InsertObject(uchar* pchKey, char8* pchValue, int32 vLen, uint32 uiExpires) {
  BigInt bKey;
  struct InternalResourceObject iro;

  TRACE(3, "ILHelper::InsertObject...SELF...called\n", "");


  /*  
  //Convert resource id to bigint
  uchar_to_bigint((uchar*)pchKey, m_pPeer->m_HASH_SIZE, bKey);
  
  TRACE(3, "0x", "");
  for(int i=0; i<m_pPeer->m_HASH_SIZE; i++) {
    TRACE(3, "%x", (uchar)pchKey[i]);
  }
  TRACE(3, "\n", "");

  //resource object length
  iro.vLen = vLen;
  
  //resource object value
  memcpy(iro.pchValue, (uchar*)pchValue, iro.vLen);

  //gettimeofday
  gettimeofday(&iro.tExpires, NULL);

  //addtimeval
  iro.tExpires = addtimeval(iro.tExpires, uiExpires, 0);
  
  //insert the object in the map
  m_objRO[bKey] = iro;

  printf("key in map is: %s\n", bKey.ToHex().c_str() );

  */

  return 1;
}//InsertObject



void ILHelper::ShowRO() {
  
  struct peerinfo stP;
  map<BigInt, InternalResourceObject>::iterator itr;
  string str;
  struct timeval curtime, remtime;

  /*  
  m_pPeer->getSelfPeer(stP);

  printf("\n");
  printf("Node Id: 0x");
  
  for(int i=0; i<m_pPeer->m_HASH_SIZE; i++) {
    printf("%x", m_pPeer->m_ID[i]);
  }
  printf(" %s %d\n", stP.staddr.addr[0], stP.staddr.port[0]);
  printf("Resource object\n");
  printf("---------------\n");
  
  
  for(itr=m_objRO.begin(); itr!=m_objRO.end(); itr++) {
    str=itr->first.ToHex();

    printf("ID 0x%s Value: ", str.c_str() );
    
    for(int i=0; i<itr->second.vLen; i++) {
      printf("%c", itr->second.pchValue[i]);
    }

    //current time of day
    gettimeofday(&curtime, NULL);    
    remtime=difftimeval(itr->second.tExpires, curtime);

    printf(" Rem Time: %d\n", remtime.tv_sec);
    
  }//end for
  
  */

}//ShowRO




