/*********************************************************************
 *
 * AUTHORIZATION TO USE AND DISTRIBUTE
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that: 
 *
 * (1) source code distributions retain this paragraph in its entirety, 
 *  
 * (2) distributions including binary code include this paragraph in
 *     its entirety in the documentation or other materials provided 
 *     with the distribution, and 
 *
 * (3) all advertising materials mentioning features or use of this 
 *     software display the following acknowledgment:
 * 
 *      "This product includes software written and developed 
 *       by Brian Adamson, Joe Macker and Justin Dean of the 
 *       Naval Research Laboratory (NRL)." 
 *         
 *  The name of NRL, the name(s) of NRL  employee(s), or any entity
 *  of the United States Government may not be used to endorse or
 *  promote  products derived from this software, nor does the 
 *  inclusion of the NRL written and developed software  directly or
 *  indirectly suggest NRL or United States  Government endorsement
 *  of this product.
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 ********************************************************************/

// New router template for adding a protocol to CMU extensions of ns2

#include <nrlolsr/ns/protolibManetKernel.h>
#include <nrlolsr/ns/protolibManetKernel_packet.h>

/*
  TCL Hooks
*/
int hdr_ProtolibManetKernel::offset_;

static class ProtolibManetKernelHeaderClass : public PacketHeaderClass {  
public:

  ProtolibManetKernelHeaderClass() : PacketHeaderClass("PacketHeader/ProtolibManetKernel",//0){
    					   sizeof(hdr_ProtolibManetKernel)) { //this is might not be correct...
    bind_offset(&hdr_ProtolibManetKernel::offset_);
  } 
} class_rtProtoProtolibManetKernel_hdr;


static class ProtolibManetKernelclass : public TclClass {
public:
  ProtolibManetKernelclass() : TclClass("Agent/ProtolibManetKernel") {}
  TclObject* create(int argc, const char*const* argv) {
    assert(argc == 5); 
    return (new ProtolibManetKernel((nsaddr_t) Address::instance().str2addr(argv[4]))); 
  }
} class_rtProtoProtolibManetKernel;

// interface with tcl scripts can change valuse on the fly by getting the class instance handle in tcl
int 
ProtolibManetKernel::command(int argc, const char*const* argv) {
  if(argc == 2) {
    Tcl& tcl = Tcl::instance(); 
    
    if(strncasecmp(argv[1], "id", 2) == 0) {
      tcl.resultf("%d", index);
      return TCL_OK;
    }
    
    if(strncasecmp(argv[1], "start", 2) == 0) { 
      return TCL_OK;
    }               
  }
  else if(argc == 3) {
    if(strcmp(argv[1], "attach-manet") == 0) {
      manetpointer = (Agent*)TclObject::lookup(argv[2]); 
      return TCL_OK;
    }
    else if(strcmp(argv[1], "log-target" ) == 0 || strcmp(argv[1], "tracetarget") == 0) {
      logtarget = (Trace*) TclObject::lookup(argv[2]);
      if(logtarget == 0)
	return TCL_ERROR;
      return TCL_OK;
    }
    else if(strcmp(argv[1], "if-queue") == 0) {
      ifqueue = (PriQueue*) TclObject::lookup(argv[2]);
      if(ifqueue == 0)
	return TCL_ERROR;
      return TCL_OK;
    }
    else if(strcmp(argv[1], "target") == 0) {
      Tcl& tcl = Tcl::instance();
      tcl.evalf("%s set node_", this->name());
      tcl.evalf("%s set ifq_(0)",tcl.result());
      ifqueue = (PriQueue*) TclObject::lookup(tcl.result());
      return Agent::command(argc, argv); 
    }
  }
  return Agent::command(argc, argv); 
}

/* 
   Constructor
*/

ProtolibManetKernel::ProtolibManetKernel(nsaddr_t id) : Agent(PT_ProtolibManetKernel) {
  srand(10);
  index = id;
  logtarget = 0;
}

