/*
 * Copyright (c) 2003-2005 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Limited and no part
 * of it may be redistributed, published or disclosed except as outlined
 * in the written contract supplied with this product.
 *
 * $Id: dagcat_setup.c 13453 2010-12-02 23:30:08Z jomi.gregory $
 CAT setup tool
 */

#include "dag_config_api.h"
#include "dag_platform.h"
#include "cat_interface.h"
#include "dagutil.h"
#include "dagclarg.h"
#include "dagapi.h"
#include "dagcat_includes.h"
#include "dagcat_parse.h"

#define HSBM_SUPPORT 1

#define MAX_FILENAME_LEN   1024
#define CAT_MODE_BUFSIZE    128
/* checks val  is in [from,to] */
#define IS_VALUE_IN_RANGE(val,from,to) ((val>=from)&&(val<=to))
/* check for [from1,to1] comes before [from2,to2] */
#define IS_RANGE_BEFORE(from1,to1,from2,to2) ((from1<from2)&&(to1<from2))
/* check for [from1,to1] comes after [from2,to2] */
#define IS_RANGE_AFTER(from1,to1,from2,to2) ((from1>to2))
/* check for [from1,to1] overlaps with [from2,to2] */
#define IS_RANGES_OVERLAP(from1,to1,from2,to2) (!((IS_RANGE_BEFORE(from1,to1,from2,to2))||(IS_RANGE_AFTER(from1,to1,from2,to2))))
#define IS_RANGES_EQUAL(from1,to1,from2,to2) ((from1==from2)&&(to1==to2))
/* check for [from1,to1] is contained in [from2,to2] */
#define IS_RANGE_CONTAINMENT(from1,to1,from2,to2) ((from1>=from2)&&(to1<=to2))
//#define CATRULE_PARSE_TEST

/* CVS Header. */
static const char* const kCvsHeader __attribute__ ((unused)) = "$Id: dagcat_setup.c 13453 2010-12-02 23:30:08Z jomi.gregory $";
static const char* const kRevisionString = "$Revision: 13453 $";


typedef enum dagcat_config_mode
{
    kCATModeInvalid = -1,
    kCATModeDup3 = 0,
    kCATModeZ4,
    kCATModeZ8,
    kCATModeDup2,
    kCATModeZ3,
    kCATModeZ2,
    kCATModeCustomRuleFile,
    kCATModeZ3Monitor,
    kCATModeLast
}dagcat_config_mode_t ;

/** Possible command line option identifiers */
enum
{
	CLA_HELP,
	CLA_VERBOSE,
	CLA_VERSION,
	CLA_MODE,
 	CLA_DEVICE,
    CLA_RULE_FILENAME,
    CLA_QUERY_CAT_CAPS
};
#define BUFFER_SIZE  256

static char dagname[BUFFER_SIZE] = "dag0";
static int dagstream = 0;

static void
print_version(void)
{
	printf("%s (%s) %s\n", dagutil_get_progname(), kDagReleaseVersion, kRevisionString);
}

typedef struct
{
    dagcat_config_mode_t mode;
    const char* string;
} dagcat_mode_string_map_t;

static  dagcat_mode_string_map_t dagcat_mode_string [] = { 
                        {kCATModeInvalid,"Invalid"},
                        {kCATModeDup3,"dup3"},
                        {kCATModeZ4,"z4"},
                        {kCATModeZ8,"z8"},
                        {kCATModeDup2,"dup2"}, 
                        {kCATModeZ3,"z3"},
                        {kCATModeZ2,"z2" },
                        {kCATModeZ3Monitor,"z3monitor"},
                        {kCATModeCustomRuleFile, "customized"},
                         };
static char *cat_lib_mode_string[] = { "ETH_HDLC_POS","COLOR_HDLC_POS_COLOR_ETH" ,"COLOR_HASH_POS_COLOR_HASH_ETH","COLOR_HASH_POS_COLOR_HASH_ETH_SHIFTED"};
/* function declarations */
static cat_mode find_cat_mode(dag_card_ref_t card, dag_component_t root_comp);
static void print_usage(ClArgPtr clarg);
#ifdef HSBM_SUPPORT
static int combine_hlbs_to_stream(dag_component_t cat_component,uint32_t  hlb_from, uint32_t hlb_to,uint64_t destn_stream);
#else
static int combine_hlbs_to_stream(dag_component_t cat_component,uint32_t  hlb_from, uint32_t hlb_to,int32_t destn_stream);
#endif
static int32_t configure_in_dupn(dag_component_t cat_component, uint32_t n);
static int32_t configure_in_mode( char *dagname, dagcat_config_mode_t mode, char *filename);
static int32_t configure_in_custom_mode( dag_component_t cat_component, char* filename);
static int32_t fill_cat_capabilities(dag_component_t cat_component, dagcat_capabilities_t *in_cap);
static int32_t initialize_a_cat_rule(dagcat_config_rule_p rule, const dagcat_capabilities_t* cap);
static int32_t validate_cat_rule(const dagcat_config_rule_p rule, const dagcat_capabilities_t* cap);
static int32_t parse_cat_rule_file(FILE *fin, const dagcat_capabilities_t* cap, dagcat_config_rule_p *head);
static int32_t apply_a_cat_rule( dag_component_t cat_component, dagcat_config_rule_p this_rule);
static int32_t apply_cat_rules_list(dag_component_t cat_component, dagcat_config_rule_p head);
static void cleanup_cat_rules_list(dagcat_config_rule_p head);
static void display_cat_rules_list(dagcat_config_rule_p head);
static int clear_cat_table_entries(dag_component_t cat_component);
static dagcat_config_mode_t string_to_dagcat_mode (const char* string);
static const char* dagcat_mode_to_string (dagcat_config_mode_t in_mode);
static int32_t configure_in_zn(dag_component_t cat_component, uint32_t n, int monitor_stream_number);
static int32_t validate_convert_bit_encoded_streams_to_binary(uint32_t max_streams, uint64_t* this_stream);
static uint8_t is_cat_output_binary_encoded ( dag_component_t cat_component);
static int32_t dagcat_set_bypass_value(dag_component_t cat_component, uint8_t value);
static int32_t dagcat_validate_mode(cat_mode cat_lib_mode, dagcat_config_mode_t config_mode );
static void dagcat_print_supported_modes(FILE* outfile);
static void print_cat_query_capabilities(char *dagname);
static uint8_t is_indices_valid_for_rule(uint32_t this_color, uint32_t this_hash, uint32_t this_iface, dagcat_config_rule_p this_rule);
static int32_t apply_inverted_cat_rule( dag_component_t cat_component, dagcat_config_rule_p this_rule);

/*--------------------------------------------------------------------
 
 FUNCTION :      is_cat_output_binary_encoded
 
 DESCRIPTION :   Checks whether the CAT output is binary encoded or not
                card. The max stream value supported will depend on this
 
 PARAMETERS :    dag_component_t cat_component  - The CAT component
  RETURNS :       1 if output is binary encoded
                0 if output is bit encoded 
---------------------------------------------------------------------*/

uint8_t is_cat_output_binary_encoded ( dag_component_t cat_component)
{   uint8_t bool_value = 0;
	dag_card_ref_t card_ref =  NULL;
    attr_uuid_t attribute;
	
    card_ref = dag_component_get_card_ref(cat_component);
    if ( NULL == card_ref )
        return 0;
	attribute = dag_component_get_attribute_uuid(cat_component,kBooleanAttributeOutputType);
    if ( kNullAttributeUuid == attribute )
        return 0;
	bool_value = dag_config_get_boolean_attribute(card_ref,attribute);
	return !(bool_value);
}

/*--------------------------------------------------------------------
 
 FUNCTION :      dagcat_set_bypass_value
 
 DESCRIPTION :   Enable or disable the CAT module
 
 PARAMETERS :    cat_component  - The CAT component
                value- if 1 enables bypass mode(disables CAT)
                       if 0 disables bypass mode (enables CAT)
  RETURNS :       -1 if failed 
                0 if success
---------------------------------------------------------------------*/
int32_t dagcat_set_bypass_value(dag_component_t cat_component,uint8_t value)
{
	dag_card_ref_t card_ref =  NULL;
    attr_uuid_t attribute;
	
    card_ref = dag_component_get_card_ref(cat_component);
    if ( NULL == card_ref )
        return -1;
	attribute = dag_component_get_attribute_uuid(cat_component, kBooleanAttributeByPass);
    if ( kNullAttributeUuid == attribute )
    {
        dagutil_error("No attribute \n");
        return -1;
    }
	if ( kDagErrNone  != dag_config_set_boolean_attribute(card_ref,attribute, 0) )
    {
        dagutil_error("Could not set value\n");
        return -1;
    }
	return 0;
}

