#include <stdio.h>
#include <string.h>
#include <uni_errors.h>
#include <uni_enums.h>
#include <uni.h>
#include <uni_mesg.h>
#include <uni_debug.h>
#include <uni_message.h>
#include <uni_decode.h>

int aal_params_decoder();
int cell_rate_param_ident();
int cell_rate_param_check();
int traffic_desc_decoder();
int bb_bearer_capab_decoder();
int bb_high_layer_info_decoder();
int bb_low_layer_info_decoder();
int call_state_decoder();
int called_pty_number_decoder();
int called_pty_subaddr_decoder();
int calling_pty_number_decoder();
int calling_pty_subaddr_decoder();
int cause_decoder();
int connection_ident_decoder();
int qos_params_decoder();
int bb_repeat_indic_decoder();
int restart_indic_decoder();
int bb_send_complete_decoder();
int transit_net_select_decoder();
int endpoint_reference_decoder();
/*int endpoint_state_decoder(); */

#define DOG   decode_octet_group(buf+bi, elem->hdr.tbl_index, og_tbl_idx)

extern uni_message_handler message_handlers[];


/******************************************************************************
*  check_element_length
*    The length must either fall within a bound or meet an exact value.
******************************************************************************/
int check_element_length(int mtbl_idx, int elist_idx, int len)
{
    element_info *e= &(messages_tbl[mtbl_idx].elist[elist_idx]);
    /*
     * If min_len in the message tbl is zero, max_len is the
     * exact len that must be specified.
     */
    if (e->min_len == 0) {
        if ((len + UNI_ELEM_HDR_LEN) != e->max_len) {
            printf("\nuni: chk_elem_len - %s message, ",
		   messages_tbl[mtbl_idx].name);
            printf("elem code %02x - expected len %d, saw len of %d\n",
		   e->code, e->max_len, len); 
            return 0;
        } else {
	    return 1;
	}
    }
    
    /* Else, the element length falls includes a range. */
    if (((len + UNI_ELEM_HDR_LEN) < e->min_len ) || 
	((len + UNI_ELEM_HDR_LEN) > e->max_len)) {
        printf("\nuni: chk_elem_len - %s message, ",
	       messages_tbl[mtbl_idx].name);
        printf("elem code: %02x - expected len b/w %d-%d, saw len %d\n", 
	       e->code, e->min_len, e->max_len, len);
        return 0;
    } else {
        return 1;
    }
}
  
/******************************************************************************
*  grab_field_value_in_list
******************************************************************************/
u_int grab_field_value_in_list(list l, int pos)
{
    list m;
    field f;
    int j;
    
    mydolist(m, l, pos);
    f = (field) l->first;
    return  (f->val.ival);
}

/******************************************************************************
*  decode_mesg_hdr
******************************************************************************/
uni_message_header decode_mesg_hdr(u_char *buf)
{
    uni_message_header hdr = 
     (uni_message_header) allocate_memory(sizeof(struct uni_message_header));

    /*
     * check for correct protocol discriminator 
     */
    if ((hdr->proto_disc = (u_int) buf[0]) != uni) {
        /* ignore message */
        printf("uni: incorrect protocol discriminator\n");
        deallocate_memory(hdr); 
        return 0;
    } 
    
    /* pad field should be 0, reference value length should be <= 3 */
    if (((u_int) (buf[1] & 0xf0)) != 0) {
        /* ignore message */
        printf("uni: bad ref val len of %d\n", hdr->crv_length);
        deallocate_memory(hdr); 
        return 0;
    }
    if ((hdr->crv_length = ((u_int) buf[1]) & 0x0f) != 3) {
        /* ignore message */
        printf("uni: bad ref val len of %d\n", hdr->crv_length);
        deallocate_memory(hdr); 
        return 0;
    } 
    hdr->crv_flag = (((u_char) buf[2]) >> 7);
    hdr->crv = ((u_int) buf[2]) & 0x7f;
    hdr->crv = (hdr->crv << 8) | ((u_int) buf[3]);
    hdr->crv = (hdr->crv << 8) | ((u_int) buf[4]);
    printf("CRV: 0x%06x\n", hdr->crv);
    /*
     * check for a valid message type, this field starts in byte 5 of hdr
     * second byte of message ignored (always 0x80)
     */
    hdr->type = (u_int) buf[5]; 
    GET_TBL_IDX(hdr->idx, hdr->type, messages_tbl, MAX_MESSAGES);
    if (hdr->idx >= MAX_MESSAGES) {
        printf("uni: decode_hdr - unrecognized mesg type: 0x%02x\n",
	       hdr->type);
        deallocate_memory(hdr); 
        return 0;
    }
    /* skip byte # 6 */
    /*
     * decode message length
     */
    hdr->length = (u_int) buf[7];
    hdr->length = (hdr->length<<8) | ((u_int)buf[8]);
    hdr->retry_timer = 0;
    return hdr;
}

