/*
 * =====================================================================================
 *
 *       Filename:  rt.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  10/03/24 12:55:35 PM IST
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  YOUR NAME (), 
 *   Organization:  
 *
 * =====================================================================================
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <assert.h>
#include "rt.h"

void
rt_init_rt_table(rt_table_t *rt_table){

    rt_table->head = NULL;
}

rt_entry_t *
rt_add_or_update_rt_entry(rt_table_t *rt_table,
                    char *dest, 
                    char mask, 
                    char *gw_ip, 
                    char *oif){

	bool new_entry;
    rt_entry_t *head = NULL;
    rt_entry_t *rt_entry = NULL;	

	new_entry = false;

	rt_entry = rt_look_up_rt_entry(rt_table, dest, mask);

	if(!rt_entry) {
    	
		rt_entry = calloc(1, sizeof(rt_entry_t));
    	
		strncpy(rt_entry->rt_entry_keys.dest, dest,
			sizeof(rt_entry->rt_entry_keys.dest));
    	rt_entry->rt_entry_keys.mask = mask;
		
		rt_entry->nfc = nfc_create_new_notif_chain(0);
		new_entry = true;
	}

    if(gw_ip)
        strncpy(rt_entry->gw_ip, gw_ip, sizeof(rt_entry->gw_ip));
    if(oif)
        strncpy(rt_entry->oif, oif, sizeof(rt_entry->oif));

	if (new_entry) {
		head = rt_table->head;
		rt_table->head = rt_entry;
		rt_entry->prev = 0;
		rt_entry->next = head;
		if(head)
			head->prev = rt_entry;
	}

	if (gw_ip || oif) {

		/* Entry is being updated by the publisher, send
 		 * notification to all subscribers*/
		nfc_invoke_notif_chain(rt_entry->nfc,
				(char *)rt_entry, sizeof(rt_entry_t),
				0, 0, NFC_MOD);
	}

    return rt_entry;
}

bool
rt_delete_rt_entry(rt_table_t *rt_table,
    char *dest, char mask){

    rt_entry_t *rt_entry = NULL;

    ITERTAE_RT_TABLE_BEGIN(rt_table, rt_entry){
    
        if(strncmp(rt_entry->rt_entry_keys.dest, 
            dest, sizeof(rt_entry->rt_entry_keys.dest)) == 0 &&
            rt_entry->rt_entry_keys.mask == mask){

            rt_entry_remove(rt_table, rt_entry);

			nfc_invoke_notif_chain(rt_entry->nfc,
					(char *)rt_entry, sizeof(rt_entry_t),
					0, 0, NFC_DEL);
			nfc_delete_all_nfce(rt_entry->nfc);
			free(rt_entry->nfc);
			rt_entry->nfc = NULL;
			free(rt_entry->nfc);

            free(rt_entry);
            return true;
        }
    } ITERTAE_RT_TABLE_END(rt_table, curr);

    return false;
}

void
rt_clear_rt_table(rt_table_t *rt_table){


}

void
rt_free_rt_table(rt_table_t *rt_table){


}

void
rt_dump_rt_table(rt_table_t *rt_table){

    rt_entry_t *rt_entry = NULL;

    ITERTAE_RT_TABLE_BEGIN(rt_table, rt_entry){

        printf("%-20s %-4d %-20s %s\n",
            rt_entry->rt_entry_keys.dest, 
            rt_entry->rt_entry_keys.mask, 
            rt_entry->gw_ip,
            rt_entry->oif);
		printf("\tPrinting Subscribers : ");
		
		glthread_t *curr;
		notif_chain_elem_t *nfce;

		ITERATE_GLTHREAD_BEGIN(&rt_entry->nfc->notif_chain_head, curr){

			nfce = glthread_glue_to_notif_chain_elem(curr);
			
			printf("%u ", nfce->subs_id);

		} ITERATE_GLTHREAD_END(&rt_entry->nfc->notif_chain_head, curr)
		printf("\n");
    } ITERTAE_RT_TABLE_END(rt_table, rt_entry);
}

rt_entry_t *
rt_look_up_rt_entry(rt_table_t *rt_table,
					char *dest, char mask) {

	rt_entry_t *rt_entry = NULL;
	
	ITERTAE_RT_TABLE_BEGIN(rt_table, rt_entry) {
		if ((strncmp(rt_entry->rt_entry_keys.dest,
					dest, sizeof(rt_entry->rt_entry_keys.dest)) == 0) &&
			rt_entry->rt_entry_keys.mask == mask) {

			return rt_entry;
		}
	} ITERTAE_RT_TABLE_END(rt_table, rt_entry);
	return NULL;
}

void
rt_table_register_for_notification(
		rt_table_t *rt_table,
		rt_entry_keys_t *key,
		size_t key_size,
		nfc_app_cb app_cb,
		uint32_t subs_id) {

	bool new_entry_created;
	rt_entry_t *rt_entry;
	notif_chain_elem_t nfce;

	new_entry_created = false;

	rt_entry = rt_look_up_rt_entry(rt_table, key->dest, key->mask);

	if (!rt_entry) {
		/* rt_entry was not existing before, but we are
 		 * creating it because subscriber is interested in notif
 		 * for this entry. Create such an entry without data. Later
 		 * When publisher would actually cate this entry, all registered
 		 * subscriber should be notified
 		 * */	
		rt_entry = rt_add_or_update_rt_entry(
					rt_table, key->dest, key->mask, 0, 0);
		new_entry_created = true;
	}
	
	memset(&nfce, 0, sizeof(notif_chain_elem_t));

	assert(key_size <= MAX_NOTIF_KEY_SIZE);

	/* No need to keep keys as nfce;s are tied to
	 * routing table entry which contain keys*/	
	//memcpy(nfce.key, (char *)key, key_size);
	//nfce.key_size = key_size;
	nfce.app_cb = app_cb;
	nfce.subs_id = subs_id;
	nfc_register_notif_chain(rt_entry->nfc, &nfce);

	/* Subscriber subscribes to already existing rt entry,
 	 * immediately send notif to Subscriber with opcode
 	 * NFC_ADD*/
	if (!new_entry_created) {
		app_cb(rt_entry, sizeof(rt_entry_t), NFC_ADD, subs_id);
	}
}