/*--------------------------------------------------------------------
  FUNCTION :      string_to_dagcat_mode
  DESCRIPTION :   To converte string to enum
  PARAMETERS :    String
  RETURNS :       The converted dagcat_config_mode_t
---------------------------------------------------------------------*/
dagcat_config_mode_t string_to_dagcat_mode (const char* string)
{
    int count = sizeof(dagcat_mode_string)/sizeof(dagcat_mode_string_map_t);
    int i;

    for (i = 0; i < count; i++)
    {
        if (strcmp(dagcat_mode_string[i].string, string) == 0)
        {
            return dagcat_mode_string[i].mode;
        }
    }
    return kCATModeInvalid;
}

/*--------------------------------------------------------------------
 FUNCTION :      dagcat_mode_to_string
 DESCRIPTION :   To converted enum to string
 PARAMETERS :    dagcat_config_mode_t
 RETURNS :       The converted string
---------------------------------------------------------------------*/
const char* dagcat_mode_to_string (dagcat_config_mode_t in_mode)
{
    int count = sizeof(dagcat_mode_string)/sizeof(dagcat_mode_string_map_t);
    int i;

    for (i = 0; i < count; i++)
    {
        if (dagcat_mode_string[i].mode == in_mode)
        {
            return dagcat_mode_string[i].string;
        }
    }
    return NULL;
}
/*--------------------------------------------------------------------
 FUNCTION :      find_cat_mode
 DESCRIPTION :   To check the the card configuration and find the mode as 
                required in the CAT library, as follows.
                    - If IPF and HASH are enabled, it is COLOR_HASH_POS_COLOR_HASH_ETH
                    - If only IPF , then it is COLOR_HDLC_POS_COLOR_ETH
                    - If IPF is not enabled , ETH_HDLC_POS
 PARAMETERS :    card:       card reference
                root_comp :  the root component
 RETURNS :        cat_mode 
---------------------------------------------------------------------*/
cat_mode find_cat_mode(dag_card_ref_t card, dag_component_t root_comp)
{

    dag_component_t ipf_component = NULL;
    dag_component_t hat_component = NULL;
    attr_uuid_t current_attribute = 0;
    ipf_component = dag_component_get_subcomponent(root_comp,kComponentIPF, 0); 
    //dsm_comp = dag_component_get_subcomponent(root_comp,kComponentDSM, 0); 
    if ( (ipf_component== NULL) )
    {
        return ETH_HDLC_POS;
    }
   /* check if IPF is enabled */
    current_attribute = dag_component_get_attribute_uuid(ipf_component, kBooleanAttributeIPFEnable);
    if ( current_attribute && !dag_config_get_boolean_attribute(card, current_attribute ))
    {
        /* IPF not enabled - so not colored */
        return ETH_HDLC_POS;
    }
    /* from here , it is MODE is COLORed */
    /* chk for HAT is present */
    hat_component = dag_component_get_subcomponent(root_comp,kComponentHAT, 0); 
    if ( (hat_component == NULL) )
    {
        /* HAT not present - so no HASH */
        return COLOR_HDLC_POS_COLOR_ETH;
    }
    /* HAT is present , check if it is enabled */
    current_attribute = dag_component_get_attribute_uuid(hat_component, kBooleanAttributeHashEncoding);
    if ( current_attribute && !dag_config_get_boolean_attribute(card, current_attribute ))
    {
        /* hash is not enabled ..so just COLOR*/
        return COLOR_HDLC_POS_COLOR_ETH;
    }
    /* from here , it is COLOR_HASH */
    /* check if its shifted COLOR_HASH */
    current_attribute = dag_component_get_attribute_uuid(hat_component, kBooleanAttributeIPFShiftColour);
    if ( current_attribute && dag_config_get_boolean_attribute(card, current_attribute ))
    {
        return COLOR_HASH_POS_COLOR_HASH_ETH_SHIFTED;
    }
    return COLOR_HASH_POS_COLOR_HASH_ETH;

}
/*--------------------------------------------------------------------
  FUNCTION :    is_sonet_pp_module_present
 DESCRIPTION :  Checks if the bfs packet morphing feature is presents.
 PARAMETERS :   card      - card reference
                root_comp - root component
  RETURNS :     1  if  yes
                0 if  no
---------------------------------------------------------------------*/
bool is_sonet_pp_module_present(dag_card_ref_t card, dag_component_t root_comp)
{
	dag_component_t sonet_pp_component = NULL;
   	attr_uuid_t current_attribute = 0;
    sonet_pp_component = dag_component_get_subcomponent(root_comp,kComponentSonetPP, 0); 
    if ( (sonet_pp_component == NULL) )
    {
        return false;
    }
	else
	{
		return true;
	}
}
/*--------------------------------------------------------------------
  FUNCTION :    is_bfs_packet_morphing_feature_present
 DESCRIPTION :  Checks if the bfs packet morphing feature is presents.
 PARAMETERS :   card      - card reference
                root_comp - root component
  RETURNS :     1  if  yes
                0 if  no
---------------------------------------------------------------------*/
bool is_bfs_packet_morphing_feature_present(dag_card_ref_t card, dag_component_t root_comp)
{
	
    dag_component_t per_stream_component = NULL;
   	attr_uuid_t current_attribute = 0;
    per_stream_component = dag_component_get_subcomponent(root_comp,kComponentStreamFeatures, 0); 
    //dsm_comp = dag_component_get_subcomponent(root_comp,kComponentDSM, 0); 
    if ( (per_stream_component == NULL) )
    {
        return false;
    }
   /* check if bfs morphing feature is enabled is enabled */
    current_attribute = dag_component_get_attribute_uuid(per_stream_component, kBooleanAttributeBfsMorphing);
    if ( current_attribute && dag_config_get_boolean_attribute(card, current_attribute ))
    {
        return true;
    }else
	{
		return false;
	}
}
/*--------------------------------------------------------------------
  FUNCTION :    is_ext_header_strip_feature_present
 DESCRIPTION :  Checks if the is_ext_header_strip_feature_present.
 PARAMETERS :   card      - card reference
                root_comp - root component
  RETURNS :     1  if  yes
                0 if  no
---------------------------------------------------------------------*/
bool is_ext_header_strip_feature_present(dag_card_ref_t card, dag_component_t root_comp)
{
	
    dag_component_t per_stream_component = NULL;
   	attr_uuid_t current_attribute = 0;
    per_stream_component = dag_component_get_subcomponent(root_comp,kComponentStreamFeatures, 0); 
    //dsm_comp = dag_component_get_subcomponent(root_comp,kComponentDSM, 0); 
    if ( (per_stream_component == NULL) )
    {
        return false;
    }
   /* check if bfs morphing feature is enabled is enabled */
    current_attribute = dag_component_get_attribute_uuid(per_stream_component, kBooleanAttributeExtErfStrip);
    if ( current_attribute && dag_config_get_boolean_attribute(card, current_attribute ))
    {
        return true;
    }
	else
	{
		return false;
	}
}
#if 0
uint32_t get_config_erf_type(dag_card_ref_t card,dag_component_t root_component)
{

	cat_mode my_cat_mode = ETH_HDLC_POS;
	bool is_sonet_pp_present = false;
	my_cat_mode = find_cat_mode(card,root_component);
	is_sonet_pp_present = is_sonet_pp_module_present(card,root_component);
	if(my_cat_mode == 0)	
	{
		if(is_sonet_pp_present)
			return  0x1;
		else	
			return  0x2;
	}
	else if(my_cat_mode == 1)
	{	
		if(is_sonet_pp_present)
			return 0xA;
		else
			return 0xB;
	}
	else /*cat mode is 3 or 4*/
	{
		if(is_sonet_pp_present)
			return 0x13;
		else
			return 0x14;
	}			
}
#endif
/*--------------------------------------------------------------------
 FUNCTION :      dagcat_print_supported_modes
 DESCRIPTION :  Prints the supported modes by checking the array
 PARAMETERS :    outfile: to which it is to be printed (stdout)
  RETURNS :        None 
---------------------------------------------------------------------*/
void dagcat_print_supported_modes(FILE* outfile)
{
    int count = sizeof(dagcat_mode_string)/sizeof(dagcat_mode_string_map_t);
    int i;
    fprintf(outfile, "Supported modes are:");
    for (i = 0; i < count; i++)
    {
        if ((dagcat_mode_string[i].mode != kCATModeCustomRuleFile) && (dagcat_mode_string[i].mode != kCATModeInvalid))
        {
            fprintf(outfile,"%s, ",dagcat_mode_string[i].string);
        }
    }
    fprintf(outfile,"\n");
    return ;
}
void print_usage(ClArgPtr clarg)
{
	print_version();
	dagclarg_display_usage(clarg, stdout);
    /* just to adjust the spaces */
    fprintf(stdout,"\t\t\t     ");
    dagcat_print_supported_modes( stdout);
}
/*--------------------------------------------------------------------
 FUNCTION :      clear_cat_table_entries
 DESCRIPTION :   Clears all the entries of the CAT table. All the values would be zeroed(no stream select)
                This function should be called before loading new configuration, because the new configuration
                may not change all the entries of the CAT table.
 PARAMETERS :    cat_component  - The CAT component
  RETURNS :         -1 if failed 
                0 if success
---------------------------------------------------------------------*/
int clear_cat_table_entries(dag_component_t cat_component)
{
	uint64_t destn_stream = 0;					  //drop
	uint64_t ret_val = 0;
	int address = 0;
	int max_address = (1 << cat_get_no_of_input_bits(cat_component)) - 1;
	for( address = 0; address <= max_address; address++)
	{
		ret_val = cat_new_set_raw_entry_verify(cat_component, (uint16_t) address, destn_stream);
		if ( destn_stream != ret_val )
		{
			dagutil_error("Clearing CAT entry failed at address: 0x%04x\n",address);
			return -1;
		}
	}
	return 0;
}
/*--------------------------------------------------------------------
 
 FUNCTION :      combine_hlbs_to_stream
 DESCRIPTION :   Sets the stream value in the CAT table for a specified hlb (hash) range.
                The color and iface would vary from 0 to their max values.
                Used in configuring in load balancing modes (zN)
 PARAMETERS :    cat_component  - The CAT component
                hlb_from : the start value for hlb range
                hlb_to :  the end value of the hlb range
                destn_strem : the destn_stream value to be written to the CAT table.(bit e
   RETURNS :       -1 if failed 
                0 if success
---------------------------------------------------------------------*/
#ifdef HSBM_SUPPORT
int combine_hlbs_to_stream(dag_component_t cat_component,uint32_t  hlb_from, uint32_t hlb_to,uint64_t destn_stream)
#else
int combine_hlbs_to_stream(dag_component_t cat_component,uint32_t  hlb_from, uint32_t hlb_to,int32_t destn_stream)
#endif
{
    uint32_t color_from = 0;
    uint32_t color_to =  (1 <<  cat_get_max_color(cat_component)) - 1;
    uint32_t iface_from = 0;
    uint32_t iface_to =  ( 1 <<  cat_get_max_interface(cat_component)) -1 ;
    uint32_t hlb_index = 0;
    uint32_t iface_index = 0;
    uint32_t color_index = 0;
#ifdef HSBM_SUPPORT
    uint64_t ret_val = kErrorNone;
	uint64_t stream_max_number = 1;
#else
	int32_t  ret_val =  kErrorNone;
#endif
    uint32_t max_streams = 0;
    uint8_t  is_binary_encoded = 0;
	

    /* checks the stream is within the range */
    max_streams = cat_get_no_of_output_bits( cat_component);
	stream_max_number = (stream_max_number << max_streams);
    is_binary_encoded = is_cat_output_binary_encoded(cat_component);
    if ( is_binary_encoded )
    {
        if ( -1 == validate_convert_bit_encoded_streams_to_binary(max_streams, &destn_stream) )
        {
            dagutil_error("Binary Encoded output. Invalid stream: 0x%"PRIx64" \n",destn_stream);
            return -1;
        }
    }
    else
    {
        if ( destn_stream >= stream_max_number )
        {
            dagutil_error("Stream number not supported:0x%"PRIx64".The number of streams not sufficient to supprt this mode\n",destn_stream);
            return -1;
        }
    }
	
	dagutil_verbose_level(1, "Mapping hash range %2d->%2d:0x%"PRIx64" \n", hlb_from,hlb_to,destn_stream); 
	for ( hlb_index = hlb_from; hlb_index <= hlb_to; hlb_index++)
		for ( color_index = color_from; color_index <= color_to;color_index++)
    		for ( iface_index = iface_from; iface_index <= iface_to; iface_index++)	       
            {	
				#ifdef HSBM_SUPPORT
                ret_val = cat_new_set_entry_verify(cat_component, iface_index, color_index, hlb_index, destn_stream);
				#else
				ret_val = cat_set_entry_verify(cat_component, iface_index, color_index, hlb_index, destn_stream);
				#endif
                if ( destn_stream != ret_val )
                {
                    dagutil_verbose_level(2, "Failed for Iface(%d)Color(%d)Hash(%d)Stream(%"PRIx64")\n",iface_index, color_index, hlb_index, destn_stream);
                    dagutil_error("Setting CAT entry failed \n");
                    return -1;
                }
            }

	return 0;
}
/*--------------------------------------------------------------------
 
 FUNCTION :      configure_in_zn
 DESCRIPTION :   To configure in particular load balancing mode with monitor on a particular stream
                Distribute the hash values from 0 to max into different streams values.
 PARAMETERS :    cat_component  - The CAT component
                n  - the value of N in zN
                monitor_stream_number - the stream number on which all packets wud be directed(monitor)
   RETURNS :       -1 if failed 
                0 if success
---------------------------------------------------------------------*/
int32_t configure_in_zn(dag_component_t cat_component, uint32_t n, int monitor_stream_number)
{
    int32_t ret_val = 0;
    uint32_t hash_increment = 0;
    uint32_t hash_from = 0;
    uint32_t hash_to = 0;
#ifdef HSBM_SUPPORT
    uint64_t destn_stream = 0x01;
#else
	int32_t destn_stream = 0x01;
#endif
    uint32_t max_hlb_value = 0;
    uint32_t monitor_stream = 0;

    max_hlb_value = ( 1 << get_configured_hash_width(cat_component)) - 1;
    if ( n > max_hlb_value + 1 )
    {
        dagutil_error(" z%d not possible. Maximum hash value is %d \n", n, max_hlb_value + 1);
        return -1;
    }
    if ( monitor_stream_number >=  0 )
    {
        monitor_stream = ( 1 << (monitor_stream_number >> 1 ) );
    }
    hash_increment = (max_hlb_value + 1) / n ;
    hash_to = hash_increment - 1;
    for(;hash_to <= max_hlb_value; hash_from+=hash_increment, hash_to += hash_increment, destn_stream <<= 1)
    {
     //printf("Configuring HLB's %d -> %d to stream%d\n",hash_from,hash_to,destn_stream);   
     ret_val = combine_hlbs_to_stream(cat_component, hash_from, hash_to, (destn_stream | monitor_stream ));
     if ( ret_val < 0 )
        return -1;
    }
    if ( (max_hlb_value + 1) % n )
    {
        destn_stream = 0x01;
         /* distribute the remaining hash values.Using hash_rom as the iterating variable */
        for(;hash_from <= max_hlb_value; hash_from++)
        {
            ret_val = combine_hlbs_to_stream(cat_component, hash_from, hash_from, (destn_stream|monitor_stream) );
            if ( ret_val < 0 )
                return -1;
        }
    }
   return 0;
}
/*--------------------------------------------------------------------
  FUNCTION :      configure_in_dupn
 DESCRIPTION :   To configure in particular duplication mode
 PARAMETERS :    cat_component  - The CAT component
                n  - the value of N in dupN
  RETURNS :       -1 if failed 
                0 if success
---------------------------------------------------------------------*/
int32_t configure_in_dupn(dag_component_t cat_component,uint32_t n)
{

    uint8_t hlb_from = 0;
    uint8_t hlb_to = 0;
#ifdef HSBM_SUPPORT
    int32_t destn_stream = 0;
#else
	uint64_t destn_stream = 0;
#endif
    int32_t ret_val = -1;
    uint32_t max_streams = 0;
    uint32_t max_hlb_value = 0;
    
    if ( is_cat_output_binary_encoded(cat_component) )
    {
        dagutil_error("CAT outputs are binary encoded.So dup is not possible\n");
        return -1;
    }
    max_streams =  cat_get_no_of_output_bits(cat_component);
    if ( n > max_streams) 
    {
        dagutil_error("CAT output supports only %d streams.So dup%d is not possible\n",max_streams, n);
        return -1;
    }
    /* calculate the destination stream . for dup3 destn_stream = 0x7, for dup2  destn_stream = 0x3 */
    destn_stream = ( 1 << n ) - 1; 
    max_hlb_value = ( 1 << get_configured_hash_width(cat_component)) - 1;
    
    dagutil_verbose("Configuring in Dup%d \n",n);
    
    for(hlb_from = 0, hlb_to = 0;hlb_to <= max_hlb_value; hlb_from+=1, hlb_to += 1)
    {    
        ret_val = combine_hlbs_to_stream(cat_component, hlb_from, hlb_to, destn_stream);
        if ( ret_val < 0 )
            return -1;
    }
    return 0;
}
/*--------------------------------------------------------------------
  FUNCTION :      dagcat_validate_mode
 DESCRIPTION :   To validate the mode, against the card configuration and hence the CAT lib mode.
                eg, For zN (load balancing) modes, we use hash values to distribute. So the mode shoud be HASHed.
 PARAMETERS :    cat_lib_mode  - The CAT lib mode
                config_mode  - The tool's input mode 
  RETURNS :       1 if valid 
                0 if not valid
---------------------------------------------------------------------*/
int32_t dagcat_validate_mode(cat_mode cat_lib_mode, dagcat_config_mode_t config_mode )
{
    switch ( config_mode)
    {
        case kCATModeDup3:
        case kCATModeDup2:
            return 0;
        case kCATModeZ8:
        case kCATModeZ4:
        case kCATModeZ2:
        case kCATModeZ3:
        case kCATModeZ3Monitor:
            if ( (cat_lib_mode == ETH_HDLC_POS) || (cat_lib_mode == COLOR_HDLC_POS_COLOR_ETH ))
            {
                dagutil_error("Please enable IPF/DSM and HASH mode for Z Modes\n");
                return -1;
            }
        case kCATModeCustomRuleFile:
            return 0;
        default :
            dagutil_error("Invalid Mode in %s\n",__FUNCTION__);
            return -1;
    }
    return 0;
}
/*--------------------------------------------------------------------
  FUNCTION :      configure_in_mode
 DESCRIPTION :   To configure in a particular  mode
 PARAMETERS :    dagname  - dag name
                mode  - the input mode (parsed frm the string )
                filename - Name of the rule file, if customized mode. For other modes NULL
  RETURNS :       -1 if failed 
                0 if success
---------------------------------------------------------------------*/
int32_t configure_in_mode( char *dagname, dagcat_config_mode_t mode, char *filename)
{
    dag_card_ref_t card_ref = NULL;
    dag_component_t root_component = NULL;
    dag_component_t  cat_component = NULL;
    dag_component_t  hat_component = NULL;
	int ret_val = 0;
#if 0	
	dag_component_t  per_stream_component = NULL;
	bool is_ext_header_stripping_present = false;
	bool is_bfs_packet_morphing_present = false;
	bool is_sonet_pp_present = false;
	int ret_val ,loop = 0;
	uint32_t config_erf_type = 0;
	uint32_t no_of_streams = 0;
	attr_uuid_t current_attribute = NULL;
#endif
    cat_mode my_cat_mode = ETH_HDLC_POS;
    //printf( "configure_in_mode mode %d \n", mode);
    card_ref = dag_config_init(dagname);
    if (card_ref == NULL)
    {
        dagutil_error("No card\n");
        return -1;
    }
    root_component = dag_config_get_root_component(card_ref);
    if ( NULL == root_component)
    {
        dagutil_error("No Root component\n");
        dag_config_dispose(card_ref);
        return -1;
    }
    cat_component = dag_component_get_subcomponent(root_component, kComponentCAT, 0);
    if ( NULL == cat_component)
    {
        dagutil_error("No CAT component\n");
        dag_config_dispose(card_ref);
        return -1;
    }
    /* make sure that it has a HAT component -defensive check.The tool assumes that  kComponentHAT is present*/
    hat_component = dag_component_get_subcomponent(root_component, kComponentHAT, 0);
    if ( NULL == hat_component)
    {
        dagutil_error("No HAT component\n");
        dag_config_dispose(card_ref);
        return -1;
    }
    my_cat_mode = find_cat_mode(card_ref, root_component);
    cat_set_entry_mode(my_cat_mode);
    dagutil_verbose("CAT Mode is %s\n", cat_lib_mode_string[my_cat_mode]);
	

	#if 0
	/*Support for BFS */
	/*if true means have to configure the morphing configuration register in the perstream component.*/
	is_bfs_packet_morphing_present = is_bfs_packet_morphing_feature_present(card_ref,root_component);
	/*if true means CAT runs in 14:8 mode and only will have 4 bits of hash. --> 16 streams load balancing.*/
	is_ext_header_stripping_present = is_ext_header_strip_feature_present(card_ref,root_component);
	/*SONET firmware is loaded and not ethernet.*/
	is_sonet_pp_present	 = is_sonet_pp_module_present(card_ref,root_component);
	

	config_erf_type = get_config_erf_type(card_ref,root_component);
	
	if((is_bfs_packet_morphing_present == true)&& ( is_ext_header_stripping_present == true))
	{
		per_stream_component = dag_component_get_subcomponent(root_component,kComponentStreamFeatures,0); 
    	if ( (per_stream_component == NULL) )
    	{
        	return NULL;
    	}
		/* check if bfs morphing feature is enabled is enabled */
    	current_attribute = dag_component_get_attribute_uuid(per_stream_component, kUint32AttributeNumberOfStreams);
		no_of_streams = dag_config_get_uint32_attribute(card_ref, current_attribute);
		if (is_sonet_pp_present)
		{
			for(loop = 0; loop < no_of_streams;loop++)
			{
					
				current_attribute = dag_component_get_attribute_uuid(per_stream_component, kUint32AttributeMorphingConfiguration);
				dag_config_set_uint32_attribute(card_ref,current_attribute,config_erf_type);
			}
		}
		else
		{
			for(loop = 0; loop < no_of_streams;loop++)
			{
					
				current_attribute = dag_component_get_attribute_uuid(per_stream_component, kUint32AttributeMorphingConfiguration);
				dag_config_set_uint32_attribute(card_ref,current_attribute,config_erf_type);
			}
		}
	}
	#endif
	/* for z modes, the color and hash should be enabled . Make that check here */
    ret_val = dagcat_validate_mode(my_cat_mode, mode);
    if ( -1 == ret_val )
    {
        dag_config_dispose(card_ref);
        return -1;
    }
    switch ( mode)
    {
        case kCATModeDup3:
            /* call generic dup function with n= 3*/
            ret_val =  configure_in_dupn(cat_component, 3);
            break;
        case kCATModeDup2:
            /* call generic dup function with n= 2*/
            ret_val =  configure_in_dupn(cat_component, 2);
            break;
        case kCATModeZ8:
            /* call generic z function where n = 8, no monitor stream  so -1 */
            ret_val =  configure_in_zn(cat_component, 8, -1);
            break;
        case kCATModeZ4:
            /* call generic z function where n = 4, no monitor stream  so -1 */
            ret_val =  configure_in_zn(cat_component, 4 , -1);
            break;
        case kCATModeZ2:
            /* call generic z function where n = 2, no monitor stream  so -1 */
            ret_val =  configure_in_zn(cat_component, 2 , -1);
            break;
        case kCATModeZ3:
            /* call generic z function where n = 3, no monitor stream  so -1 */
            ret_val =  configure_in_zn(cat_component, 3, -1);
            break;
        case kCATModeCustomRuleFile:
            ret_val = configure_in_custom_mode(cat_component, filename );
            break;
        case kCATModeZ3Monitor:
            /* call generic z function where n = 3,  monitor stream 6 */
            ret_val =  configure_in_zn(cat_component, 3, 6);
            break;
        default :
            dagutil_error("Invalid Mode. \n");
            ret_val =  -1;
    }
    if ( -1 != ret_val )
    {
		/* if success, swap the CAT banks to enable the newly loaded rules */
        dagutil_verbose_level ( 1, "Swapping the banks \n");
        cat_swap_banks(cat_component);
        dagutil_verbose_level ( 1, "Activated CAT bank:%u\n",cat_get_current_bank(cat_component));
        dagutil_verbose_level ( 1, "Enabling the CAT \n");
        if ( -1 == dagcat_set_bypass_value(cat_component, 0) )
        {
            dagutil_error("Failed to enable CAT \n");
        }
    }
    dag_config_dispose(card_ref);
    return ret_val;
}
/*--------------------------------------------------------------------
  FUNCTION :      print_cat_query_capabilities
 DESCRIPTION :   To print the CAT's capabilities with the current card configuration.
                It prints,  max supported color
                            max supported hash 
                            max supported iface
                            max supported stream
 PARAMETERS :    dagname  - dag name
 RETURNS :       None
---------------------------------------------------------------------*/
void print_cat_query_capabilities(char *dagname)
{
    dag_card_ref_t card_ref = NULL;
    dag_component_t root_component = NULL;
    dag_component_t  cat_component = NULL;
    int ret_val = 0;
    cat_mode my_cat_mode = ETH_HDLC_POS;
    dagcat_capabilities_t cat_cap;
    //printf( "configure_in_mode mode %d \n", mode);
    card_ref = dag_config_init(dagname);
    if (card_ref == NULL)
    {
        dagutil_error("No card\n");
        return;
    }
    root_component = dag_config_get_root_component(card_ref);
    if ( NULL == root_component)
    {
        dagutil_error("No Root component\n");
        dag_config_dispose(card_ref);
        return;
    }
    cat_component = dag_component_get_subcomponent(root_component, kComponentCAT, 0);
    if ( NULL == cat_component)
    {
        dagutil_error("No CAT component\n");
        dag_config_dispose(card_ref);
        return;
    }
    my_cat_mode = find_cat_mode(card_ref, root_component);
    cat_set_entry_mode(my_cat_mode);
    dagutil_verbose_level(2, "CAT Mode is %s\n", cat_lib_mode_string[my_cat_mode]);
    
    /* initilize the cat capabilities */
    memset (&cat_cap, 0, sizeof(cat_cap));
    ret_val = fill_cat_capabilities(cat_component, &cat_cap);
    if ( -1 == ret_val)
    {
        dagutil_error("Failed to get cat capabilities \n");
    }
    printf("The CAT supports the following \n");
    printf("Color  Maximum Value\t: %d\n",cat_cap.cat_max_color);
    printf("Hash   Maximum Value\t: %d\n",cat_cap.cat_max_hash);
    printf("Iface  Maximum Value\t: %d\n",cat_cap.cat_max_iface);
    printf("Stream Maximum Value\t: %d\n",(cat_cap.is_binary_encoded)?((1 <<cat_cap.cat_max_stream)-1):((cat_cap.cat_max_stream - 1) << 1));
    dag_config_dispose(card_ref);
    return;
}
/*--------------------------------------------------------------------
  FUNCTION :      fill_cat_capabilities
 DESCRIPTION :   To get the CAT capabilities and fill it in the structure passed
 PARAMETERS :    cat_component  -The CAT component.
                in_cap - (outward param) the structure into which the cap hs to be filled
  RETURNS :       -1 if failed 
                0 if success
---------------------------------------------------------------------*/
int32_t fill_cat_capabilities(dag_component_t cat_component, dagcat_capabilities_t *in_cap)
{
    int32_t ret_val = -1;
    /* get max hash value */
    ret_val = get_configured_hash_width(cat_component);
    if ( -1 == ret_val )
    {
        dagutil_error("Error in getting Max hash \n");
        return ret_val;
    }
    in_cap->cat_max_hash =  ( 1 << ret_val ) - 1;

    /* get max iface value */
    ret_val = cat_get_max_interface(cat_component);
    if ( -1 == ret_val )
    {
        dagutil_error("Error in getting Max iface \n");
        return ret_val;
    }
    in_cap->cat_max_iface =  ( 1 << ret_val ) - 1;
    
    /* get max color value */
    ret_val = cat_get_max_color(cat_component);
    if ( -1 == ret_val )
    {
        dagutil_error("Error in getting Max color \n");
        return ret_val;
    }
    in_cap->cat_max_color =  ( 1 << ret_val ) - 1;

    /* get max stream */
    ret_val = cat_get_no_of_output_bits(cat_component);
    if ( -1 == ret_val )
    {
        dagutil_error("Error in getting Max output \n");
        return ret_val;
    }
    in_cap->cat_max_stream =  ret_val;
    //Get the is_binary_encoded value 
    in_cap->is_binary_encoded = is_cat_output_binary_encoded(cat_component);
    /* one bit hot encoded */
    return 0;
}
/*--------------------------------------------------------------------
  FUNCTION :      initialize_a_cat_rule
 DESCRIPTION :   To initialize a CAT rule before getting a parsed data from the parser.
                The parameters are filled with the max ranges.
 PARAMETERS :    rule  -(outward parameter)Pointer to the rule which has to be initialized.
                cap - the structure which contains the max supported values
  RETURNS :       -1 if failed 
                0 if success
---------------------------------------------------------------------*/
int32_t initialize_a_cat_rule(dagcat_config_rule_p rule, const dagcat_capabilities_t* cap)
{
    if ( NULL == rule ) 
    {
        dagutil_error("Null pointers in %s\n", __FUNCTION__);
        return -1;
    }
    memset( rule , 0 ,sizeof (dagcat_config_rule_t));
    if ( NULL == cap )
    {
        dagutil_warning("CAT capabilities not given in %s\n",__FUNCTION__);
        return 0;
    }
    rule->hash_to   = cap->cat_max_hash;
    rule->color_to   = cap->cat_max_color;
    rule->iface_to   = cap->cat_max_iface;
    return 0;
}
/*--------------------------------------------------------------------
  FUNCTION :      validate_convert_bit_encoded_streams_to_binary
 DESCRIPTION :   To validate and convert bit encoded streams into binary.The parsed streams from the parser 
                is always bit encoded. So it CAT support binary encoded output, conversion is needed.
                Also checks if duplication of streams are there.
 PARAMETERS :    max_streams - The maximum supported stream bits.
                this_stream  -(inward & outward parameter)The stream number in bit-encoded format.
                
  RETURNS :       -1 if failed 
                0 if success
---------------------------------------------------------------------*/
int32_t validate_convert_bit_encoded_streams_to_binary(uint32_t max_streams, uint64_t *this_stream)
{
   /* input  is binary encoded .The rule->stream_value is encoded as one bit per stream *
    * Find the position at which the 1 occur and thats the stream number            *
    * If more than 1s occur, that is not supported                                  *
    */
    int position = -1;
    uint64_t  temp_stream = *this_stream;
    while(  temp_stream  )
    {
        position++;
        if ( temp_stream & 0x01)
        {
            temp_stream >>= 1;
            break;
        }
        temp_stream >>= 1;
    }
    if( 0 != temp_stream )
    {
        dagutil_error ("CAT does  not allow multiple streams\n");
        return -1;
    }
    /* position has the receive stream number */
    if ( position >=  (1 << max_streams))
    {
        dagutil_error("stream value is greater than maximum supported \n",(1 << max_streams));
        return -1;
    }
    if ( -1 != position )
        *this_stream = position;
    return 0;
}

