#define    NEGATIVE_CACHE_TIME_OUT 180;
#define    GRAFT_TIME_OUT 3;

List         *\pim_dm_interface_table_ptr;
List         *\Mroute_table_ptr;
boolean       \leaf_router;



pim_pkt_ici_ptr = op_intrpt_ici ();
pim_pkt_ptr = op_pk_get (op_intrpt_strm ());

op_ici_attr_get (pim_pkt_ici_ptr, "interface_received", &pim_pkt_intf_addr);
op_ici_attr_get (pim_pkt_ici_ptr, "src_addr", &pim_pkt_src_addr);
/*op_ici_attr_get (pim_pkt_ici_ptr, "dest_addr", &pim_pkt_dest_addr);*/

Mroute_table_ptr = op_prg_list_create ();

pim_root_pkt_dispatch (pim_pkt_ptr, pim_pkt_intf_addr);



void pim_root_pkt_dispatch (Packet *pkt_ptr, IpT_Address intf_addr)
{


pim_dm_intface_table_init ()
{
  int                 num_intf, intf_index;
  IpT_Info*           ip_info_ptr = OPC_NIL;
  IpT_Interface_Info* ip_interface_ptr;
  OmsT_Pr_Handle      ip_proc_record_handle;
  List*               pim_dm_intf_table_ptr;
  PimDM_Interface*    intf_table_entry_ptr;

  /* Get ip handle here. copy from ospf code */

  num_intf = op_prg_list_size (ip_info_ptr->ip_iface_table_ptr);

  /* Create pim_dm interface table */
  pim_dm_interface_table_ptr = op_prg_list_create ();

  for (intf_index = 0; intf_index < num_intf; intf_index++) {
     ip_interface_ptr = (IpT_Interface_Info *) op_prg_list_access 
          (ip_info_ptr->ip_iface_table_ptr, intf_index);
     intf_table_entry_ptr = op_prg_mem_alloc (sizeof (PimDM_Interface));
     intf_table_entry_ptr->intf_ptr = ip_interface_ptr; 
     intf_table_entry_ptr->pim_dm_neighbor_list = op_prg_list_create ();
     op_prg_list_insert (pim_dm_intf_table_ptr, intf_table_entry_ptr, 
             OPC_LISTPOS_TAIL);
  } 
}


PimDM_Mroute_Entry *
pim_dm_Mroute_entry_create (IpT_address src_addr, IpT_address group_addr)
{
  
   PimDM_Mroute_Entry *Mroute_entry;
   SGPair             *S_G;
   boolean             oif_empty = TRUE;
   PimDM_Interface*    intf_table_entry_ptr;
   int                 pim_dim_intf_table_size;
   int                 intf_index;
   List               *oif;
   OIF_Intface        *oif_intf;

   Mroute_entry = (PimDM_Mroute_Entry *) op_prg_mem_alloc 
         (sizeof(PimDM_Mroute_Entry));
   S_G = (SGPair *) op_prg_mem_alloc (sizeof (SGPair));    
   S_G -> source = src_addr;
   s_G -> group = group_addr;
   Mroute_entry->SrcGrp = S_G;

   /* need to get RPF neighbor */
   Mroute_entry->RPF_neighbor = RPF_nbr;
   Mroute_entry->iif = RPF_nbr; 
   
   oif = op_prg_list_create ();
   if (leaf_router == FALSE) { /* start to construct oifs*/ 
      pim_dim_intf_table_size = op_prg_list_size (pim_dm_interface_table_ptr);
      for (intf_index =0; intf_index < pim_dim_intf_table_size; intf_index++) {
         intf_table_entry_ptr = (PimDM_Interface *) op_prg_list_access 
            (pim_dm_interface_table_ptr, intf_index);
         if (intf_table_entry_ptr->pim_dm_neighbor_list != OPC_NIL) {
            oif_intf = (OIF_Intface *) op_prg_mem_alloc (sizeof (OIF_Intface));
            oif_intf->forward_staus = Forward;
            oif_intf->interface_addr = intf_table_entry_ptr->interface_addr; 
            op_prg_list_insert (oif, oif_intf, OPC_LISTPOS_TAIL);            
         }
         
      }
   }
   else {
      pim_dim_intf_table_size = op_prg_list_size (pim_dm_interface_table_ptr);
      for (intf_index =0; intf_index < pim_dim_intf_table_size; 
            intf_index++) {
         intf_table_entry_ptr = (PimDM_Interface *) op_prg_list_access 
            (pim_dm_interface_table_ptr, intf_index);
         if (intf_table_entry_ptr->pim_dm_neighbor_list != OPC_NIL) {
            oif_intf = (OIF_Intface *) op_prg_mem_alloc (sizeof 
                    (OIF_Intface));
            if (Mlocal_find_G (IpT_Address group) == TRUE)) {
               oif_intf->forward_staus = Forward;         
               oif_empty = FALSE;
	    }
	    else {
               oif_intf->forward_staus = Prune;
               oif_intf->rebroadcast_time = op_intrpt_schedule_call 
                  (op_sim_time () + RebroadcastInterval, 0, 
                  pim_interface_rebroadcast, oif_intf); 
            }     
            op_prg_list_insert (oif, oif_intf, OPC_LISTPOS_TAIL);
          }
      }
   }            
   Mroute_entry->oif = oif;

   /* negative cache entry, need to send prune upstream       */
   /* need to find how to detect time out belong to this entry*/
   if (oif_empty == TRUE) {
      pim_dm_prune_send (RPF_nbr);
      Mroute_entry->num_forward_intf = 0;
      Mroute_entry->negative_timer_handle = op_intrpt_schedule_self 
         (op_sim_time () + NEGATIVE_CACHE_TIME_OUT, 0, negative_cache_entry_delete, S_G);
              
   }
}
      
  
  