/******************************************************************************
 * aal_params_decoder
 *     Decode aal_params. Note: I don't handle user defined params the moment. 
 *****************************************************************************/
int aal_params_decoder(char *buf, element elem)
{
    register int i;
    register int bi = 4;
    register int len = elem->hdr.length;
    register int og_tbl_idx = 0;
    list l;
    u_int temp;
    octet_group og;
    octet_group_desc *ogptr;        /* ptr to og in element table */
    field f;

    debug("parse", "uni: aal_params_decoder");
    /*
     * Check AAL type. I'm only dealing with AAL 5 for the moment.
     */ 
    if (buf[bi] != aal_type_5) {
        printf("\nuni: aal_params_decoder - bad aal_type %02x\n", buf[bi]);
        return 0;
    } else {
        if ((og = DOG) != 0) { 
            elem->octet_groups = insert(og, elem->octet_groups);
            og_tbl_idx++; bi++; 
            len -= 1;
        } else
            return 0;
    }
    /*
     * Go through the rest of the octet groups.
     */
    while (len) {            
        /* params must be in order */
        for (i = og_tbl_idx; i < NUM_OGS_AAL_PARAMS; i++) {
            ogptr = &(the_elements_tbl[elem->hdr.tbl_index].ogs[i]);
             if ((u_char) buf[bi] == (u_char) ogptr->internal_fields[0][2])
                break;
        }
        if (i == NUM_OGS_AAL_PARAMS) {
            printf("\nuni: aal_params_decoder - bad identifier %02x\n",
                   buf[bi]);
            return 0;
        } else 
            og_tbl_idx = i;
        
        if ((og =  DOG) != 0) {
            elem->octet_groups = insert(og, elem->octet_groups);
        } else {
            return 0;
        }    
        len -= og->len;
        bi += og->len;
    }
    return 1;
}


/******************************************************************************
 *  cell_rate_param_ident
 *****************************************************************************/
int cell_rate_param_ident(u_int param, u_int *fwd_traff_param_combo,
u_int *bkw_traff_param_combo, int tagging)
{
    /*debug("parse","uni: cell_rate_param_check - param: %d\n", param));/**/
    if (tagging) {
        if(param && 0x02)
            *fwd_traff_param_combo |= TAGGING;
        if(param && 0x01)
            *bkw_traff_param_combo |= TAGGING;
    } else {
        switch(param) {
            case fwd_peak_cell_rate_0_ident:
                *fwd_traff_param_combo |= PCR_0; break;
            case bkw_peak_cell_rate_0_ident:
                *bkw_traff_param_combo |= PCR_0; break;
            case fwd_peak_cell_rate_1_ident:
                *fwd_traff_param_combo |= PCR_0_1; break;
            case bkw_peak_cell_rate_1_ident:
                *bkw_traff_param_combo |= PCR_0_1; break;
            case fwd_sust_cell_rate_0_ident:
                *fwd_traff_param_combo |= SCR_0; break;
            case bkw_sust_cell_rate_0_ident:
                *bkw_traff_param_combo |= SCR_0; break;
            case fwd_sust_cell_rate_1_ident:
                *fwd_traff_param_combo |= SCR_0_1; break;
            case bkw_sust_cell_rate_1_ident:
                *bkw_traff_param_combo |= SCR_0_1; break;
            case fwd_max_burst_size_0_ident:
                *fwd_traff_param_combo |= MBS_0; break;
            case bkw_max_burst_size_0_ident:
                *bkw_traff_param_combo |= MBS_0; break;
            case fwd_max_burst_size_1_ident:
                *fwd_traff_param_combo |= MBS_0_1; break;
            case bkw_max_burst_size_1_ident:
                *bkw_traff_param_combo |= MBS_0_1; break;
            case best_effort_ident:
                *fwd_traff_param_combo |= BEST_EFFORT;
                *bkw_traff_param_combo |= BEST_EFFORT; break;
            case traffic_mngt_options_ident:
                break;
            default:
                printf("uni: cell_rate_param_check - BAD!\n");
                break;
        }
    }
}

/******************************************************************************
 *  cell_rate_param_check
 *     not doing anything with these params for the moment so
 *     not acting on errors yet and thus always returning success (1)
 *****************************************************************************/
int cell_rate_param_check(int fwd, int bkw) 
{
    return 1; 
}


/******************************************************************************
 * traffic_desc_decoder
 *     Decode traffic_desc element. 
 *****************************************************************************/