/*--------------------------------------------------------------------
  FUNCTION :      validate_cat_rule
 DESCRIPTION :   Validates a parsed  CAT rule against the CAT capabilities.
 PARAMETERS :    rule  -Pointer to the rule which has to be validated.
                cap - the structure which contains the max supported values
  RETURNS :       -1 if failed 
                0 if success
---------------------------------------------------------------------*/
int32_t validate_cat_rule(const dagcat_config_rule_p rule, const dagcat_capabilities_t* cap)
{
	uint64_t max_streams = 1;
    if ( NULL == rule ) 
    {
        dagutil_error("Null pointers in %s\n", __FUNCTION__);
        return -1;
    }
    if ( NULL == cap )
    {
        dagutil_warning("CAT capabilities not given in %s\n",__FUNCTION__);
        return 0;
    }
    if( rule->color_to > cap->cat_max_color )
    {
        dagutil_error("color value is greater than maximum supported(%d) \n",cap->cat_max_color);
        return -1;
    }
    if( rule->hash_to > cap->cat_max_hash )
    {
        dagutil_error("hash value is greater than maximum supported (%d)\n", cap->cat_max_hash);
        return -1;
    }
    if( rule->iface_to > cap->cat_max_iface)
    {
        dagutil_error("iface value is greater than maximum supported (%d) \n",cap->cat_max_iface);
        return -1;
    }
    if ( 0 == cap->is_binary_encoded )
    {
        if ( rule->stream_value >= ( max_streams << cap->cat_max_stream ) )
        {
            dagutil_error("stream value is greater than maximum supported (%d)\n", (cap->cat_max_stream -1 ) * 2 );
            return -1;
        }
    }
    else
    {
        /* the parsed value is in bit encoded form. Validate it and convert it into binary */
        if ( -1 == validate_convert_bit_encoded_streams_to_binary(cap->cat_max_stream, &(rule->stream_value)))
        {
            dagutil_error("Invalid streams \n");
            return -1;
        }
    }
    return 0;
}

