#include "stdhead.h"
#include "lib.h"
#include "parse.h"

char *config_buff = 0, *curr_ptr = 0, *token = 0;

// Read the entire configuration file into config_buff
void read_config_file (char *file_name)
{
    int fd, n, size = 0;
    
    if ((fd = open (file_name, O_RDONLY)) == -1)
    {
	printf ("Error opening file %s.\n", file_name);
	exit (0);
    }
    
    config_buff = (char *) realloc (config_buff, 1);
    size = 1;

    while (read (fd, &config_buff[size - 1], 1) > 0)
    {
	size++;
	config_buff = (char *) realloc (config_buff, size);
    }
    config_buff[size - 1] = '\0';

    close (fd);
}
    
// skip all whitespace
void skip_whitespace (void)
{
    while ((*curr_ptr == ' ') || (*curr_ptr == '\t') || (*curr_ptr == '\r') || (*curr_ptr == '\n'))
	curr_ptr++;
}

// move across substring return success/failure
int move_across_substring (char *substring)
{
    curr_ptr = strstr (curr_ptr, substring);
    if (curr_ptr)
	curr_ptr += strlen (substring);
    else
	return 0;
    return 1;
}

// Move across the substring. If not found exit
int Move_across_substring (char *substring)
{
    curr_ptr = strstr (curr_ptr, substring);
    if (curr_ptr)
	curr_ptr += strlen (substring);
    else
    {
	printf ("Configuration file not correct.\n");
	exit (1);
    }
    return 1;
}

// Read the next token
void read_token (void)
{
    char *p;
    int n = 0;
    
    if (token)
	token = (char *) realloc (token, 0);
    
    while ((*curr_ptr != ' ') && (*curr_ptr != '\t') && (*curr_ptr != '\r') && (*curr_ptr != '\n'))
    {
	n++;
	token = (char *) realloc (token, n);
	p = token + (n - 1);
	*p = *curr_ptr;
	curr_ptr++;
    }

    n++;
    token = (char *) realloc (token, n);
    p = token + (n - 1);
    *p = '\0';
}

// Find if substring s1 occurs before s2
int find_if_s1_before_s2 (char *s1, char *s2)
{
    char *search_s1 = 0, *search_s2 = 0;
    search_s1 = strstr (curr_ptr, s1);
    search_s2 = strstr (curr_ptr, s2);

    if (!search_s1 && !search_s2)
	return 0;
    else if ((search_s2 && (search_s2 > search_s1)) || !search_s2)
    {
        curr_ptr = search_s1;
        curr_ptr += strlen (s1);
        return 1;
    }
    else
        return 0;
}

// Return an array of link list of possible range and user node
struct config_head ** parse_and_assign (char *file_name, int *subnet_count, int relay_agent_flag)
{
    extern struct DUID *server_duid;
    struct DUID1 *server_duid1;
    struct DUID2 *server_duid2;
    struct DUID3 *server_duid3;
    
    char *bkup_curr_ptr = 0, *start = 0;
    struct config_range *range_node, *prev_range_ptr = 0;
    struct config_user *user_node, *prev_user_ptr = 0;
    int node_count = 0, i, count = 0;
    struct config_head **head;
    u_int8_t temp[2];
    *(subnet_count) = 0;
        
    if (relay_agent_flag)	// Multiple subnets configuration
	return;
    
    // One server subnet only
    if (file_name)
	read_config_file (file_name);
    else
	read_config_file (DEFAULT_CONFIG_FILE);

    // assign current pointer to configuration buffer
    curr_ptr = config_buff;
    Move_across_substring ("duid_type");
    skip_whitespace();
    read_token();
    
    server_duid = (struct DUID *) malloc (sizeof (struct DUID));
    server_duid -> u_duid_type.duid_type = atoi (token);
    server_duid -> opt = 0;
    Move_across_substring ("{");
    