int traffic_desc_decoder(char *buf, element elem)
{

    register int i;
    register int bi = 4;
    register int og_tbl_idx = 0;
    register int len = elem->hdr.length;
    u_int fwd_traff_param_combo = 0;
    u_int bkw_traff_param_combo = 0;
    u_int temp;
    octet_group og;
    octet_group_desc *ogptr;        /* ptr to og in element table */
 
    debug("parse", "uni: traffic_desc_decoder\n");
    /*--------------------------------------------------------------------
    * Go through the rest of the octet groups.
    *-------------------------------------------------------------------*/
    while ((len) && ((buf[bi] & 0xf0) != 0))    {            
        /* Compare buf with traffic_desc ogs and break when found */
        for (i = og_tbl_idx; i < NUM_OGS_TRAFFIC_DESC; i++) {
            ogptr = &(the_elements_tbl[elem->hdr.tbl_index].ogs[i]);
             if ((u_char)buf[bi]==(u_char) ogptr->internal_fields[0][2])
                break;
        }
        if (i == NUM_OGS_TRAFFIC_DESC) {
            printf("\nuni: traffic_desc_decoder - bad identifier %d\n",
                   buf[bi]);
            return 0;
        } else {   
            /* 
             * record type of identifier, to check combination later  */
            cell_rate_param_ident(((u_char)buf[bi]), 
                &fwd_traff_param_combo,&bkw_traff_param_combo,DONT_TAGG_CHK);
            og_tbl_idx = i;
        }
        
        if ((og = DOG) != 0) 
            elem->octet_groups = insert(og, elem->octet_groups);
        else {
	    printf("\nuni: traffic_desc_dcdr - problem parsing octet group\n");
            return 0;
	}
        len -= og->len;
        bi += og->len;
    }
    /*--------------------------------------------------------------------
    * final "tagging" octet included, note: assume rsvd bits are 0's
    *-------------------------------------------------------------------*/
    if (len) {
        og_tbl_idx = NUM_OGS_TRAFFIC_DESC - 1;
        if ((og = DOG) != 0) {
            /* record tagging ident, to check overall combination later */
            cell_rate_param_ident((u_char)buf[bi], 
                &fwd_traff_param_combo, &bkw_traff_param_combo,DO_TAGG_CHK);
            elem->octet_groups = insert(og, elem->octet_groups);
        } else {
	    printf("\nuni: traffic_desc_dcdr - tagging problem\n");
            return 0;
	}
    }
    if (cell_rate_param_check(bkw_traff_param_combo, bkw_traff_param_combo))
        return 1;
    else {
        printf("\nuni: traffic_desc_dcdr -invalid cell rate params combo\n");
        return 0;
    }
}

    
/******************************************************************************
 *   bb_bearer_capab_decoder
 *****************************************************************************/
int bb_bearer_capab_decoder(char *buf, element elem)
{
    register int bi = 4;
    register int og_tbl_idx = 0;
    octet_group og;
    u_int temp;
    
    debug("parse", "uni: bb_bearer_capab_decoder\n");

    /*--------------------------------------------------------------------
    * get first octet group and check appropriate fields 
    *-------------------------------------------------------------------*/
    if ((og = DOG) == 0) 
        return 0;
    og_tbl_idx++;
    temp = grab_field_value_in_list(og->fields, 1);
    if (temp == bcob_x) {
        if (elem->hdr.length != 3) {
             printf("uni: Error - bb_bearer_capab_decoder");
             printf(", BCOC-X class, bad elem len of %d\n",elem->hdr.length);
             return 0;
        }
        /* 
         * don't insert this og, get again but get full* og, i.e. with
         * traffic type and timing req's
         */
        if ((og = DOG) == 0) 
            return 0;
        og_tbl_idx++;
    }
    else if ((temp == bcob_a) && (temp == bcob_c)) {
        if (elem->hdr.length != 2) {
             printf("uni: Error - bb_bearer_capab_decoder");
             printf(" ,bad elem length of %d\n", elem->hdr.length); 
             return 0;
        }
    } else {
        printf("uni: Error - bb_bearer_capab_decode -illegal bearer class\n");
        return 0;
    }
    elem->octet_groups = insert(og, elem->octet_groups);
    bi += og->len;
    /*--------------------------------------------------------------------
    * get last octet group and check appropriate fields 
    *-------------------------------------------------------------------*/
    if ((og = DOG) == 0) 
        return 0;
    temp = grab_field_value_in_list(og->fields, 1);
    if ((temp != not_susceptible) & (temp != susceptible)) {
        printf("uni: Err: bb_bear_capab_decod-illegal suscpt. to clipping\n");
        return 0;
    }
    temp = grab_field_value_in_list(og->fields, 3);
    if (temp != point_to_point) {
        printf("uni: Err: bb_bear_capab_decod-illegal user_pln_call_cofig\n");
        return 0;
    }
    elem->octet_groups = insert(og, elem->octet_groups);
    return 1;
}