/*--------------------------------------------------------------------
  FUNCTION :      check_for_range_overlaps
 DESCRIPTION :   Checks the given rule has range overlaps with the already parsed rules.
 PARAMETERS :    this_head  -head node of the already parsed list.
                this_rule - the newly parsed rule which needs to be checked for any overlaps
  RETURNS :       -1 if overlaps 
                0 if not overlaps
---------------------------------------------------------------------*/
int check_for_range_overlaps(const dagcat_config_rule_p this_head, const dagcat_config_rule_p this_rule)
{
    dagcat_config_rule_p current = this_head;
    int count = 0;
    uint8_t color_overlaps =0;
    uint8_t iface_overlaps =0;
    uint8_t hash_overlaps =0;
    while ( NULL != current )
    {
        color_overlaps =0;
        iface_overlaps =0;
        hash_overlaps =0;
        count++;
        /* check for each range overlap.Could write a function for generic check */
        /*  check for color overlap */
        if ( !this_rule->is_color_inverted &&  !current->is_color_inverted )
        {
            if(IS_RANGES_OVERLAP(this_rule->color_from,this_rule->color_to,current->color_from,current->color_to))
            {
                color_overlaps = 1;
            }
        }
        else if( this_rule->is_color_inverted &&  current->is_color_inverted )
        {
            color_overlaps = 1;
        }
        else
        {
#if 0
            if(!IS_RANGES_EQUAL(this_rule->color_from,this_rule->color_to,current->color_from,current->color_to))
            {
                color_overlaps = 1;
            }
#endif
            if(!this_rule->is_color_inverted && !IS_RANGE_CONTAINMENT(this_rule->color_from,this_rule->color_to,current->color_from,current->color_to))
            {
                color_overlaps = 1;
            }
            else if(!current->is_color_inverted && !IS_RANGE_CONTAINMENT(current->color_from,current->color_to, this_rule->color_from,this_rule->color_to))
            {
                color_overlaps = 1;
            }
        }
        /*  check for iface overlap */
        if ( !this_rule->is_iface_inverted &&  !current->is_iface_inverted )
        {
            if(IS_RANGES_OVERLAP(this_rule->iface_from,this_rule->iface_to,current->iface_from,current->iface_to))
            {
                iface_overlaps = 1;
            }
        }
        else if( this_rule->is_iface_inverted &&  current->is_iface_inverted )
        {
            iface_overlaps = 1;
        }
        else
        {
            if(!this_rule->is_iface_inverted && !IS_RANGE_CONTAINMENT(this_rule->iface_from,this_rule->iface_to,current->iface_from,current->iface_to))
            {
                iface_overlaps = 1;
            }
            else if(!current->is_iface_inverted && !IS_RANGE_CONTAINMENT(current->iface_from,current->iface_to, this_rule->iface_from,this_rule->iface_to))
            {
                iface_overlaps = 1;
            }
        }
        /*  check for hash overlap */
        if ( !this_rule->is_hash_inverted &&  !current->is_hash_inverted )
        {
            if(IS_RANGES_OVERLAP(this_rule->hash_from,this_rule->hash_to,current->hash_from,current->hash_to))
            {
                hash_overlaps = 1;
            }
        }
        else if( this_rule->is_hash_inverted &&  current->is_hash_inverted )
        {
            hash_overlaps = 1;
        }
        else
        {
            if(!this_rule->is_hash_inverted && !IS_RANGE_CONTAINMENT(this_rule->hash_from,this_rule->hash_to,current->hash_from,current->hash_to))
            {
                hash_overlaps = 1;
            }
            else if(!current->is_hash_inverted && !IS_RANGE_CONTAINMENT(current->hash_from,current->hash_to, this_rule->hash_from,this_rule->hash_to))
            {
                hash_overlaps = 1;
            }
        }
#if 0
        if ( (this_rule->is_color_inverted == current->is_color_inverted )  && (IS_VALUE_IN_RANGE(this_rule->color_from, current->color_from, current->color_to) || 
        IS_VALUE_IN_RANGE(current->color_from,this_rule->color_from,this_rule->color_to) ))  
        {
            //dagutil_error("Color Range Overlaps with rule %d\n",count);
            if ( this_rule->is_color_inverted )
            color_overlaps = 1;
        }
        //else if ( (this_rule->is_color_inverted != current->is_color_inverted )  &&  !((this_rule->color_from == current->color_from) && (this_rule->color_to == current->color_to) ) )
        else if ( (this_rule->is_color_inverted != current->is_color_inverted )  &&  !(IS_VALUE_IN_RANGE(this_rule->color_from, current->color_from, current->color_to) || 
        IS_VALUE_IN_RANGE(current->color_from,this_rule->color_from,this_rule->color_to) ) )
        {
            //dagutil_error("Color Range Overlaps with rule %d\n",count);
            color_overlaps = 1;
        }
        if ( (this_rule->is_iface_inverted == current->is_iface_inverted )  && (IS_VALUE_IN_RANGE(this_rule->iface_from, current->iface_from, current->iface_to) || 
        IS_VALUE_IN_RANGE(current->iface_from,this_rule->iface_from,this_rule->iface_to) ))  
        {
            //dagutil_error("Color Range Overlaps with rule %d\n",count);
            iface_overlaps = 1;
        }
        else if ( (this_rule->is_iface_inverted != current->is_iface_inverted )  &&  !((this_rule->iface_from == current->iface_from) && (this_rule->iface_to == current->iface_to) ) )
        {
            //dagutil_error("Color Range Overlaps with rule %d\n",count);
            iface_overlaps = 1;
        }
        if ( (this_rule->is_hash_inverted == current->is_hash_inverted )  && (IS_VALUE_IN_RANGE(this_rule->hash_from, current->hash_from, current->hash_to) || 
        IS_VALUE_IN_RANGE(current->hash_from,this_rule->hash_from,this_rule->hash_to) ))  
        {
            //dagutil_error("Color Range Overlaps with rule %d\n",count);
            hash_overlaps = 1;
        }
        else if ( (this_rule->is_hash_inverted != current->is_hash_inverted )  &&  !((this_rule->hash_from == current->hash_from) && (this_rule->hash_to == current->hash_to) ) )
        {
            //dagutil_error("Color Range Overlaps with rule %d\n",count);
            hash_overlaps = 1;
        }
#endif 
        
        if ( color_overlaps && iface_overlaps && hash_overlaps)
        {
            dagutil_error("CAT rule at line#%u overlaps with a previous rule\n",dagcat_get_current_line_number()-1);
            if ( dagutil_get_verbosity() > 0 )
            {
                printf("Rule given in line  %d \t",dagcat_get_current_line_number()-1);
                printf("%sColor[%d-%d] %sHash[%d-%d] %sIface[%d-%d] Stream[0x%"PRIx64"]\n", (this_rule->is_color_inverted)?"Not":"", this_rule->color_from, this_rule->color_to,(this_rule->is_hash_inverted)?"Not":"", this_rule->hash_from, this_rule->hash_to, (this_rule->is_iface_inverted)?"Not":"",this_rule->iface_from, this_rule->iface_to, this_rule->stream_value);
                printf("Overlaps with Rule  %d \t", count);
                printf("%sColor[%d-%d] %sHash[%d-%d] %sIface[%d-%d] Stream[0x%"PRIx64"]\n",(current->is_color_inverted)?"Not":"", current->color_from, current->color_to,(current->is_hash_inverted)?"Not":"", current->hash_from, current->hash_to, (current->is_iface_inverted)?"Not":"",current->iface_from, current->iface_to, current->stream_value);
            }
            return -1;
        }
        current = current->next;
    }
    return 0;
}
/*--------------------------------------------------------------------
  FUNCTION :      parse_cat_rule_file
 DESCRIPTION :   Parse the given file and add them to a list 'head'ed by the input parameter.
 PARAMETERS :   fin - the opened file.
                cap - the CAT capabilities 
                head - (outward parameter) the 'head' of the list to hold the parsed list
  RETURNS :       -1 if failed 
                0 if  success
---------------------------------------------------------------------*/
int32_t parse_cat_rule_file(FILE *fin, const dagcat_capabilities_t* cap, dagcat_config_rule_p *head)
{
    int ret_val = -1;
    dagcat_config_rule_p tail = NULL;
    dagcat_config_rule_p current_rule = NULL;
    int rules_count = 0;

    /* initialize the external rule variable used by parser */
    initialize_a_cat_rule( &dagcat_config_rule, cap);
    dagcat_scanner_set_stdout(stdout);
	dagcat_parserestart(fin);
    while (1)
    {
        current_rule = NULL;
		ret_val = dagcat_parselex();
		if(ret_val == T_RULE_DONE)
		{
			rules_count ++;
		}
		else if (ret_val == T_RULE_CONTINUE)
		{
			printf("This state is unused please contact suppot@endace.com \
 and send the rule file used and this line print out. ret_val: %d rules_count:%d\n",ret_val,rules_count);
		}
		else if ( ret_val < 0 )
		{
			printf(" errors dagcat_parseflex returns: %d at rule: %d line# %d\n",ret_val,rules_count, dagcat_get_current_line_number());
			//break;//TODO should we stop or go ahead with parsed rules
            return -1;
		}
		else if (ret_val == 0)
		{
			break;
			
		} 
        else
         {
			printf("Unknown state please contact suppot@endace.com \
 and send the rule file used and this line print out. ret_val: %d rules_count:%d\n",ret_val,rules_count);
            return -1;
		}
		/* we have a syntactically correct rule here . Validate the rule */
		ret_val = validate_cat_rule(&dagcat_config_rule, cap);
        if ( -1 == ret_val )
        {
            dagutil_error("CAT rule validation failed at line#%u\n",dagcat_get_current_line_number()-1);
            return ret_val;
        }
        /* checks whether the newly parsed rule has any overlaps with the rules in the list */
        ret_val = check_for_range_overlaps(*head, &dagcat_config_rule);
        if ( -1 == ret_val )
        {
            dagutil_error("The file has rules with overlapping ranges\n");
            return ret_val;
        }
        /* we have valid rule here*/
        /* allocate and add to the list */
        current_rule = (dagcat_config_rule_p) malloc ( sizeof(dagcat_config_rule_t) );
        if ( NULL == current_rule )
        {
            dagutil_error(" Malloc failed ..\n");
            return -1;
        }
        memcpy(current_rule, &dagcat_config_rule, sizeof(dagcat_config_rule_t));
        if ( NULL == *head )
        {
            *head = current_rule;
        }
        else
        {
            tail->next = current_rule;
        }
        tail = current_rule;
        /* reset for the next rule */
		initialize_a_cat_rule( &dagcat_config_rule, cap);
	}
    dagutil_verbose( "Succesfully parsed %d rules\n",rules_count);
    return 0;
}
/*--------------------------------------------------------------------
  FUNCTION :      is_indices_valid_for_rule
 DESCRIPTION :   Checks if the given indices (color,hash iface) are applicable for this rule.
 PARAMETERS :   this_color - color index.
                this_hash - hash index 
                this_iface - iface index
                this_rule - given rule against which the indices are to be validated.
  RETURNS :       1  if  yes
                0 if  no
---------------------------------------------------------------------*/
uint8_t is_indices_valid_for_rule(uint32_t this_color, uint32_t this_hash, uint32_t this_iface, dagcat_config_rule_p this_rule)
{
    /* Using the X-OR logic. If 'inverted' and  Inside the range , return 0 */
    /* If 'not inverted' and 'outside the range', return 0 */
    if ( !(this_rule->is_color_inverted ^ IS_VALUE_IN_RANGE(this_color, this_rule->color_from, this_rule->color_to) ) )
        return 0;
    if ( !(this_rule->is_hash_inverted ^ IS_VALUE_IN_RANGE(this_hash, this_rule->hash_from, this_rule->hash_to) ) )
        return 0;
     if ( !(this_rule->is_iface_inverted ^ IS_VALUE_IN_RANGE(this_iface, this_rule->iface_from, this_rule->iface_to) ) )
        return 0;
    return 1;
}

