#include "dagutil.h"
#include "dagapi.h"
#include "../include/attribute.h"
#include "../include/util/set.h"
#include "../include/card.h"
#include "../include/cards/card_initialization.h"
#include "../include/component.h"
#include "../include/util/utility.h"
#include "../include/cards/common_dagx_constants.h"
#include "../include/util/enum_string_table.h"
#include "../include/cards/dag71s_impl.h"
#include "../include/attribute_factory.h"
#include "../include/create_attribute.h"
#include "../include/components/phymod_component.h"
#include "../include/components/qa2225_framer_component.h"
#include "../include/dag_attribute_codes.h"
#include "../include/attribute_types.h"

#define kRegStatusControl 0x00

static dag_err_t phy_mode_update_register_base(ComponentPtr component);
static int phy_mode_post_initialize(ComponentPtr component);
static void phy_mode_default(ComponentPtr component);
static void phy_mode_dispose(ComponentPtr component);
static void phy_mode_reset(ComponentPtr component);

static void phyrate_set_value(AttributePtr attribute,void *value, int length);
static void* phyrate_get_value(AttributePtr attribute);
static void phyrate_to_string(AttributePtr attribute);
static void phyrate_from_string(AttributePtr attribute, const char* string);

Attribute_t phy_mode_attr[]=
{
	{
		/* Name */                 "phy_rate",
		/* Attribute Code */       kUint32AttributePhyRate,
		/* Attribute Type */       kAttributeUint32,
		/* Description */          "Changes the phy network protocol of the port to user specified value if it is supported,else remains unchanged.",
		/* Config-Status */        kDagAttrConfig,
		/* Index in register */    0,
		/* Register Address */     DAG_REG_PHY_MOD,
		/* Offset */               kRegStatusControl,
		/* Size/length */          1,
		/* Read */                 grw_iom_read,
		/* Write */                grw_iom_write,
		/* Mask */                 0xffffff,
		/* Default Value */        0,
		/* SetValue */             phyrate_set_value,
		/* GetValue */             phyrate_get_value,
		/* SetToString */          phyrate_to_string,
		/* SetFromString */        phyrate_from_string,
		/* Dispose */              attribute_dispose,
		/* PostInit */             attribute_post_initialize,
	}
};

#define NB_ELEM (sizeof(phy_mode_attr) / sizeof(Attribute_t))
ComponentPtr
phy_mod_get_new_component(DagCardPtr card, uint32_t index)
{
	ComponentPtr result = component_init(kComponentPhyMode, card);
	phy_mod_state_t* state = NULL;
	if (NULL != result)
	{
		component_set_dispose_routine(result, phy_mode_dispose);
		component_set_post_initialize_routine(result, phy_mode_post_initialize);
		component_set_reset_routine(result, phy_mode_reset);
		component_set_default_routine(result, phy_mode_default);
		component_set_update_register_base_routine(result, phy_mode_update_register_base);
		component_set_name(result,"Phy Rate Component.");
		component_set_description(result,"Phy Rate Component can report and alter the operating mode on each network interface of the card.");
		state = (phy_mod_state_t*)malloc(sizeof(phy_mod_state_t));
		state->mIndex = index;
		state->mPhyModeBase = (uint32_t*)(card_get_iom_address(card) + card_get_register_address(card, DAG_REG_PHY_MOD, state->mIndex));
		component_set_private_state(result, state);
	}
	return result;
}


static
dag_err_t phy_mode_update_register_base(ComponentPtr component)
{
	if (1 == valid_component(component))
	{
		phy_mod_state_t* state = NULL;
		DagCardPtr card;
		state = component_get_private_state(component);
		card = component_get_card(component);
		NULL_RETURN_WV(state, kDagErrGeneral);
		state->mPhyModeBase = (uint32_t*)(card_get_iom_address(card) + card_get_register_address(card, DAG_REG_PHY_MOD, state->mIndex));
		return kDagErrNone;
	}
	return kDagErrInvalidParameter;
}


static
int phy_mode_post_initialize(ComponentPtr component)
{
	if (1 == valid_component(component))
	{
		DagCardPtr card = NULL;
		phy_mod_state_t* state = NULL;
		/* Get card reference */
		card = component_get_card(component);
		/* Get counter state structure */
		state = component_get_private_state(component);
		/*the base address*/
		state->mPhyModeBase = (uint32_t*)(card_get_iom_address(card) + card_get_register_address(card, DAG_REG_PHY_MOD, state->mIndex));
		/* Add attribute of counter */
		read_attr_array(component, phy_mode_attr, NB_ELEM, state->mIndex);

		return 1;
	}
	return kDagErrInvalidParameter;
}