/******************************************************************************
 *   bb_high_layer_info_decoder
 *****************************************************************************/
int bb_high_layer_info_decoder(char *buf, element elem)
{
    register int bi = 4;
    octet_group og;

    debug("parse", "uni: bb_high_layer_info_decoder\n");
    if (decode_generic_element(buf, elem)) {
        return 1;
    } else {
        return 0;
    }
}


/******************************************************************************
 *   bb_low_layer_info_decoder
 *****************************************************************************/
int bb_low_layer_info_decoder(char *buf, element elem)
{
    register bi = 4;
    register int len = elem->hdr.length;   
    u_int temp, snap;
    int *t;
    int og_tbl_idx = 0;
    octet_group og;
    notes note;

    debug("parse", "uni: bb_low_layer_info_decoder\n");

    if ((((u_int) buf[bi] & 0x6f) >> 5) == 1) {            /* layer 1 id */
        if ((og = DOG) != 0) 
            bi += og->len;
        else
            return 0;
        elem->octet_groups = insert(og, elem->octet_groups);
        if (!(len -= og->len)) return 1;
    }
    if ((((u_int) buf[bi] & 0x60) >> 5) == 2) {           /* layer 2 id */
        temp =  ((u_int) buf[bi] & 0x1f);
        if ((temp == hdlc_arm_1)||(temp == hdlc_arm_2)||(temp == hdlc_arm_3)) 
            og_tbl_idx = 3;
        else if (temp == user_specified)
            og_tbl_idx = 2;
        else {
            /*printf("logical lan\n");*/
            og_tbl_idx = 1;
        }
        if ((og = DOG) != 0) 
            bi += og->len;
        else
            return 0;
        elem->octet_groups = insert(og, elem->octet_groups);
        if (!(len -= og->len)) return 1;
    }
    if ((((u_int) buf[bi] & 0x6f) >> 5) == 3) {           /* layer 3 id */
        temp =  ((u_int) buf[bi] & 0x1f);
        if ((temp == itu_x25)||(temp == iso_iec_8208)||(temp == x223_iso_8878))
        {
            printf("layer 3 - x25 stuff\n");
            og_tbl_idx = 7;
            if ((og = DOG) != 0) 
                bi += og->len;
            else
                return 0;
        } else if (temp == user_spec) {
            printf("layer 3 - user_spec\n");
            og_tbl_idx = 6;
            if ((og = DOG) != 0) 
                bi += og->len;
            else
                return 0;
        } else if (temp == iso_iec_tr_9577) {
            /* printf("layer 3 - iso_iec_tr_9577\n");*/
            og_tbl_idx = 5;
            if ((og = DOG) != 0) 
                bi += og->len;
            else
                return 0;
            elem->octet_groups = insert(og, elem->octet_groups);
            t = (int *)field_lookup_in_elem(elem, init_proto_ident, flat,0);
            if (*t == snap_ipi)
            {
                /*printf("layer 3 - SNAP\n");*/
                if ((len -= og->len) < 6) 
                    return 0;
                og_tbl_idx = 6;
                og = DOG;
                bi += og->len;
                elem->octet_groups = insert(og, elem->octet_groups); /* snap */
                og_tbl_idx = 7;
                og = DOG;
                bi += og->len;
                elem->octet_groups = insert(og, elem->octet_groups); /* oui */
                og_tbl_idx = 8;
                og = DOG;
                bi += og->len;
                elem->octet_groups = insert(og, elem->octet_groups); /* pid*/
	        }
        } else {
            printf("layer 3 - default\n");
            og_tbl_idx = 4;
            if ((og = DOG) != 0) 
                bi += og->len;
            else
                return 0;
        }
    }
    return 1;
}

/******************************************************************************
 *   call_state_decoder
 *****************************************************************************/
int call_state_decoder(char *buf,element elem)
{
    register int bi = 4;

    debug("parse", "uni: call_state_decoder\n");
    if (decode_generic_element(buf,  elem))  
        return 1;
    else
        return 0;
}

/******************************************************************************
 *   called_pty_number_decoder
 *****************************************************************************/