pim_hello_message_rcvd (Packet *message_ptr, IpT_Address msg_intf_addr, 
     IpT_Address Neighbor_address)
{
   PimDM_Interface* interface_ptr;

   /* Get the router ID of the neighbor. Do we need this? */

   op_pk_nfd_get (message_ptr, "router ID", &msg_router_id);
   interface_ptr = pim_interface_find (msg_intf_addr);
   pim_neighbor_update (interface_ptr, neighbor_address, message_ptr);

}



pim_graft_message_rcvd (Packet *message_ptr, IpT_Address msg_intf_addr)
{ 
   IpT_Address         group_addr;
   IpT_Address         source_addr;
   int                 Mroute_size;
   int                 Mroute_index; 
   PimDM_Mroute_Entry *Mroute_entry;
    
   group_addr = op_pk_nfd_get (message_ptr, "group_addr", &group_addr);
   source_addr = op_pk_nfd_get (message_ptr, "source_addr", &source_addr);
   Mroute_size = op_prg_list_size (Mroute_table_ptr);      
   for (Mroute_index = 0; Mroute_intex < Mroute_size; Mroute_index++) {
      Mroute_entry = (PimDM_Mroute_Entry *) op_prg_list_access 
             (Mroute_table_ptr,  Mroute_index)
      if (Mroute_entry->SrcGrp->group == group_addr) {
         pim_oif_update (Mroute_entry, msg_intf_addr, Forward);             
         if (!Mroute_entry->num_forward_intf) {              
            pim_neighbor_message_send (Mroute_entry->iif, Graft);
            Mroute_entry->num_forward_intf ++;
         }
      }
   }
  
   pim_neighbor_message_send (source_addr, Graft_Ack);
}