/*--------------------------------------------------------------------
  FUNCTION :      apply_a_cat_rule
 DESCRIPTION :   Apply the rule to the CAT. 
 PARAMETERS :    cat_component  The CAT component.
                this_rule - The rule which is to be applied and which has atleast one inverted range.
  RETURNS :       -1 if failed 
                0 if success
---------------------------------------------------------------------*/
int32_t apply_a_cat_rule( dag_component_t cat_component, dagcat_config_rule_p this_rule)
{
    uint32_t color_index = 0;
    uint32_t iface_index = 0;
    uint32_t hash_index  = 0;
    //int destn_stream = 0x1;
    uint64_t ret_val = 0;
    for( color_index = this_rule->color_from; color_index <= this_rule->color_to; color_index++)
        for( hash_index = this_rule->hash_from; hash_index <= this_rule->hash_to; hash_index++)
            for( iface_index = this_rule->iface_from; iface_index <= this_rule->iface_to; iface_index++)
            {
                ret_val = cat_new_set_entry_verify(cat_component, iface_index, color_index, hash_index, this_rule->stream_value);
                if ( this_rule->stream_value != ret_val )
                {
                    dagutil_verbose_level(2, "Failed for Iface(%d)Color(%d)Hash(%d)Stream(%"PRIx64")\n",iface_index, color_index, hash_index, this_rule->stream_value);
                    dagutil_error("Setting CAT entry failed \n");
                    return -1;
                }
            }
    return 0;
}