int called_pty_number_decoder(char *buf, element elem)
{
    register int bi = 4;
    u_int temp;

    debug("parse", "uni: called_pty_number_decoder\n");
    temp = ((u_int) buf[bi] & 0x7f) >> 4; 
    /*
    if ((temp != 0) && (temp != 1))
    {
        printf("\nuni: called_pty_#_decoder - bad type_of_number %d\n",
               buf[bi]);
        return 0;
    }
    temp = (u_int) buf[bi] & 0x0f; 
    if ((temp != 1) && (temp != 2))
    {
        printf("\nuni: called_pty_#_decoder - bad addr_plan_ident %d\n",
               buf[bi]);
        return 0;
    }
    */
    if (decode_generic_element(buf,  elem))  
        return 1;
    else
        return 0;
}

/******************************************************************************
 *   called_pty_subaddr_decoder
 *****************************************************************************/
int called_pty_subaddr_decoder(char *buf, element elem)
{
    register int bi = 4;
    
    debug("parse", "uni: called_pty_subaddr_decoder\n");
    if (decode_generic_element(buf, elem))  
        return 1;
    else
        return 0;
}

/******************************************************************************
 *   calling_pty_number_decoder
 *****************************************************************************/
int calling_pty_number_decoder(char *buf, element elem)
{
    register int bi = 4;
    int temp;

    debug("parse", "uni: calling_pty_number_decoder\n");
    temp = ((u_int) buf[bi] & 0x7f) >> 4; 
    if ((temp != 0) && (temp != 1))
    {
        printf("\nuni: calling_pty_#_decoder - bad type_of_number %d\n",
                               buf[bi]);
        return 0;
    }
    temp = (u_int) buf[bi] & 0x0f; 
    if ((temp != 1) && (temp != 2))
    {
            printf("\nuni: calling_pty_#_decoder - bad addr_plan_ident %d\n",
               buf[bi]);
            return 0;
    }
    if (decode_generic_element(buf,  elem))  
        return 1;
    else
        return 0;
}

/******************************************************************************
 *   calling_pty_subaddr_decoder
 *****************************************************************************/
int calling_pty_subaddr_decoder(char *buf, element elem)
{
    register bi = 4;
    
    debug("parse", "uni: calling_pty_subaddr_decoder");
    if (decode_generic_element(buf,  elem))  
        return 1;
    else
        return 0;
}

/******************************************************************************
 *   cause_decoder
 *****************************************************************************/
int cause_decoder(char *buf, element elem)
{
    register i, bi = 4;
    int og_tbl_idx = 0;
    octet_group og;
    element_header tehdr;
    element e;
    register int len = elem->hdr.length;   

    /* get first octet group (spare and location) */
    if ((og = DOG) == 0)    
        return 0;
    bi += og->len;
    elem->octet_groups = insert(og, elem->octet_groups);
    if (!(len -= og->len)) return 1;

    /* get cause value octet group */
    og_tbl_idx = 1;
    if ((og = DOG) == 0)
        return 0;
    bi += og->len;
    elem->octet_groups = insert(og, elem->octet_groups);
    if (!(len -= og->len)) return 1;

    /* switch on cause value to parse diagnostics */
    switch (((int) buf[bi-og->len] & 127))  {
        case unassigned_number:                /* unallocated number */
        case no_route_to_dest:    
        case qos_unavailable:
            og_tbl_idx = 2;
            if ((og = DOG) == 0) 
               return 0;
            break;
        case call_denied:                      /* call_rejected */
            og_tbl_idx = 3;
            if ((og = DOG) == 0) 
               return 0;
            elem->octet_groups = insert(og, elem->octet_groups);
            if (!(len -= og->len)) return 1;
            switch ((buf[bi++] & 0x7c) >> 2) {
                case info_elem_missing:
                case info_elem_content_insufficient:
                    bi++;
                    og_tbl_idx = 3;
                    if ((og = DOG) == 0) 
                        return 0;
                    elem->octet_groups = insert(og, elem->octet_groups);
                    if (!(len -= og->len)) 
                        return 1;
                    else 
                       return 0;       /* element too long */
                case user_specific:   /* treating this like a bunch of bytes */
                    og_tbl_idx = 10;
                    for (; len; len -= og->len, bi += og->len) {
                        if ((og = DOG) == 0) 
                           return 0;
                      elem->octet_groups = insert(og, elem->octet_groups);
                    }
                    return 1;   
            }
            break;
        case number_changed:
            e = (element) allocate_memory (sizeof(struct element));
            if (!simple_decode_elem_hdr(buf, e)) {
		print_errors("uni: Err: cause elem diagnostcs:", UNRECOG_ELEM);
		return 0;
            }
            if ((e->hdr.code!=called_party_number) |
		(e->hdr.code!=transit_net_select)) {
		printf("uni: cause_decoder - invalid IE in diagnostics\n");
		return 0;
            }
            if (e->hdr.length <= len) {
		if ((coders[e->hdr.tbl_index].decoder_func)(buf, tehdr)) {
		    /* how to store this element now? */
		}
	    } else {
		printf("uni: cause_decoder - cause IE too short\n");
		deallocate_element(e);
		return 0;
            }
            break;
        case access_info_discarded:
        case incompatible_dest:
        case info_elem_nonexist_nonimplemented:
        case invalid_info_elem_contents:
           og_tbl_idx = 4;
           for (; len; len -= og->len, bi += og->len) {   /* IE id(s) */
               if ((og = DOG) == 0)
                       return 0;
               elem->octet_groups = insert(og, elem->octet_groups);
           }
           return 1;
        case traffic_desc_unavailable:
            og_tbl_idx = 5;
        case identified_channel_non_existence:
            og_tbl_idx = 6;             /* vpci */
            if ((og = DOG) == 0)
                return 0;
            elem->octet_groups = insert(og, elem->octet_groups);
            og_tbl_idx++;
            if ((og = DOG) == 0)    /* vci */
                return 0;
            elem->octet_groups = insert(og, elem->octet_groups);
            return 1;
        case mandatory_info_elem_missing:
            og_tbl_idx = 0;
            break;
        case message_type_nonexist_nonimplemented:
        case mesg_not_compatible_with_call_state:
            og_tbl_idx = 8;
            if ((og = DOG) == 0)             /* message type */
                    return 0;
            elem->octet_groups = insert(og, elem->octet_groups);
            return 1;
        case recovery_or_timer_expire:  /* 3 ia5_char iding timer*/
            og_tbl_idx = 9;
            for (i = 0; len & (i < 3); len -= og->len, bi += og->len, i++) {
               if ((og = DOG) == 0) 
                       return 0;
               elem->octet_groups = insert(og, elem->octet_groups);
            }
            if (i <= 2) return 0;
            return 1;
        default:
            break;
    }
}

