#include "stdhead.h"
#include "request.h"
#include "clilib.h"

void fix_ia_option_length(struct DHCP_MESSAGE *adv, struct DHCP_MESSAGE *dhcp_message_ptr);

// This function creates a REQUEST message from the ADCVERTISE message and 
// returns a pointer to the REQUEST message
struct DHCP_MESSAGE * create_request_message (struct DHCP_MESSAGE *advertise_message, char *interface_name)
{
    // Allocate memory fot the REPLY message
    struct DHCP_MESSAGE *dhcp_message_ptr = (struct DHCP_MESSAGE *) malloc (sizeof (struct DHCP_MESSAGE));
    struct OPTIONS *options_ptr;
    struct DUID * duid_ptr;
    struct IA * ia_ptr;
    struct IA_ADDRESS * iaaddr_ptr;
    
    // Assign the message type as REQUEST
    dhcp_message_ptr -> u_msg_type.msg_type = REQUEST;
    // Generate the Transaction Id
    generate_trans_id (&dhcp_message_ptr -> u_trans_id.trans_id);
    BLOG("BLOG: TRANS_ID:  %x\n",dhcp_message_ptr -> u_trans_id.trans_id);
    // Copy and paste the Client Id option from the ADVERTISE message into 
    // the REQUEST message
    dhcp_message_ptr -> opt = copy_message_option (advertise_message, OPTION_CLIENTID);
    // Move onto to the next (option) next (duid) node
    duid_ptr = (struct DUID *) dhcp_message_ptr -> opt -> opt_data;
    // Copy and Paste the Server Id option from the ADVERTISE message into the
    // REQUEST message
    duid_ptr -> opt = copy_message_option (advertise_message, OPTION_SERVERID);
    // Move onto to the next (option) next (duid) node
    duid_ptr = (struct DUID *) duid_ptr -> opt -> opt_data;
    // Copy and Paste the IA option from the ADVERTISE message into the
    // REQUEST message
    duid_ptr -> opt = copy_message_option (advertise_message, OPTION_IA);
    // Move onto to the next (option) next (ia) node
    ia_ptr = (struct IA *) duid_ptr -> opt -> opt_data;
    // Copy and Paste the IA_ADDRESS option from the ADVERTISE message into the
    // REQUEST message
    ia_ptr -> opt = copy_message_option (advertise_message, OPTION_IAADDR);
    // Move onto to the next (option) next (ia_address) node
    iaaddr_ptr = (struct IA_ADDRESS *) ia_ptr -> opt -> opt_data;
    // Assign the next option pointer as zero
    iaaddr_ptr -> opt = 0;
    //fix OPTION_IA_length, add by bruce hsu
    fix_ia_option_length(advertise_message,dhcp_message_ptr);    
    return dhcp_message_ptr;
}    

//find option function
struct OPTIONS * find_option(struct DHCP_MESSAGE *dhcp_message_ptr, int type)
{
    struct OPTIONS *options_ptr;
    struct DUID * duid_ptr; 
    struct IA * ia_ptr;
    struct IA_ADDRESS * iaaddr_ptr;
    struct PREFERENCE * preference_ptr;
    struct STATUS_CODE * status_code_ptr;
    
    //find if state code existed
    options_ptr=dhcp_message_ptr->opt;
    while(NULL != options_ptr){
        if(type==options_ptr->u_opt_code.opt_code){  //bingo!!!
            BLOG("BLOG: find option %d", type);
            return options_ptr; 
        }
        switch(options_ptr->u_opt_code.opt_code)
	    {
		    case OPTION_CLIENTID :
		    case OPTION_SERVERID :
		        duid_ptr = options_ptr->opt_data;
		        options_ptr = duid_ptr->opt;
		        break;
		
		    case OPTION_IA :
		        ia_ptr = options_ptr ->opt_data;
		        options_ptr = ia_ptr->opt;
		        break;
		
		    case OPTION_IAADDR :
		        iaaddr_ptr = options_ptr->opt_data;
		        options_ptr =iaaddr_ptr->opt;
		        break;
		    
		    case OPTION_PREFERENCE :
		        preference_ptr = options_ptr ->opt_data;
		        options_ptr = preference_ptr->opt;
		        break;

            case OPTION_STATUS_CODE:
                status_code_ptr=options_ptr ->opt_data;
                options_ptr = status_code_ptr ->opt;
                break;
                
            default:
                break;
	    }
    }
    return NULL; //can not find option...
}


//because the advertise message may include state code message, but we do not send it back,
//that will cause IA option has wrong length, so we had to fix it...   bruce hsu
void fix_ia_option_length(struct DHCP_MESSAGE *adv, struct DHCP_MESSAGE *dhcp_message_ptr)
{
    struct OPTIONS *opt_st=NULL; 
    struct OPTIONS *opt_ia=NULL;
    
    opt_st=find_option(adv, OPTION_STATUS_CODE);
    opt_ia=find_option(dhcp_message_ptr, OPTION_IA);
    if((NULL!=opt_st) && (NULL !=opt_ia)){
        BLOG("BLOG: st_len: %d, ia_len: %d/n", opt_st->u_opt_len.opt_len, opt_ia->u_opt_len.opt_len);
        opt_ia->u_opt_len.opt_len=opt_ia->u_opt_len.opt_len-opt_st->u_opt_len.opt_len-4;  // 4 is length of header 
    }
    
}