    switch (server_duid -> u_duid_type.duid_type)
    {
	case 1 :
	    start = curr_ptr;
    	    server_duid1 = (struct DUID1 *) malloc (sizeof (struct DUID1));
    	    server_duid -> duid_type = server_duid1;
	    
            Move_across_substring ("time");
	    skip_whitespace();
	    read_token();
    	    server_duid1 -> u_time.time = atoi (token);
	    
	    curr_ptr = start;
            Move_across_substring ("hardware_type");
	    skip_whitespace();
	    read_token();
	    server_duid1 -> u_haddr_type.haddr_type = atoi (token);
    
	    curr_ptr = start;
	    Move_across_substring ("hardware_len");
	    skip_whitespace();
	    read_token();
	    server_duid1 -> haddr_len = atoi (token);
	    
	    curr_ptr = start;
	    Move_across_substring ("hardware_addr");
	    skip_whitespace();
	    read_token();
	    server_duid1 -> link_layer_address = (u_int8_t *) malloc (sizeof (u_int8_t) * server_duid1 -> haddr_len);
	    for (i = 0; i < 3 * server_duid1 -> haddr_len - 1; i += 3)
	    {
		temp[0] = convert_character_to_hex (token[i]);
		temp[1] = convert_character_to_hex (token[i + 1]);
		server_duid1 -> link_layer_address[count++] = (temp[0] << 4) | temp[1];
	    }
	    break;
	    
	case 2 :
	    start = curr_ptr;
    	    server_duid2 = (struct DUID2 *) malloc (sizeof (struct DUID2));
    	    server_duid -> duid_type = server_duid2;
	    
	    curr_ptr = start;
	    Move_across_substring ("ident_len");
	    skip_whitespace();
	    read_token();
	    server_duid2 -> u_identifier_length.identifier_length = atoi (token);
	    
	    curr_ptr = start;
	    Move_across_substring ("identifier");
	    skip_whitespace();
	    read_token();
	    server_duid2 -> identifier = (u_int8_t *) malloc (sizeof (u_int8_t) * server_duid2 -> u_identifier_length.identifier_length);
	    for (i = 0; i < server_duid2 -> u_identifier_length.identifier_length; i++)
		server_duid2 -> identifier[i] = token[i];
		
	    curr_ptr = start;
	    Move_across_substring ("domain_name");
	    skip_whitespace();
	    read_token();
	    server_duid2 -> domain_name_len = sizeof (token);
	    server_duid2 -> domain_name = (char *) malloc (sizeof (char) * server_duid2 -> domain_name_len);
	    for (i = 0; i < server_duid2 -> domain_name_len; i++)
		server_duid2 -> domain_name[i] = token[i];
	    break;
	    
	case 3 :
	    start = curr_ptr;
    	    server_duid3 = (struct DUID3 *) malloc (sizeof (struct DUID3));
    	    server_duid -> duid_type = server_duid3;
	    
            Move_across_substring ("hardware_type");
	    skip_whitespace();
	    read_token();
	    server_duid3 -> u_haddr_type.haddr_type = atoi (token);
    
	    curr_ptr = start;
	    Move_across_substring ("hardware_len");
	    skip_whitespace();
	    read_token();
	    server_duid3 -> haddr_len = atoi (token);
	    
	    curr_ptr = start;
	    Move_across_substring ("hardware_addr");
	    skip_whitespace();
	    read_token();
	    server_duid3 -> link_layer_address = (u_int8_t *) malloc (sizeof (u_int8_t) * server_duid3 -> haddr_len);
	    for (i = 0; i < 3 * server_duid3 -> haddr_len - 1; i += 3)
	    {
		temp[0] = convert_character_to_hex (token[i]);
		temp[1] = convert_character_to_hex (token[i + 1]);
		server_duid3 -> link_layer_address[count++] = (temp[0] << 4) | temp[1];
	    }
	    break;
	    
	default :
	    printf ("Unrecognized DUID type\n");
	    exit (1);
    }

    Move_across_substring ("}");
    head = (struct config_head **) malloc (sizeof (struct config_head *));
    head[(*subnet_count)++] = (struct config_head *) malloc (sizeof (struct config_head));
    Move_across_substring ("subnet");
    
    Move_across_substring ("t1");
    skip_whitespace();
    read_token();
    head[*(subnet_count) - 1] -> t1 = atoi (token);
        
    Move_across_substring ("t2");
    skip_whitespace();
    read_token();
    head[*(subnet_count) - 1] -> t2 = atoi (token);
    