/******************************************************************************
 *   connection_ident_decoder
 *****************************************************************************/
int connection_ident_decoder(char *buf, element elem)
{
    debug("parse", "uni: connection_ident_decoder\n");
    if (decode_generic_element(buf,  elem))  
        return 1;
    else
        return 0;
}

/******************************************************************************
 *   qos_params_decoder
 *****************************************************************************/
int qos_params_decoder(char *buf, element elem)
{
    register bi = 4;

    debug("parse", "uni: qos_params_decoder\n");
    if (((int) buf[bi] < 0) && ((int) buf[bi] > 4))
    {    
        printf("\nuni: qos_params_decoder - bad qos_class_fwd %d\n", 
                   buf[bi]);
        return 0;
    }
    if (((int) buf[bi+1] < 0) && ((int) buf[bi+1] > 4))
    {    
        printf("\nuni: qos_params_decoder - bad qos_class_bwk %d\n", 
                   buf[bi]);
        return 0;
    }
    if (decode_generic_element(buf,  elem))  
      return 1;
    else
      return 0;
}

/******************************************************************************
 *   bb_repeat_indic_decoder
 *****************************************************************************/
int bb_repeat_indic_decoder(char *buf, element elem)
{
    register bi = 4;

    debug("parse", "uni: bb_repeat_indic_decoder\n");
    if ((((u_char) buf[bi]) & 0x0f) != REPEAT_INDICATOR) {    
        printf("\nuni: bb_repeat_indic_decoder - bad repeat_indic %d\n", 
                   buf[bi]);
        return 0;
    }
    if (decode_generic_element(buf,  elem))
        return 1;
    else
        return 0;
}

/******************************************************************************
 *   restart_indic_decoder
 *****************************************************************************/
int restart_indic_decoder(char *buf, element elem)
{
    register bi = 4;

    debug("parse", "uni: restart_indic_decoder\n");
    if (((((u_char) buf[bi]) & 0x07) != indic_vc)
       && ((((u_char) buf[bi]) & 0x07) != all_vc))
    {    
        printf("\nuni: restart_indic_decoder - bad restart_indic %d\n", 
                   buf[bi]);
        return 0;
    }
    if (decode_generic_element(buf,  elem))  
        return 1;
    else
        return 0;
}

/******************************************************************************
 *   bb_send_complete_decoder
 *****************************************************************************/
int bb_send_complete_decoder(char *buf, element elem)
{
    register bi = 4;

    debug("parse", "uni: bb_send_complete_decoder\n");
    if ((((u_char) buf[bi]) & 0x7f) != SENDING_COMPLETE_INDIC)
    {    
        printf("\nuni: bb_send_complere_decoder - bad indic %d\n", 
                   buf[bi]);
        return 0;
    }
    if (decode_generic_element(buf,  elem))  
        return 1;
    else
        return 0;
}

/******************************************************************************
 *   transit_net_select_decoder
 *****************************************************************************/