/*--------------------------------------------------------------------
  FUNCTION :      apply_inverted_cat_rule
 DESCRIPTION :   Apply the rule to the CAT. The rule has atleast one inverted (not-ed) range.
                  This function could be optimized.Currently iterates thruough 0 to maxs of all the ranges 
                  and apply if the indices falls in the rule's ranges  
 PARAMETERS :    cat_component  The CAT component.
                this_rule - The rule which is to be applied and which has atleast one inverted range.
  RETURNS :       -1 if failed 
                0 if success
---------------------------------------------------------------------*/
int32_t apply_inverted_cat_rule( dag_component_t cat_component, dagcat_config_rule_p this_rule)
{
    uint32_t color_index = 0;
    uint32_t iface_index = 0;
    uint32_t hash_index  = 0;
    uint64_t ret_val = 0;
    dagcat_capabilities_t cat_cap;
    /* initilize the cat capabilities */
    memset (&cat_cap, 0, sizeof(cat_cap));
    ret_val = fill_cat_capabilities(cat_component, &cat_cap);
    if ( -1 == ret_val)
    {
        dagutil_error("Failed to get cat capabilities \n");
        return ret_val;
    }
    for( color_index = 0; color_index <= cat_cap.cat_max_color; color_index++)
        for( hash_index = 0; hash_index <= cat_cap.cat_max_hash; hash_index++)
            for( iface_index = 0; iface_index <= cat_cap.cat_max_iface; iface_index++)
            {
                if( is_indices_valid_for_rule(color_index, hash_index, iface_index, this_rule) )
                {
                    ret_val = cat_new_set_entry_verify(cat_component, iface_index, color_index, hash_index, this_rule->stream_value);
                    if ( this_rule->stream_value != ret_val )
                    {
                        dagutil_verbose_level(2, "Failed for Iface(%d)Color(%d)Hash(%d)Stream(%"PRIx64")\n",iface_index, color_index, hash_index, this_rule->stream_value);
                        dagutil_error("Setting CAT entry failed \n");
                        return -1;
                    }
                }
            }
    return 0;
}
/*--------------------------------------------------------------------
  FUNCTION :      is_any_inverted_range
 DESCRIPTION :   Checks whether the rule has any inverted range. 
 PARAMETERS :    in_rule - The cat rule
  RETURNS :      1 if  inverted
                0 if not inverted
---------------------------------------------------------------------*/
uint8_t is_any_inverted_range(dagcat_config_rule_p in_rule )
{
    return ( in_rule->is_color_inverted || in_rule->is_hash_inverted || in_rule->is_iface_inverted);
}
/*--------------------------------------------------------------------
  FUNCTION :      apply_cat_rules_list
 DESCRIPTION :   Apply the rule list to the CAT 
 PARAMETERS :   cat_component - The CAT component.
                head - the 'head' of the list.
  RETURNS :      1 if  failed
                0 if successded
---------------------------------------------------------------------*/
int32_t apply_cat_rules_list(dag_component_t cat_component, dagcat_config_rule_p head)
{
    int ret_val = -1;
    dagcat_config_rule_p current_rule = head;
    int rule_count = 1;
    if ( NULL == head )
    {
        dagutil_error("No rules \n");
        return -1;
    }
    while( current_rule )
    {
        if ( !is_any_inverted_range(current_rule) )
        {
            ret_val = apply_a_cat_rule(cat_component, current_rule );
        }
        else
        {
            dagutil_verbose_level(2, "Rule %d has inverted range(s) \n",rule_count);
            ret_val = apply_inverted_cat_rule(cat_component, current_rule );
        }
        if ( -1 == ret_val )
        {
            dagutil_error("Failed to apply rule %d\n", rule_count);
            return -1;
        }
        current_rule = current_rule->next;
        rule_count++;
    }
    return 0;
}
/*--------------------------------------------------------------------
  FUNCTION :      cleanup_cat_rules_list
 DESCRIPTION :   Clean up the parsed rule's list
 PARAMETERS :   head - the 'head' of the list.
  RETURNS :      None
---------------------------------------------------------------------*/
void cleanup_cat_rules_list(dagcat_config_rule_p head)
{
    dagcat_config_rule_p current_rule = head;
    dagcat_config_rule_p next_rule    = NULL;
    while( current_rule )
    {
        next_rule = current_rule->next;
        free (current_rule);
        current_rule = next_rule;
    }
}
/*--------------------------------------------------------------------
  FUNCTION :      display_cat_rules_list
 DESCRIPTION :   display the rules
 PARAMETERS :   head - the 'head' of the list.
  RETURNS :      None
---------------------------------------------------------------------*/
void display_cat_rules_list(dagcat_config_rule_p head)
{
    dagcat_config_rule_p current_rule = head;
    int rule_count = 1;
    while( current_rule )
    {
        printf("Rule:%d\t%sColor[%d-%d] %sHash[%d-%d] %sIface[%d-%d] Stream[0x%"PRIx64"]\n",rule_count++,(current_rule->is_color_inverted)?"Not":"   ", current_rule->color_from, current_rule->color_to,(current_rule->is_hash_inverted)?"Not":"   ", current_rule->hash_from, current_rule->hash_to, (current_rule->is_iface_inverted)?"Not":"   ",current_rule->iface_from, current_rule->iface_to, current_rule->stream_value);
        current_rule = current_rule->next;
    }
}
 /*--------------------------------------------------------------------
  FUNCTION :      configure_in_custom_mode
 DESCRIPTION :   To configure in a particular  mode
 PARAMETERS :    cat_component: The cat component
                filename - Name of the rule file, if customized mode. For other modes NULL
  RETURNS :       -1 if failed 
                0 if success
---------------------------------------------------------------------*/
int32_t configure_in_custom_mode( dag_component_t cat_component, char* filename)
{
    int32_t ret_val = 0;
    FILE * fin = NULL;
    dagcat_capabilities_t cat_cap;
    dagcat_config_rule_p head = NULL;
	
	fin = fopen(filename,"r");
	if( fin == NULL )
	{
		dagutil_error("File is missing or no access\n");
		return -1;
	}
    /* initilize the cat capabilities */
    memset (&cat_cap, 0, sizeof(cat_cap));
    ret_val = fill_cat_capabilities(cat_component, &cat_cap);
    if ( -1 == ret_val)
    {
        dagutil_error("Failed to get cat capabilities \n");
        return ret_val;
    }
    ret_val = parse_cat_rule_file( fin, &cat_cap, &head);
    if ( (-1 == ret_val) || ( NULL == head ) )
    {
        dagutil_error("Failed to parse the rule file \n");
        return ret_val;
    }
    dagutil_verbose_level(2, "Printing the rules \n");
    if ( dagutil_get_verbosity() > 1 )
        display_cat_rules_list(head);
    dagutil_verbose_level(1, "Clearing the CAT table entiries \n");
    ret_val = clear_cat_table_entries(cat_component);
    if (-1 == ret_val) 
    {
        dagutil_error("Failed to initialize the CAT table\n");
    }
    dagutil_verbose_level(1, "Applying the rules to the CAT table.\n");
    ret_val = apply_cat_rules_list ( cat_component, head );
    if (-1 == ret_val) 
    {
        dagutil_error("Failed to apply the rule file \n");
    }
    dagutil_verbose_level(1, "Cleaning up rule list.\n");
    if( NULL != head)
        cleanup_cat_rules_list(head);
    return ret_val;
}