    // Build all user nodes
    bkup_curr_ptr = curr_ptr;
    while (find_if_s1_before_s2 ("user", "subnet"))//if user is before subnet.
    {
	user_node = (struct config_user *) malloc (sizeof (struct config_user));
	if (!node_count)
	{
	    head[*(subnet_count) - 1]->first_node_type = USER_NODE;
	    head[*(subnet_count) - 1]->next = user_node;
	}
	else
	{
    	    prev_user_ptr->next = user_node;
	    prev_user_ptr->next_node_type = USER_NODE;
	}
	node_count++;
	prev_user_ptr = user_node;
	
	move_across_substring ("{");
	start = curr_ptr;
	
	move_across_substring ("link_local_address");
	skip_whitespace();
	read_token();
	user_node -> link_local_addr = (struct in6_addr *) malloc (sizeof (struct in6_addr));
	inet_pton (AF_INET6, token, user_node -> link_local_addr);
		
	curr_ptr = start;
	move_across_substring ("IPv6_address");
	skip_whitespace();
	read_token();
	user_node -> ipv6_addr = (struct in6_addr *) malloc (sizeof (struct in6_addr));
	inet_pton (AF_INET6, token, user_node -> ipv6_addr);
	
	curr_ptr = start;
	move_across_substring ("preferred_lifetime");
	skip_whitespace();
	read_token();
	user_node -> pref_life = atoi (token);
	
	curr_ptr = start;
	move_across_substring ("valid_lifetime");
	skip_whitespace();
	read_token();
	user_node -> valid_life = atoi (token);
		
	curr_ptr = start;
	move_across_substring ("max_renew_count");
	skip_whitespace();
	read_token();
	user_node -> max_renew_count = atoi (token);
	
	curr_ptr = start;
	move_across_substring ("preference_val");
	skip_whitespace();
	read_token();
	user_node -> pref_val = atoi (token);
	
	move_across_substring ("}");
    }

    // Build all range nodes

    curr_ptr = bkup_curr_ptr;    
    while (find_if_s1_before_s2 ("range", "subnet"))
    {
	range_node = (struct config_range *) malloc (sizeof (struct config_range));
	if (!node_count)
	{
	    head[*(subnet_count) - 1]->first_node_type = RANGE_NODE;
	    head[*(subnet_count) - 1]->next = range_node;
	}
	else
	{
	    if (prev_user_ptr)
	    {
	        prev_user_ptr->next = range_node;
		prev_user_ptr->next_node_type = RANGE_NODE;
		prev_user_ptr = 0;
	    }
	    else
	    {
		prev_range_ptr->next = range_node;
		prev_range_ptr->next_node_type = RANGE_NODE;
	    }
	}
	node_count++;
	prev_range_ptr = range_node;
	
	skip_whitespace();
	read_token();
	range_node -> st_addr = (struct in6_addr *) malloc (sizeof (struct in6_addr));
	inet_pton (AF_INET6, token, range_node -> st_addr);
		
	skip_whitespace();
	read_token();
	range_node -> end_addr = (struct in6_addr *) malloc (sizeof (struct in6_addr));
	inet_pton (AF_INET6, token, range_node -> end_addr);
	
	move_across_substring ("{");
	start = curr_ptr;
	
	move_across_substring ("preferred_lifetime");
	skip_whitespace();
	read_token();
	range_node -> pref_life = atoi (token);
	
	curr_ptr = start;
	move_across_substring ("valid_lifetime");
	skip_whitespace();
	read_token();
	range_node -> valid_life = atoi (token);
		
	curr_ptr = start;
	move_across_substring ("max_renew_count");
	skip_whitespace();
	read_token();
	range_node -> max_renew_count = atoi (token);
	
	curr_ptr = start;
	move_across_substring ("preference_val");
	skip_whitespace();
	read_token();
	range_node -> pref_val = atoi (token);
	
	move_across_substring ("}");
    }
    
    Move_across_substring ("}");
    if (prev_user_ptr)
    {
	prev_user_ptr -> next_node_type = NO_NODE;
	prev_user_ptr -> next = 0;
    }
    else if (prev_range_ptr)
    {
	prev_range_ptr -> next_node_type = NO_NODE;
	prev_range_ptr -> next = 0;
    }
    else
    {
	head[*(subnet_count) - 1] -> first_node_type = NO_NODE;
	head[*(subnet_count) - 1] -> next = 0;
    }
    
