/************************************************************************/
/*	Routing Protocol Simulator	Relese 1.0	1994/3/17	*/
/*									*/
/*		module 	: routing table management     			*/
/*		file	: rt_cost.c			       		*/
/*									*/
/*   Copyright (c) 1994 by Systems Development Laboratory Hitachi,Ltd.	*/
/*   All rights reserved.						*/
/*----------------------------------------------------------------------*/
/*	UPDATE HISTORY							*/
/*									*/
/************************************************************************/
static char copyright[]=
  "@(#)Copyright (c) 1994 by Systems Development Laboratory Hitachi,Ltd.\n All rights researved.\n";

#include "rps.h"
#include "netmng.h"
#include "set.h"

#define MAX_COST	0xffffff

static struct SET min_rt_path;
static struct SET path;
static struct SET work;

extern struct RT_TBL *rt_search();
extern u_long get_link_cost();
extern struct SET_ITEM	*get_min_cost_item_SET();
extern struct SET_ITEM *get_SET_items();
extern struct SET_ITEM *get_specified_SET_item();
extern int get_SET_item_count();
extern struct NET_TBL *get_net_list();
extern char *inetaddr_to_char();
extern struct AREA_TBL *rt_area_search();

void get_next_node();
u_long get_next_hop();

/*

  get_min_path

*/
void get_min_path(node)
u_long node;
{
    struct SET_ITEM *k,*p;
    u_long k_node,k_prev;
    u_long k_cost;

    bzero(&path,sizeof(struct SET));
    bzero(&work,sizeof(struct SET));
    bzero(&min_rt_path,sizeof(struct SET));
    
    initialize_SET(&path);
    initialize_SET(&min_rt_path);

    register_SET_BGP(&min_rt_path,node,0,NIL);
    initialize_SET(&work);
    get_next_node(node,&work);
    for(p = get_SET_items(&work,NULL);p;p=get_SET_items(&work,p)) {
	register_SET_BGP(&path,p->node,p->cost,node);
    }

    
    while(1) {
	if(get_SET_item_count(&path) <= 0) {
	    break;
	}
	k = get_min_cost_item_SET(&path);
	
        if(!k) {
	    log_trace(stderr,"Can't find min_cost_item in get_min_path\n");
	    print_SET(&work);
	    print_SET(&path);
	}
	
	k_node = k->node;
	k_cost = k->cost;
	k_prev = k->prev;
	register_SET_BGP(&min_rt_path,k_node,k_cost,k_prev);
	del_SET(&path,k_node);
	initialize_SET(&work);
	get_next_node(k_node,&work);
	for(p = get_SET_items(&work,NULL);p;p=get_SET_items(&work,p)) {
	    if(get_specified_SET_item(&min_rt_path,p->node) == NULL) {
		register_SET_BGP(&path,p->node,k_cost+p->cost,k_node);
	    }
	}
    }
}

/*

  get_next_node

*/
void get_next_node(node,set)
u_long node;
struct SET *set;
{
    struct RT_TBL *rt,*cnct_rt;
    struct INDEX *index;
    struct NET_TBL *net;
    struct RT_INTF *cnet;
    long cost;

    if(!node || !set) {
	log_trace(stderr,"invalid argment < node = %d, set = %x > in get_next_node\n",node,set);
	return;
    }
      
    if((rt = rt_search(node)) == NULL) return;
    
    for(cnet=rt->ilist;cnet!=NULL;cnet=cnet->next) {
	net = cnet->net;
	for(index=net->cnct_rt;index!=NULL;index=index->next) {
	    cnct_rt = (struct RT_TBL *)index->dst;
	    if(node != cnct_rt->id) {
		if((cost = get_link_cost(node,cnct_rt->id)) != -1) {
		    register_SET_BGP(set,cnct_rt->id,cost,node);
		}
	    }
	}
    }
}

/*

  get_net_cost

*/
long get_net_cost(node,net)
u_long node;
struct NET_TBL *net;
{

    struct RT_TBL *cnct_rt;
    struct INDEX *index;
    struct SET_ITEM *set;
    u_long min_cost,cost;
    struct RT_INTF *cnet;
    
    if(!node || !net) {
	log_trace(stderr,"invalid argment < node = %d, net = %x > in get_next_node\n",node,net);
	return -1;
    }

    min_cost = MAX_COST;

