/***********************************************************************
 * 
 *  Copyright (C) 2005-2006 Novell, Inc. All Rights Reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; version 2.1
 *  of the License.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, Novell, Inc.
 * 
 *  To contact Novell about this file by physical or electronic mail, 
 *  you may find current contact information at www.novell.com.
 * 
 ***********************************************************************/

#include <stdlib.h>
#include <memory.h>
#include "sscs_lldefs.h"


/*  */
/*
 * NAME - _ll_CreateLink 
 *
 * DESCRIPTION
 *	This function will create a link to be used in a list.
 *		   
 */
LL_LINK_T		*_ll_CreateLink(uint32_t			itemSize)
{ /* beginning of the call */
/* ########################## DECLARATIONS START HERE ######################### */

	/* allocate the new link */
	LL_LINK_T		*newLink = (LL_LINK_T *)(malloc(sizeof(LL_LINK_T) + itemSize));

/* ############################## CODE STARTS HERE ############################ */

	if(newLink)
	{
		/* clear out the allocated buffer */
		memset(newLink, 0 , sizeof(sizeof(LL_LINK_T)+itemSize));
		newLink->item = (((uint8_t *)newLink) + sizeof(LL_LINK_T));
		return(newLink);
	}

	return(NULL);

/* ############################### CODE ENDS HERE ############################# */
}	/* end of _ll_CreateLink */



/*  */
/*
 * NAME	- ll_RemoveCurrentLink
 *
 * DESCRIPTION
 *	
 *		   
 */
void	ll_RemoveCurrentLink(LL_LINKLIST_T		*list)
{ /* beginning of the call */
/* ######################## DECLARATIONS START HERE ######################## */

	int				i;
	LL_LINK_T		*prev = NULL, *targetLink = NULL;

/* ########################### CODE STARTS HERE ############################ */

	if(list->elemCount)
	{
		// save the link to delete
		targetLink = list->clp;
		// start at the head
		list->clp = list->head;

		for(i = 0; i < (int)list->elemCount; i++)
		{
			if(list->clp == targetLink)
			{
				if(prev)
				{
					prev->next = list->clp->next;
					list->clp = prev;
				}
				else
				{
					list->head = list->head->next;
					list->clp = list->head;
				}

				free(targetLink);
				list->elemCount--;
				break;
			}
			else
			{
				prev = list->clp;
				list->clp = list->clp->next;
			}
		}
	}
		
/* ############################### CODE ENDS HERE ############################# */
}	/* end of ll_RemoveCurrentLink */





/*  */
/*
 * NAME	- ll_InsertSharedSecretLink
 *
 * DESCRIPTION
 *	This function inserts a new link into the link list after the
 *	CLP. If the CLP is pointing to the tail of the list the new
 *	link will become the new tail for the list. The new link will
 *	become the CLP and the index will be set to CLP.
 *		   
 */
int	ll_InsertSharedSecretLink
		(
			LL_LINKLIST_T			*list,
			uint32_t				kLen,
			SS_UTF8_T				*key,
			uint32_t				vLen,
			uint8_t					*value
		)
{ /* beginning of the call */
/* ########################## DECLARATIONS START HERE ######################################## */
    
	uint32_t			keyLen = sscs_Utf8StrSize(key);
	LL_LINK_T			*newLink =  _ll_CreateLink(sizeof(LL_SHSEC_KEY_VAL_T) + keyLen + vLen);
	LL_SHSEC_KEY_VAL_T  *sharedLink;

/* ############################## CODE STARTS HERE ########################################### */
   
	if(newLink)
	{
		if(list->elemCount)
		{
			list->clp = list->head;
			/* check for duplicates */
			do
			{
				// check to see if the key exists
				if(((ll_GetSHSecKeyLen(list) == kLen) &&	
					(memcmp(ll_GetSHSecKey(list), key, ll_GetSHSecKeyLen(list))) == 0))
				{
					// the key exists now check for the value
					if((ll_GetSHSecValLen(list) == vLen) && 
						(memcmp(ll_GetSHSecVal(list), value, ll_GetSHSecValLen(list)) == 0))
					{
						/* element already in the list */
						free(newLink);
						return(NSSCS_LL_SUCCESS);
					}
					else
					{
						// free the link with old valude
						ll_RemoveCurrentLink(list);
						break;
					}
				}

				/* move forward one link */
				if(list->clp->next)
				{
					list->clp = list->clp->next;
					continue;
				}
				else
				{
					/* end of the list not found */
					break;
				}
			} while(TRUE);

			if (list->clp)
			{
				list->clp->next = newLink;
				/* reset the clp to the new link */
				list->clp = newLink;
				(list->elemCount)++;
			}
			else
			{
				list->head = list->clp = newLink;
				list->elemCount = 1;				
			}
		}
		else
		{
			list->head = list->clp = newLink;
			list->elemCount = 1;
		}
		
		/* copy linkData to the link */
		sharedLink = (LL_SHSEC_KEY_VAL_T*)list->clp->item;
		sharedLink->kLen = kLen;
		sharedLink->vLen = vLen;
		sharedLink->key = (SS_UTF8_T *)(((uint8_t *)sharedLink) + sizeof(LL_SHSEC_KEY_VAL_T));
		sharedLink->value = (((uint8_t *)sharedLink) + sizeof(LL_SHSEC_KEY_VAL_T)) + keyLen;

		sscs_Utf8Strncpy(sharedLink->key, key, kLen);
		memcpy(sharedLink->value, value, vLen);

		return(NSSCS_LL_SUCCESS);
	}
	else	
	{
		return(NSSCS_E_LL_SYS_FAILURE);		
	}

/* ############################### CODE ENDS HERE ############################# */
}	/* end of ll_InsertSharedSecretLink */