#ifdef CATRULE_PARSE_TEST
 /*--------------------------------------------------------------------
  FUNCTION :      test_inverted_range_validity
 DESCRIPTION :   To test 
 ---------------------------------------------------------------------*/
int32_t test_inverted_range_validity( dagcat_capabilities_t *cat_cap, dagcat_config_rule_p head)
{
   
    uint32_t color_index = 0;
    uint32_t iface_index = 0;
    uint32_t hash_index  = 0;
    int ret_val = 0;
    dagcat_config_rule_p this_rule = head;
    
    while( this_rule )
   {
    for( color_index = 0; color_index <= cat_cap->cat_max_color; color_index++)
        for( hash_index = 0; hash_index <= cat_cap->cat_max_hash; hash_index++)
            for( iface_index = 0; iface_index <= cat_cap->cat_max_iface; iface_index++)
            {
                if( is_indices_valid_for_rule(color_index, hash_index, iface_index, this_rule) )
                {
                   // printf("%d   %d %d valid\n",color_index ,  hash_index, iface_index);
                }
            }
    this_rule = this_rule->next;
    }
}
/*--------------------------------------------------------------------
  FUNCTION :      test_inverted_range_validity
 DESCRIPTION :   To test 
 ---------------------------------------------------------------------*/
int32_t test_parsing( dag_component_t cat_component, char* filename)
{
    int32_t ret_val = 0;
    FILE * fin;
    dagcat_capabilities_t cat_cap;
    dagcat_config_rule_p head = NULL;
	
	fin = fopen(filename,"r");
	if( fin == NULL )
	{
		printf("File is missing or no access\n");
		return -1;
	}
    /* initilize the cat capabilities */
    memset (&cat_cap, 0, sizeof(cat_cap));
    cat_cap.cat_max_color = 15;
    cat_cap.cat_max_iface = 1;
    cat_cap.cat_max_hash = 4;
    cat_cap.cat_max_stream =  8;
    cat_cap.is_binary_encoded = 0;

    ret_val = parse_cat_rule_file( fin, &cat_cap, &head);
    if ( (-1 == ret_val) || ( NULL == head ) )
    {
        dagutil_error("Failed to parse the rule file \n");
        return ret_val;
    }
    printf("Printing the rules \n");
    display_cat_rules_list(head);
    test_inverted_range_validity(&cat_cap, head);
    if( NULL != head)
        cleanup_cat_rules_list(head);
    return ret_val;
}
#endif //CATRULE_PARSE_TEST
int
main(int argc, const char *argv[])
{
	ClArgPtr        clarg;
	int             result;
	FILE*           errorfile = NULL;
	int             argindex;
	int             code;
	char 		dagname_buf[DAGNAME_BUFSIZE];
    char        filename_buf[MAX_FILENAME_LEN];
    char        cat_mode_buffer[CAT_MODE_BUFSIZE];
    dagcat_config_mode_t config_mode = kCATModeInvalid;
    uint8_t query_cat_caps = 0;

	dagutil_set_progname("dagcat-setup");

	/* Set up the command line options. */
	clarg = dagclarg_init(argc, (const char* const *) argv);
	dagclarg_add(clarg, "This page.", "--help", 'h', CLA_HELP);
    dagclarg_add(clarg, "Queries the maximum values allowed for different parameters", "--query", 'q',CLA_QUERY_CAT_CAPS);
	dagclarg_add(clarg, "Display version information.", "--version", 'V', CLA_VERSION);
	dagclarg_add(clarg, "Increase verbosity.", "--verbose", 'v', CLA_VERBOSE);
	//dagclarg_add_int(clarg, "Mode(0-Dup3, 1-Z4, 2-Z8)", "--mode", 'm', "mode", &config_mode, CLA_MODE);
    dagclarg_add_string(clarg, "DAG card to use. default: /dev/dag0.", "--device", 'd', "device", dagname, DAGNAME_BUFSIZE, CLA_DEVICE);
    dagclarg_add_string(clarg, "Name of the file containing the rules to load.", "--rules", 'f', "filename", filename_buf, MAX_FILENAME_LEN, CLA_RULE_FILENAME);
    dagclarg_add_string(clarg,"Mode in which CAT should be configured in", "--mode", 'm',"mode", cat_mode_buffer, CAT_MODE_BUFSIZE, CLA_MODE);
	/* Parse the command line options. */
	result = dagclarg_parse(clarg, errorfile, &argindex, &code);

	while (1 == result)
	{
		switch (code)
		{
			case CLA_HELP:
				print_usage(clarg);
				return EXIT_SUCCESS;
				break;
	
			case CLA_VERBOSE:
				dagutil_inc_verbosity();
				errorfile = stderr;
				break;
	
			case CLA_VERSION:
				print_version();
				return EXIT_SUCCESS;
				break;
			case CLA_DEVICE:
			    break;				
            case CLA_MODE:
                if (kCATModeCustomRuleFile == config_mode )
                {
                    dagutil_error("mode not allowed. CAT rule file is specified already. So can not specify the mode\n");
                    return EXIT_FAILURE;
                }
                config_mode = string_to_dagcat_mode(cat_mode_buffer);
				if( (config_mode <= kCATModeInvalid) || (config_mode >= kCATModeLast) ) 
				{
					dagutil_error("Invalid CAT mode: ");
                    dagcat_print_supported_modes(stdout);
                    return EXIT_FAILURE;
				}
				break;
            case CLA_RULE_FILENAME:
                if (kCATModeInvalid != config_mode )
                {
                    dagutil_error("CAT mode specified already. So can not specify the rule file\n");
                    return EXIT_FAILURE;
                }
                config_mode = kCATModeCustomRuleFile;
                break;
            case CLA_QUERY_CAT_CAPS:
				query_cat_caps = 1;
				break;
			default:
				if ((0 == strcmp(argv[argindex], "--usage")) || (0 == strcmp(argv[argindex], "-?")))
				{
					print_usage(clarg);
					return EXIT_SUCCESS;
				}
				else if (argv[argindex][0] == '-')
				{
					/* Unknown option. */
					dagutil_error("unknown option %s\n", argv[argindex]); 
					dagclarg_display_usage(clarg, stdout);
					return EXIT_FAILURE;
				}
				break;
		};
		result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	};

	/* ClargPtr should no longer be necessary. */
	dagclarg_dispose(clarg);

    if (-1 == dag_parse_name(dagname,dagname_buf, DAGNAME_BUFSIZE, &dagstream))
	{
	   dagutil_panic("dag_parse_name(%s): %s\n",  dagname_buf,strerror(errno));
	}
    /* check whether   the query option is given */
    if ( 1 == query_cat_caps)
    {
        print_cat_query_capabilities (dagname_buf);
        return EXIT_SUCCESS;
    }
    if ( kCATModeInvalid == config_mode )
    {
        dagutil_error("Could not recoginze the mode. Please try %s --help\n",dagutil_get_progname());
        return -1;
    }
    dagutil_verbose_level(0,"Given Mode: %s  \n", dagcat_mode_to_string(config_mode));

#ifndef CATRULE_PARSE_TEST
    if ( -1 == configure_in_mode( dagname_buf, (dagcat_config_mode_t)config_mode,filename_buf ) )
    {
        dagutil_panic("The tool Failed to configure the CAT in the given mode\n");
    }
    else 
    {
        dagutil_verbose_level(0, "Successfully configured in %s mode\n",dagcat_mode_to_string(config_mode));
    }
#else
    /* Used to test the parsing of rule file */
     if ( -1 == test_parsing(NULL, filename_buf))
        printf("Faild parsing \n");
#endif
    return 0;
}