int transit_net_select_decoder(char *buf, element elem)
{
    register bi = 4;
    u_char first_octet = 0xa1; /* TYPE_OF_NET_IDENT, NET_IDENT_PLAN */

    debug("parse", "uni: transit_net_select_decoder\n");
    if (((u_char) buf[bi]) != first_octet)
    {    
        printf("\nuni: transit_net_select_decoder - bad first octet %d\n", 
                   buf[bi]);
        return 0;
    }
    if (decode_generic_element(buf,  elem))  
        return 1;
    else
        return 0;
}

/******************************************************************************
 *   endpoint_reference_decoder
 *****************************************************************************/
int endpoint_reference_decoder(char *buf, element elem)
{
    register bi = 4;
    u_char first_octet = 0xa1;

    debug("parse", "uni: endpoint_reference_decoder\n");

    if (decode_generic_element(buf,  elem))  
        return 1;
    else
        return 0;
}

/******************************************************************************
 *   endpoint_state_decoder
 *****************************************************************************/
int endpoint_state_decoder(char *buf, element elem)
{
    register bi = 4;
    u_char first_octet = 0xa1;

    debug("parse", "uni: endpoint_state_decoder\n");
    if (decode_generic_element(buf, elem))  
        return 1;
    else
        return 0;
}


/******************************************************************************
 * decode_octet_group
 *****************************************************************************/
octet_group decode_octet_group(char *buf, int ei, int oi)
/*
 * ei = element table index
 * oi = octet group table index
 *  
 */
{       
    register int i = 1, j = 0, tlen, shift;
    int char_len, done = 0;
    int bi = 0, ifi = 0;            /* ifi = internal fields table index */
    int bptr;                       /* bit position ptr in octet */
    octet_group_desc *ogptr;        /* ptr to og in element table */
    octet_group og = 0;
    field f;

    og =(octet_group) allocate_memory (sizeof(struct octet_group));
    og->len = 0;
    og->fields = 0;
    ogptr = the_elements_tbl[ei].ogs;
    bptr = char_len = (ogptr[oi].encoded == ext) ? CHAR_7 : CHAR;

    while ((og->len < (char_len * ogptr[oi].fixed_length)) & !done) { 
        f = (field) allocate_memory (sizeof(struct field));
        f->code = ogptr[oi].internal_fields[ifi][0];
        f->len = ogptr[oi].internal_fields[ifi][1];
        /*
         * decode an "address" field
         */
        if (ogptr[oi].encoded == addr_coded) {

            f->val.cval = (u_char *) allocate_memory ((f->len / 8) + 1);
            bcopy(buf+bi, f->val.cval, UNI_ADDR_LEN);
        }
        /*
         * decode an encoded field
         */
        else if (ogptr[oi].encoded == ext) {
            f->val.ival = 0;		/* NHH */
            tlen = f->len;
            while (tlen) {
                if (tlen > char_len) 
                    f->val.ival <<= char_len;
                else
                    f->val.ival <<= tlen;
                shift = ((bptr - tlen) < 0) ? 0 : bptr - tlen;
                f->val.ival |= (((u_char) buf[bi]) & BMASK(bptr)) >> shift;
                tlen -= bptr - shift;
                bptr -= bptr - shift;
                if (bptr == 0) {
                    if (((u_char) buf[bi]) >> CHAR_7) 
                        done = 1;
                    bi++;
                    bptr = char_len;
                }
            }
        /*
         * else, decode what has to be a "flat" field
         */
        } else {
            for (i=0, f->val.ival = 0; i < (f->len / CHAR);  i++, bi++) {
                f->val.ival <<= CHAR;
                f->val.ival |= (u_char) buf[bi];
            }   
            bptr = CHAR;
        }     
        /* insert field into octet group's field list */
        og->fields = insert(f, og->fields);
        og->len += f->len;
        ifi++;
    }
    og->len = og->len/char_len;
    return og;        
}


/******************************************************************************
 *  decode_element_header
 *
 *  Decode an element header which is made up of octet groups (og's). The og's
 *  that make up an "element header" are: 1)information_element_identifier,
 *  2) instruction_field, and 3) element length. Then follow the "contents"
 *  octet groups. For the moment, to keep things simple, only the contents 
 *  are considered in octet groups. 
 *****************************************************************************/