    for(index=net->cnct_rt;index!=NULL;index=index->next) {
	cnct_rt = (struct RT_TBL *)index->dst;
	if((set = get_specified_SET_item(&min_rt_path,cnct_rt->id))) {
	    GET_LINK_NET(cnct_rt,cnet,net);
	    cost = cnet->cost;
	    if(min_cost > set->cost + cost) {
		min_cost = set->cost + cost;
	    }
	}
    }

    return min_cost;
}

/*

  get_rt_cost

*/
long get_rt_cost(node)
u_long node;
{
    struct SET_ITEM *set;
    
    if(!node) {
	log_trace(stderr,"invalid argment < node = %d > in get_next_node\n",node);
	return -1;
    }

    if((set = get_specified_SET_item(&min_rt_path,node))) {
	return (long)set->cost;
    }
    else {
	return -1;
    }

}

/*

  get_next_hop

*/
u_long get_next_hop(src,dst)
u_long src;
u_long dst;
{
    u_long node,next_hop;
    struct SET *set;
    
    if(!src || !dst) {
	log_trace(stderr,"invalid argment < src = %d, dst = %x > in get_next_node\n",src,dst);
	return -1;
    }

    next_hop = NIL;
    set = &min_rt_path;

    for(node=get_path_from_SET(set,dst);node!=NIL;node=get_path_from_SET(set,NIL)) {
	if(node == src) return next_hop;
	next_hop = node;
    }

    return src;
}

/*

  path_search_area

*/
int path_search_area(abr_idx)
struct INDEX *abr_idx;
{
    u_long node;
    struct SET_ITEM *set;
    struct RT_TBL *rt;
    struct INDEX *index;

    if(!abr_idx) {
	log_trace(stderr,"invalid argment < abr_idx = %x > in get_next_node\n",abr_idx);
	return 0;
    }

    for(set=get_SET_items(&min_rt_path,NULL);set;set=get_SET_items(&min_rt_path,set)) {
	for(index=abr_idx;index!=NULL;index=index->next) {
	    rt = (struct RT_TBL *)index->dst;
	    if(set->node == rt->id) return 1;
	}
    }

    return 0;
}


/*

  path_net_area

*/
u_long path_net_area(area,net)
struct AREA_TBL  *area;
struct NET_TBL *net;
{

    struct RT_TBL *cnct_rt,*abr;
    struct INDEX *index,*aindex;
    struct SET_ITEM *org;
    struct SET *set;
    u_long min_cost,cost;
    struct RT_INTF *cnet;
    u_long node,pre_node;
    u_long next_hop;
    
    if(!area || !net) {
	log_trace(stderr,"invalid argment < area = %dx net = %x > in get_next_node\n",area,net);
	return -1;
    }

    set = &min_rt_path;
    org=get_SET_items(set,NULL);
    for(index=net->cnct_rt;index!=NULL;index=index->next) {
	cnct_rt = (struct RT_TBL *)index->dst;
	if(!get_specified_SET_item(&min_rt_path,cnct_rt->id))
	  continue;

	IF_STATE_DOWN(cnct_rt,net);
	if(org->node == cnct_rt->id) {
	    next_hop = org->node;
	}
	else {
	    next_hop = get_next_hop(org->node,cnct_rt->id);
	}
	if(rt_area_search(next_hop,area->number))
	  return next_hop;
	else
	  return org->node;
    }

    return 0 ;
}

/*

  path_rt_area

*/
u_long path_rt_area(area,rt)
struct AREA_TBL  *area;
struct RT_TBL *rt;
{
    struct SET_ITEM *org;
    struct SET *set;
    u_long next_hop;

    set=&min_rt_path;
    if(!get_specified_SET_item(set,rt->id))
      return 0;
    
    org=get_SET_items(set,NULL);
    if(org->node == rt->id) {
	next_hop = org->node;
    }
    else {
	next_hop = get_next_hop(org->node,rt->id);
    }
    
    if(rt_area_search(next_hop,area->number))
      return next_hop;
    else
      return org->node;
    
}

/*

  path_rt_cost

*/
long path_rt_cost(term_node,dst_node)
u_long term_node;
u_long dst_node;
{
    struct SET *set;
    u_long node;
    
    set = &min_rt_path;

    for(node=get_path_from_SET(set,term_node);node!=NIL;node=get_path_from_SET(set,NIL)) {
	if(node == dst_node) return get_rt_cost(dst_node);
    }

    return -1;
}





