void
ProtolibManetKernel::recv(Packet *p, Handler*) {
  struct hdr_cmn *ch = HDR_CMN(p);
  struct hdr_ip *ih = HDR_IP(p);
  assert(initialized());
  //fprintf(stdout,"Pmanet %d is recieving a packet source %d, destination %d, num forwards %d, ttl %d\n",here_.addr_,ih->saddr(),ih->daddr(),ch->num_forwards(),ih->ttl_);
  if(ch->ptype() == PT_ProtolibManetKernel) {
    ih->ttl_ -= 1;
    manetpointer->recv(p,NULL);
    Packet::free(p);
    return;
  }
  if((ih->saddr() == index) && (ch->num_forwards() == 0)) {
    ch->size() += IP_HDR_LEN;
    ih->ttl_ = 255;
    if(ih->dport()==698){
      ch->ptype() = PT_ProtolibManetKernel;
      ih->daddr() = IP_BROADCAST;
      //fprintf(stdout,"setting type to pt_pmanet %d==%d?\n",ch->ptype(),PT_ProtolibManetKernel);
    }
    if(ih->daddr()==IP_BROADCAST){ // if its broadcast message it need more configuration
      ih->ttl_ = 1; 
    }
  } else if(ih->saddr() == index) {
    drop(p, DROP_RTR_ROUTE_LOOP);
    return;
  } else {
    if(--ih->ttl_ == 0) {
      drop(p, DROP_RTR_TTL);
      return;
    }
  }
  if(ih->daddr()==IP_BROADCAST){
    //fprintf(stdout,"at time %f %d is sending bcast packet to %d %d==%d? on port %d invalid %d\n",Scheduler::instance().clock(),here_.addr_,ih->daddr(),PT_ProtolibManetKernel,ch->ptype(),ih->dport(),INVALID);
    if(ih->dport()!=-1){
      forward(p,(nsaddr_t)ih->daddr());//send packet to bcast address
    } else {
      drop(p, DROP_RTR_NO_ROUTE);
    }
  } else {
    //fprintf(stdout,"%d is sending packet up to manetpointer\n",here_.addr_);
    manetpointer->recv(p,NULL); //manet will figure out where packet goes and then call our forward method    
  }
  return;
}
// void
// ProtolibManetKernel::recv(Packet *p, Handler*) {
//   struct hdr_cmn *ch = HDR_CMN(p);
//   struct hdr_ip *ih = HDR_IP(p); 
//   assert(initialized());
//   if(ch->ptype() == PT_ProtolibManetKernel) {     //send to protolib protocol recv function
//     ih->ttl_ -= 1;
//     manetpointer->recv(p,NULL);
//     Packet::free(p);
//     return; 
//   }
  
//   if((ih->saddr() == index) && (ch->num_forwards() == 0)) { //packet I am originating
//     ch->size() += IP_HDR_LEN;
//     ih->ttl_ = 255; // might have to have ttl function like the socket on in protolib
//     if(ih->daddr()==IP_BROADCAST){ // if its broadcast send message on its way
//       if(ih->dport()==698){ //set the type as router if its on port 698 (change 698 to binded port later)
//       	ch->ptype() = PT_ProtolibManetKernel;
//       }
//       ih->ttl_ = 1; 
//       forward(p,(nsaddr_t)ih->daddr());//send packet to bcast address
//       return;
//     } else { //not a broadcast packet find route and send unicast packet
//       manetpointer->recv(p,NULL); //manet will figure out where packet goes and call our forward method
//       return;
//     }
//   }
//   else if(ih->saddr() == index) { //getting a packet that I sent
//     drop(p, DROP_RTR_ROUTE_LOOP);
//     return;
//   }
//   else { //packet that I need to forward
//     if(--ih->ttl_ == 0) { //make sure ttl is still valid broadcast messages should be dropped here
//       drop(p, DROP_RTR_TTL);
//       return;
//     } 
//     // find destination and/or forward packet
//     manetpointer->recv(p,NULL); //manet will figure out where packet goes and then call our forward method
//     return;
//   }
//   drop(p,DROP_RTR_NO_ROUTE); // this line took me a week to find arggg!
//   return;
// }

// will get called when link layer detects error
void ProtolibManetKernel::rt_failed(Packet *p) {
  drop(p,DROP_RTR_NO_ROUTE); // this line took me a week to find arggg!
}

void ProtolibManetKernel::forward(Packet *p, nsaddr_t nexthop) {
    struct hdr_cmn *ch = HDR_CMN(p);
    struct hdr_ip *ih = HDR_IP(p);

    /* check ttl */
    if (ih->ttl_ == 0){
      drop(p, DROP_RTR_TTL);
	return;
    }
    //    ch->num_forwards_++;
    ch->prev_hop_ = here_.addr_;
    ch->next_hop_ = nexthop;
    ch->addr_type() = NS_AF_INET;
    ch->direction() = hdr_cmn::DOWN;
    //fprintf(stdout,"at time %f %d is attempting to foward packet to %d\n",Scheduler::instance().clock(),here_.addr_,nexthop);
    // Send the packet
    Scheduler::instance().schedule(target_, p, 0.);
}





 