pim_prune_message_rcvd (Packet *message_ptr, IpT_Address msg_intf_addr, 
     IpT_Address Neighbor_address)
{

   IpT_Address         group_addr;
   IpT_Address         source_addr;
   PimDM_Interface*    intf_table_entry_ptr;
   int                 Mroute_size;
   int                 Mroute_index; 
   PimDM_Mroute_Entry *Mroute_entry;
   OIF_Interface      *oif_intf; 
   int                 num_pruned_sources;
   int                 src_index
   char               *src_name[100];
   SGPair             *S_G;

   num_pruned_sources = op_pk_nfd_get (message_ptr, "num_pruned_sources", &num_pruned_sources);
   group_addr = op_pk_nfd_get (message_ptr, "group_addr", &group_addr);
   s_G->group = group_addr;
   intf_table_entry_ptr =  pim_interface_find (msg_intf_addr);
   if (interface_ptr->interface_type != OspfC_Interface_Type_Broadcast) {
      for (src_index = 0; src_index < num_pruned_sources; src_index) {
         sprintf (src_name, "source_addr%d\n",src_index);
         source_addr = op_pk_nfd_get (message_ptr, src_name, &source_addr);
         S_G->source = source_addr;
         if (Mroute_index = Mroute_look_up (S_G)) {
            Mroute_entry = (PimDM_Mroute_Entry *) op_prg_list_access 
               (Mroute_table_ptr,  Mroute_index); 
	    if ((oif_intf = pim_oif_interface_find (Mroute_entry, msg_intf_addr)) != OPC_NIL && (oif_intf->forward_status == Forward)) {
               oif_intf->forward_status = Prune;
               Mroute_entry->num_forward_intf --;
               if (Mroute_entry->num_forward_intf == 0) 
                 Mroute_entry->negative_timer_handle = op_intrpt_schedule_self 
                    (op_sim_time () + NEGATIVE_CACHE_TIME_OUT, 0, 
                    negative_cache_entry_delete, Mroute_entry->SrcGrp); 
      } 
   }
   else { /*need to complete this part of code. How to decide source to prune*/
      for (src_index = 0; src_index < num_pruned_sources; src_index) {
         sprintf (src_name, "source_addr%d\n",src_index);
         source_addr = op_pk_nfd_get (message_ptr, src_name, &source_addr);
         S_G->source = source_addr;
         if (Mroute_index = Mroute_look_up (S_G)) {
            Mroute_entry = (PimDM_Mroute_Entry *) op_prg_list_access 
               (Mroute_table_ptr,  Mroute_index); 
	    if ((oif_intf = pim_oif_interface_find (Mroute_entry, msg_intf_addr)) != OPC_NIL && (oif_intf->forward_status == Forward)) {
               oif_intf->forward_status = Prune;
               Mroute_entry->num_forward_intf --;
               if (Mroute_entry->num_forward_intf == 0) 
                 Mroute_entry->negative_timer_handle = op_intrpt_schedule_self 
                    (op_sim_time () + NEGATIVE_CACHE_TIME_OUT, 0, 
                    negative_cache_entry_delete, Mroute_entry->SrcGrp); 
      } 
   }

}
      
   
        



void
pim_neighbor_message_send (IpT_Address dest_addr,  PimDM_Message_Type msg_type)
{  
   PimDM_Neighbor *neighbor_ptr;
   Packet         *message_ptr;
   static Ici     *ip_encap_ici_ptr = OPC_NIL;
   
  
  
   /* Create the IP encap ICI to be associated with this packet. */
   if (ip_encap_ici_ptr == OPC_NIL)
     ip_encap_ici_ptr = op_ici_create ("ip3_encap_req");

   /* Set the destination. */
   op_ici_attr_set (ip_encap_ici_ptr, "dest_addr", dest_addr);
  
   /* Install the ICI. */
   op_ici_install (ip_encap_ici_ptr);      
 
   switch (msg_type)
     {
     case Hello:
             
       
     case Prune:

     case Graft:

       if ((neighbor_ptr = pim_neighbor_find (dest_addr)) != OPC_NIL) {
          message_ptr = op_pk_create_fmt ("pim_Graft");
          neighbor_ptr->last_message_ptr = op_pk_copy (message_ptr);           
          neighbor_ptr->rxmt_timer = op_intrpt_schedule_call (op_sim_time () + Graft_Time_out, Graft, pim_neighbor_message_rxmt, neighbor_ptr);
            
       }
       break;

     case Graft_Ack:

       if ((neighbor_ptr = pim_neighbor_find (dest_addr)) != OPC_NIL) 
          message_ptr = op_pk_create_fmt ("pim_Graft_Ack");
       break;  
         
     }

   /* Send the message. */
   op_pk_send_forced (message_ptr, router_ptr->ip_outstrm);   
}



imDM_Neighbor *
pim_neighbor_create (IpT_Address neighbor_addr, Packet *message_ptr) 
{
   PimDM_Neighbor *neighbor_ptr;

   neighbor_ptr = (PimDM_Neighbor *) op_prg_mem_alloc (sizeof(PimDM_Neighbor));
   neighbor_ptr -> neighbor_address = neighbor_addr;
   op_pk_nfd_get (message_ptr, "Hello_hold_time", &(neighbor_ptr ->
       neighbor_timer)); 
   return neighbor_ptr;
};


void
negative_cache_entry_delete (SGPair *S_G, int intrpt_code)
{ 
   int entry_index;

   if (entry_index = Mroute_look_up (SGPair *S_G)) 
      op_prg_list_remove (Mroute_table_ptr, entry_index);
}



void 
pim_interface_rebroadcast(OIF_Interface *oif_intf, int intrpt_code)
{

   oif_intf->forward_status = Forward;

}  


PimDM_Interface*
pim_interface_find (IpT_Address in_addr)
{
  int              num_pim_dm_interface_table;
  int              intf_index;
  PimDM_Interface* intf_table_entry_ptr;

  num_pim_dm_interface_table = op_prg_list_size (pim_dm_interface_table_ptr);

  for (intf_index = 0; intf_index < num_pim_dm_interface_table; intf_index++) {
      intf_table_entry_ptr = (PimDM_Interface *) op_prg_list_access 
         (pim_dm_interface_table_ptr, intf_index); 
      if (in_addr == intf_table_entry_ptr->intf_ptr->addr_range_ptr->address) 
         return intf_table_entry_ptr; 
  }
  return OPC_NIL;
}    
  

PimDM_Neighbor*
pim_neighbor_find (IpT_Address neighbor_addr)
{
  int              num_pim_dm_interface_table;
  int              intf_index;
  PimDM_Interface* intf_table_entry_ptr;
  int              num_neighbor_list;
  int              neighbor_index;
  PimDM_Neighbor  *neighbor_ptr


  num_pim_dm_interface_table = op_prg_list_size (pim_dm_interface_table_ptr);

  for (intf_index = 0; intf_index < num_pim_dm_interface_table; intf_index++) {
      intf_table_entry_ptr = (PimDM_Interface *) op_prg_list_access 
         (pim_dm_interface_table_ptr, intf_index); 
      if (intf_ptr -> pim_dm_neighbor_list != OPC_NIL) {
         num_neighbor_list = op_prg_list_size (intf_ptr -> pim_dm_nighbor_list);
         for (neighbor_index = 0; neighbor_index < num_neighbor_list; neighbor_index++) {
          neighbor_ptr = op_prg_list_access (intf_ptr, neighbor_index);
          if (neighbor_ptr->neighbor_address == neighbor_addr) 
             return (neighbor_ptr);
         } 
      }
  }

  return (OPC_NIL); 
 
}



void 
pim_neighbor_update (PimDM_Interface* intf_ptr,  IpT_Address neighbor_addr, 
 Packet* msg_ptr) 
{

   int             num_neighbor_list;
   int             neighbor_index;
   PimDM_Neighbor *neighbor_ptr

   if (intf_ptr -> pim_dm_neighbor_list == OPC_NIL) {
      neighbor_ptr = pim_dm_neighbor_create (neighbor_address, message_ptr);
      op_prg_list_insert (intf_ptr->pim_dm_neighbor_list, neighbor_ptr,
              OPC_LISTPOS_TAIL);
     
   }
   else {
      num_neighbor_list = op_prg_list_size (intf_ptr -> pim_dm_nighbor_list);
      for (neighbor_index = 0; neighbor_index < num_neighbor_list; neighbor_index++) {
          neighbor_ptr = op_prg_list_access (intf_ptr, neighbor_index);
          if (neighbor_ptr->neighbor_address == neighbor_addr) 
             op_pk_nfd_get (message_ptr, "Hello_Hold_Time", &neighbor->timer);   
          return;
      }
     
      neighbor_ptr = pim_dm_neighbor_create (neighbor_addr, msg_ptr);
      op_prg_list_insert (intf_ptr->pim_dm_neighbor_list, neighbor_ptr, OPC_LISTPOS_TAIL);
   }
}

   
void 
pim_oif_update (PimDM_Mroute_Entry *Mroute_entry, IpT_Address msg_intf_addr, OIF_Status status)
{
    int            oif_size;
    int            oif_index;
    OIF_interface *oif_intf;

    oif_size = op_prg_list_size (Mroute_entry->oif);
    for (oif_index = 0; oif_index < oif_size; oif_index++) {
      if (oif_intf->interface_addr == msg_intf_addr) {
           oif_intf->forward_status = Forward;
           return;
      }
    }
    
    /* a graft is received before hello */
    oif_intf = (OIF_Intface *) op_prg_mem_alloc (sizeof (OIF_Intface));
    oif_intf->forward_staus = Forward;
    oif_intf->interface_addr = msg_intf_addr;        
    op_prg_list_insert (oif, oif_intf, OPC_LISTPOS_TAIL);            
}
     



 