void phy_mode_default(ComponentPtr component)
{
	if (1 == valid_component(component))
	{
		GenericReadWritePtr phyrate_grw = NULL;
		line_rate_t line_rate = kLineRateInvalid;
		uint32_t register_value = 0;
		uint32_t attempts = 0;
		uint32_t phy_rate = 0;
		AttributePtr attribute = NULL;
		phy_mod_state_t* state = NULL;
		state = component_get_private_state(component);
		if (1 == valid_component(component))
		{
			if(1 == ten_gigabits_firmware_loaded(state->mIndex))
			{
				line_rate = kLineRateEthernet10GE;
			}else
			{
				line_rate = kLineRateEthernet1000;
			}
			attribute = component_get_attribute(component,kUint32AttributePhyRate);
			phy_rate = line_rate_to_phy_mode_rate(line_rate);
			phyrate_grw = attribute_get_generic_read_write_object(attribute);
			/*Query if the user specified rate is supported.*/
			register_value = phy_rate;
			grw_write(phyrate_grw,register_value);
			register_value = grw_read(phyrate_grw);
			if(((register_value & 0xff) == phy_rate) && (((register_value & 0xff00) >> 8) == 0xff))
			{
				/*user specified phyrate is supported by all the data path segments.so change the mode.*/
				register_value = phy_rate;
				register_value |= (0xff <<8);
				grw_write(phyrate_grw,register_value);
				register_value = grw_read(phyrate_grw);
				while ((((register_value & 0xff0000) >> 16) != phy_rate)&& (attempts < 100))
				{
					attempts++;
					dagutil_microsleep(100);
					register_value = grw_read(phyrate_grw);
				}
				if(attempts == 100)
				{
					dagutil_warning("Cannot switch to user specified line rate as all data path segments do not support it.\n");
				}
			}
			else
			{
				/*user specified phyrate is not supported by all the data path segments.*/
				/*Do nothing.*/
			}
		}
	}
}


void phy_mode_dispose(ComponentPtr component)
{
}


void phy_mode_reset(ComponentPtr component)
{

}


void phyrate_set_value(AttributePtr attribute,void *value, int length)
{
	GenericReadWritePtr phyrate_grw = NULL;
	line_rate_t line_rate;
	ComponentPtr component = NULL;
	ComponentPtr root = NULL;
	ComponentPtr xgmii = NULL;
	AttributePtr mac_wan_mode = NULL;
	uint32_t register_value = 0;
	uint32_t attempts = 0;
	uint32_t phy_rate = 0;
	uint8_t wan_mode_val = 0;
	phy_mod_state_t* state = NULL;
	if (1 == valid_attribute(attribute))
	{
		line_rate = *(line_rate_t*)value;
		if(line_rate == kLineRateWAN)
		{
			wan_mode_val = 1;
		}
		else
		{
			wan_mode_val = 0;
		}
		component = attribute_get_component(attribute);
		state = component_get_private_state(component);
		root = component_get_parent(component);
		xgmii = component_get_subcomponent(root,kComponentXGMII,state->mIndex);
		mac_wan_mode = component_get_attribute(xgmii,kBooleanAttributeMacWanMode);
		attribute_set_value(mac_wan_mode,&wan_mode_val,1);

		phy_rate = line_rate_to_phy_mode_rate(line_rate);
		phyrate_grw = attribute_get_generic_read_write_object(attribute);
		/*Query if the user specified rate is supported.*/
		register_value = phy_rate;
		grw_write(phyrate_grw,register_value);
		register_value = grw_read(phyrate_grw);
		while ((((register_value & 0xff00) >> 8) != 0xff) && (attempts < 100))
		{
			attempts++;
			dagutil_microsleep(100);
			register_value = grw_read(phyrate_grw);
		}
		if(attempts == 100)
		{
			dagutil_warning("All data path segments do not support the user specified line_rate code 0x%2x \n",((register_value & 0xff00) >> 8));
			return;
		}
		if(((register_value & 0xff) == phy_rate))
		{
			/*user specified phyrate is supported by all the data path segments.so change the mode.*/
			attempts = 0;
			register_value = phy_rate;
			register_value |= (0xff << 8);
			grw_write(phyrate_grw,register_value);
			register_value = grw_read(phyrate_grw);
			while ((((register_value & 0xff0000) >> 16) != phy_rate)&& (attempts < 100))
			{
				attempts++;
				dagutil_microsleep(100);
				register_value = grw_read(phyrate_grw);
			}
			if(attempts == 100)
			{
				dagutil_panic("Not able to switch to the user specified line rate.\n");
			}
		}
	}
}


void phyrate_from_string(AttributePtr attribute, const char* string)
{
	line_rate_t line_rate;
	if (1 == valid_attribute(attribute))
	{

		line_rate = string_to_line_rate(string);
		attribute_set_value(attribute,&line_rate,1);
	}
}


void* phyrate_get_value(AttributePtr attribute)
{
	uint32_t register_value = 0;
	line_rate_t line_rate;
	GenericReadWritePtr current_phymode_grw = NULL;
	uint32_t *masks;

	if (1 == valid_attribute(attribute))
	{
		current_phymode_grw = attribute_get_generic_read_write_object(attribute);
		assert(current_phymode_grw);
		register_value = grw_read(current_phymode_grw);
		masks = attribute_get_masks(attribute);
		register_value &= (*masks);
		register_value = (register_value >> 16);
		line_rate = phy_mode_rate_to_line_rate(register_value);
		attribute_set_value_array(attribute, &line_rate, sizeof(line_rate));
		return (void *)attribute_get_value_array(attribute);
	}
	return NULL;
}


void phyrate_to_string(AttributePtr attribute)
{
	uint32_t value = 0;
	char *line_rate_string = (char*)malloc(64);

	if (1 == valid_attribute(attribute))
	{
		value = *(uint32_t*)attribute_get_value(attribute);
		line_rate_string = (char*)line_rate_to_string(value);
		attribute_set_to_string(attribute,line_rate_string);
	}
}