int decode_element_header(char *buf, element e, int mti, int *maskv)
{
    int idx;           /* element list index */

    bcopy(buf, &(e->hdr), 4);
    /*
    e->hdr.code = (int) buf[0];
    e->hdr.instruction_field = (u_char) buf[1] & 0x7f;
    e->hdr.length = ((u_int) ((u_char) buf[2])) << 8 | ((u_char) buf[3]);  
    */
    GET_TBL_IDX(e->hdr.tbl_index, e->hdr.code, the_elements_tbl, MAX_ELEMENTS);
    /* 
     * Check if element type and lenght are valid within this message.
     */
    if ((idx = is_ie_valid_in_mesg(mti, e->hdr.code, e->hdr.length)) < 0) {
        printf("\nuni: decode_element_hdr - elem %02x not found in mesg\n", 
                e->hdr.code);
        return 0;
    }
    *maskv = 1 << idx;
    return 1;
}

int simple_decode_elem_hdr(char *buf, element e)
{
    bcopy(buf, &(e->hdr), 4);
    /*
    e->code = (int) buf[0];
    e->instruction_field = (u_char) buf[1] & 0x7f;
    e->length = ((u_int) ((u_char) buf[2])) << 8 | ((u_char) buf[3]);  
    */
    GET_TBL_IDX(e->hdr.tbl_index, e->hdr.code, the_elements_tbl, MAX_ELEMENTS);
    return 1;
}

/******************************************************************************
 *  decode_generic_element 
 *****************************************************************************/
int decode_generic_element(char *buf, element elem)
{
    int len = elem->hdr.length;
    int bi = UNI_ELEM_HDR_LEN;
    int og_tbl_idx = 0;
    octet_group og;
    
    while (len) 
    {            
        if ((og = DOG) != 0) 
        {
            elem->octet_groups = insert(og, elem->octet_groups);
            og_tbl_idx++;
        }
        else
            return 0;
        len -= og->len;
        bi += og->len;
    }
    return 1;
}

/******************************************************************************
 *  uni_message decode_mesg(buf, mesg_hdr)
 *****************************************************************************/
uni_message decode_mesg(char *buf, uni_message_header mhdr)
{
    register int i;
    register u_int ie_mask;
    int mask_val, mlen = 0;
    element e = 0;
    uni_message mesg =
                    (uni_message)allocate_memory(sizeof(struct uni_message));
    mesg->hdr = mhdr;
    mesg->elements = 0;    


    /* Decode message header 
    if (!(mesg_hdr = decode_mesg_hdr(buf, mesg))) {
        fprintf(stderr,"uni: handle_uni_mesg - malformed mesg header\n");
        dump_encoded_mesg(buf, 10);
        return 1;
    }
    */

    while (mlen < mhdr->length) {

	e = (element) allocate_memory (sizeof (struct element));
	e->octet_groups = 0;

        /* Check if the mesg buf space contains at least an element header,
         * if so, decode element header. Element header length = 4. */
        if ((mlen += 4) <= mhdr->length) {
            if (!(decode_element_header(buf, e, mhdr->idx, &mask_val))) {
                print_errors("decode_mesg", UNRECOG_ELEM);
                return 0; 
            }

	    /* Check for blank IE */ 
	    if (e->hdr.length <= 0) {
		deallocate_element(e);
		buf += 4;
		/***** Need a goto here *******/
	    }
           
            /* Check if mesg buf is long enough to contain the entire element,
             * if so, call appropriate element decoder. */
            if ((mlen += e->hdr.length) <= mhdr->length) {
                if ((coders[e->hdr.tbl_index].decoder_func)(buf,e)) {
                    mesg->elements = insert(e, mesg->elements);
                    ie_mask |= mask_val;
                    buf += (4 + e->hdr.length);
                } else {
                     /*handle_elem_coding_erro(qp, mhdr, ehdr->code); */
                     printf("uni: error in decoding message, code: 0x%02x\n", 
			    e->hdr.code);
                     deallocate_element(e);
                     return 0;
                }
            } else { /* message buf is not large emough to contain element */
                print_errors("decode_mesg", BUFFER_DEPLETED);
		deallocate_element(e);
            }
        } else {  /* message buf is not large enough to contain element hdr */
             print_errors("decode_mesg", BUFFER_DEPLETED);
             printf("      in element, code: %02x\n", (u_char)buf[0]);
	     deallocate_element(e);
             return 0;
        }

    }

    /* Check that all mandatory elements are found in message */
    for (i = 0; i<messages_tbl[mhdr->idx].num_of_elems; i++,ie_mask >>=1) {
        /*printf("ie_mask: %02x\n", ie_mask); */
        if ((messages_tbl[mhdr->idx].elist[i].direction == BOTH) &&
            (messages_tbl[mhdr->idx].elist[i].type==MAND)&&(!(ie_mask & 0x01)))
	{
            fprintf(stderr, "uni: decode_mesg - missing MAND IEs %s\n", 
		    messages_tbl[mhdr->idx].  name);
            deallocate_uni_mesg(mesg);
            return 0;
	}
    }
    return mesg;
}