    return head;
}

// Print the contents of the linked list.
void print_config_list_contents (struct config_head *head)
{
    struct config_range *range_node = 0;
    struct config_user *user_node = 0;
    char name[64];
    
    printf ("Subnet options\n");
    printf ("T1 = %d\n", head -> t1);
    printf ("T2 = %d\n", head -> t2);
    
    if (head -> first_node_type == RANGE_NODE)
	range_node = (struct config_range *) head -> next;
    else if (head -> first_node_type == USER_NODE)
	user_node = (struct config_user *) head -> next;
    else if (head -> first_node_type == NO_NODE)
	return;
	
    while (user_node)
    {
	printf ("\nLink local address of user = %s\n", inet_ntop (AF_INET6, (char *) user_node -> link_local_addr, name, 64));
	printf ("IPv6 address of user = %s\n", inet_ntop (AF_INET6, (char *) user_node -> ipv6_addr, name, 64));
	printf ("Preferred lifetime = %d\n", user_node -> pref_life);
	printf ("Valid lifetime = %d\n", user_node -> valid_life);
	printf ("Maximum renewal count = %d\n", user_node -> max_renew_count);	
	printf ("Preference value = %d\n", user_node -> pref_val);
	
	if (user_node -> next_node_type == RANGE_NODE)
	{
	    range_node = (struct config_range *) user_node -> next;
	    break;
	}
	else if (user_node -> next_node_type == USER_NODE)
	    user_node = (struct config_user *) user_node -> next;
	else if (user_node -> next_node_type == NO_NODE)
	    return;
    }

    while (range_node)
    {
	printf ("\nStarting address of range = %s\n", inet_ntop (AF_INET6, (char *) range_node -> st_addr, name, 64));
	printf ("Ending address of range = %s\n", inet_ntop (AF_INET6, (char *) range_node -> end_addr, name, 64));
	printf ("Preferred lifetime = %d\n", range_node -> pref_life);
	printf ("Valid lifetime = %d\n", range_node -> valid_life);
	printf ("Maximum renewal count = %d\n", range_node -> max_renew_count);	
	printf ("Preference value = %d\n", range_node -> pref_val);
	
	if (range_node -> next_node_type == RANGE_NODE)
	    range_node = (struct config_range *) range_node -> next;
	else if (range_node -> next_node_type == NO_NODE)
	    return;
    }
}

void print_server_duid (void)
{
    extern struct DUID *server_duid;
    struct DUID1 *d1;
    struct DUID2 *d2;
    struct DUID3 *d3;
    int i;
    
    printf ("\nServer DUID\n");
    printf ("DUID type = %d\n", server_duid -> u_duid_type.duid_type);
    
    switch (server_duid -> u_duid_type.duid_type)
    {
	case 1 :
	    d1 = server_duid -> duid_type;
            printf ("Time = %d\n", d1 -> u_time.time);
            printf ("Hardware type = %d\nHardware address = ", d1 -> u_haddr_type.haddr_type);
            for (i = 0; i < 6; i++)
            {
                printf ("%x", d1 -> link_layer_address[i]);
                if (i != 5)
                    printf (":");
            }
            break;
	        
	case 2 :
            d2 = server_duid -> duid_type;
            printf ("Identifier length = %d\nIdentifier = ", d2 -> u_identifier_length.identifier_length);
            for (i = 0; i < d2 -> u_identifier_length.identifier_length; i++)
                printf ("%d", d2 -> identifier[i]);
            printf ("\nDomain name = ");
            for (i = 0; i < d2 -> domain_name_len; i++)
                printf ("%c", d2 -> domain_name[i]);
            break;

        case 3 :
            d3 = server_duid -> duid_type;
            printf ("Hardware type = %d\nHardware address = ", d3 -> u_haddr_type.haddr_type);
            for (i = 0; i < 6; i++)
            {
                printf ("%x", d3 -> link_layer_address[i]);
                if (i != 5)
                    printf (":");
            }
	    break;
    }
    
    printf ("\n\n");
}