/*  */
/*
 * NAME	- ll_RemoveSharedSecretLink
 *
 * DESCRIPTION
 *	This function removes a Shared Secret link from the link list at the
 *	CLP. 
 *		   
 */
int	ll_RemoveSharedSecretLink
		(
			LL_LINKLIST_T			*list,
			uint32_t				kLen,
			SS_UTF8_T				*key
		)
{ /* beginning of the call */
/* ########################## DECLARATIONS START HERE ######################### */

	LL_LINK_T			*prev = NULL;

/* ############################## CODE STARTS HERE ############################ */

	if(list->elemCount)
	{
		list->clp = list->head;
		/* check for duplicates */
		do
		{
			// keys should be unique
			if(((ll_GetSHSecKeyLen(list) == kLen) &&	
					(memcmp(ll_GetSHSecKey(list), key, ll_GetSHSecKeyLen(list))) == 0))
			{
				/* element already in the list */
				if(prev)
				{
					prev->next = list->clp->next;
					free(list->clp);
					list->clp = prev;
				}
				else
				{
					list->head = list->head->next;
					free(list->clp);
					list->clp = list->head;
				}

				list->elemCount--;
				return(NSSCS_LL_SUCCESS);
			}

			/* move forward one link */
			if(list->clp->next)
			{
				prev = list->clp;
				list->clp = list->clp->next;
				continue;
			}
			else
			{
				/* end of the list item not found */
				return(NSSCS_LL_SUCCESS);
			}
		} while(TRUE);
	}

	return(NSSCS_E_LL_SYS_FAILURE);		

/* ############################### CODE ENDS HERE ############################# */
}	/* end of ll_RemoveSharedSecretLink */




/*  */
/*
 * NAME	- ll_DestroyList
 *
 * DESCRIPTION
 *	This function completely destroy a linked list by freeing the 
 *	memory for all the links.
 *		   
 */
void		ll_DestroyList(LL_LINKLIST_T		*list)
{ /* beginning of the call */
/* ############################## CODE STARTS HERE ############################ */

	/* kill the links in the link list */
		while(list->elemCount)
		{
			list->clp = list->head;
			list->head = list->head->next;
			free(list->clp);
			list->elemCount--;
		}

/* ############################### CODE ENDS HERE ############################# */
}	/* end of ll_DestroyList */




/*  */
/*
 * NAME	- ll_Next
 *
 * DESCRIPTION
 *	This function moves the CLP to point to the next
 *	link in the list and returns a TRUE or FALSE based on the
 *	success or failure (when it reaches the end of the list) of the 
 *	operation.
 *		   
 */
int		ll_Next(LL_LINKLIST_T	 		*list)
{ /* beginning of the call */
/* ############################## CODE STARTS HERE ############################ */

	if(list)
	{
		if(list->elemCount)
		{
			if(list->clp->next)
			{
				list->clp = list->clp->next;
				return(TRUE);	/* if not at the tail of the list */
			}
		}
	}

	return(FALSE);		/* cannot go beyond the tail of the list */

/* ############################### CODE ENDS HERE ############################# */
}	/* end of ll_Next */





/*  */
/*
 * NAME	- ll_InsertNewLink
 *
 * DESCRIPTION
 *	Cache in a set of ds items.
 *		   
 */
void	*ll_InsertNewLink
		(
			LL_LINKLIST_T			*list,
			uint32_t				itemSize
		)
{ /* beginning of the call */
/* ########################## DECLARATIONS START HERE ######################### */

	LL_LINK_T				*newLink = _ll_CreateLink(itemSize);

/* ############################## CODE STARTS HERE ############################ */

	if(newLink)
	{
		if(list->elemCount)
		{
			list->clp = list->head;
			/* check for duplicates */
			while(list->clp->next)
			{
				list->clp = list->clp->next;
			}

			list->clp->next = newLink;
			/* reset the clp to the new link */
			list->clp = newLink;
			(list->elemCount)++;
		}
		else
		{
			list->head = list->clp = newLink;
			list->elemCount = 1;
		}
		


		return(newLink->item);
	}
	else	
	{
		return(NULL);		
	}

/* ############################### CODE ENDS HERE ############################# */
}	/* end of ll_InsertNewLink */






/*  */
/*
 * NAME	- ll_GetEntry
 *
 * DESCRIPTION
 *	This function returns a pointer to the current item
 *	in the list or returns NULL
 *		   
 */
void*	ll_GetEntry(LL_LINKLIST_T	 		*list)
{ /* beginning of the call */
/* ############################## CODE STARTS HERE ############################ */

	if(list)
	{
		if(list->elemCount)
		{
			if(list->clp->item)
			{
				return list->clp->item;
			}
		}
	}

	return NULL;

/* ############################### CODE ENDS HERE ############################# */
}	/* end of ll_GetEntry */