/***************************************************************************************
 *
 *  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
 *
 *  By downloading, copying, installing or using the software you agree to this license.
 *  If you do not agree to this license, do not download, install, 
 *  copy or use the software.
 *
 *  Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
 *
 *  Redistribution and use in binary forms, with or without modification, are permitted.
 *
 *  Unless required by applicable law or agreed to in writing, software distributed 
 *  under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 *  CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
 *  language governing permissions and limitations under the License.
 *
****************************************************************************************/

#include "sys_inc.h"
#include "linked_list.h"


/************************************************************************************/
HT_API LINKED_LIST * h_list_create(BOOL bNeedMutex)
{
	LINKED_LIST * p_linked_list;

	p_linked_list = (LINKED_LIST*) malloc(sizeof(LINKED_LIST));
	if (p_linked_list == NULL)
	{
		return NULL;
	}
		
	p_linked_list->p_first_node = NULL;
	p_linked_list->p_last_node = NULL;

	if (bNeedMutex)
	{
		p_linked_list->list_semMutex = sys_os_create_mutex();
	}	
	else
	{
		p_linked_list->list_semMutex = NULL;
	}
	
	return p_linked_list;
}

/************************************************************************************/
HT_API void h_list_get_ownership(LINKED_LIST * p_linked_list)
{
	if (p_linked_list->list_semMutex)
	{
		sys_os_mutex_enter(p_linked_list->list_semMutex);
	}
}

HT_API void h_list_giveup_ownership(LINKED_LIST * p_linked_list)
{
	if (p_linked_list->list_semMutex)
	{
		sys_os_mutex_leave(p_linked_list->list_semMutex);
	}
}

/************************************************************************************/
HT_API void h_list_free_container(LINKED_LIST * p_linked_list)
{
	LINKED_NODE * p_node;
	LINKED_NODE * p_next_node;

	if (p_linked_list == NULL)
	{
		return;
	}
	
	h_list_get_ownership(p_linked_list);

	p_node = p_linked_list->p_first_node;

	while (p_node != NULL) 
	{
		void * p_free;

		p_next_node = p_node->p_next;
		
		p_free = p_node->p_data;

		if (p_free != NULL)
		{
			free(p_free);
		}
		
		free (p_node);

		p_node = p_next_node;		
	}

	h_list_giveup_ownership(p_linked_list);

	if (p_linked_list->list_semMutex)
	{
		sys_os_destroy_sig_mutex(p_linked_list->list_semMutex);
	}

	free (p_linked_list);
}

/************************************************************************************/
HT_API void h_list_free_all_node(LINKED_LIST * p_linked_list)
{
	LINKED_NODE * p_node;
	LINKED_NODE * p_next_node;

	if (p_linked_list == NULL)
	{
		return;
	}
	
	h_list_get_ownership(p_linked_list);

	p_node = p_linked_list->p_first_node;

	while (p_node != NULL) 
	{
		p_next_node = p_node->p_next;
		
		if (p_node->p_data != NULL)
		{
			free(p_node->p_data);
		}
		
		free (p_node);

		p_node = p_next_node;		
	}
	
	p_linked_list->p_first_node = NULL;
	p_linked_list->p_last_node = NULL;

	h_list_giveup_ownership(p_linked_list);	
}

/************************************************************************************/
HT_API BOOL h_list_add_at_front(LINKED_LIST * p_linked_list, void * p_item)
{
	LINKED_NODE * p_node;
	LINKED_NODE * p_next_node;

	if (p_linked_list == NULL)
	{
		return FALSE;
	}
	
	if (p_item == NULL)
	{
		return FALSE;
	}
	
	p_node = (LINKED_NODE*) malloc(sizeof(LINKED_NODE));
	if (p_node == NULL)
	{
		return FALSE;
	}
	
	p_node->p_next = NULL;
	p_node->p_previous = NULL;
	p_node->p_data = p_item;

	h_list_get_ownership(p_linked_list);

	if (p_linked_list->p_first_node == NULL)
	{
		p_linked_list->p_first_node = p_node;
		p_linked_list->p_last_node = p_node;

		p_node->p_previous = NULL;
		p_node->p_next = NULL;
	}
	else
	{
		p_next_node = p_linked_list->p_first_node;

		p_node->p_next = p_next_node;
		p_node->p_previous = NULL;

		p_next_node->p_previous = p_node;
		p_linked_list->p_first_node = p_node;
	}

	h_list_giveup_ownership(p_linked_list);
	
	return TRUE;
}

/************************************************************************************/
HT_API void h_list_remove_from_front(LINKED_LIST * p_linked_list)
{
	LINKED_NODE * p_node_to_remove;

	if (p_linked_list == NULL)
	{
		return;
	}
	
	h_list_get_ownership(p_linked_list);

	p_node_to_remove = p_linked_list->p_first_node;

	if (p_node_to_remove == NULL) 
	{
		h_list_giveup_ownership(p_linked_list);
		return;
	}

	if (p_linked_list->p_first_node == p_linked_list->p_last_node)
	{
		p_linked_list->p_first_node = NULL;
		p_linked_list->p_last_node = NULL;
	}
	else
	{
		p_linked_list->p_first_node = p_node_to_remove->p_next;
		p_linked_list->p_first_node->p_previous = NULL;
	}

	free(p_node_to_remove);
	
	h_list_giveup_ownership(p_linked_list);
}

HT_API void h_list_remove_from_front_no_lock(LINKED_LIST * p_linked_list)
{
	LINKED_NODE * p_node_to_remove;

	if (p_linked_list == NULL)	
	{
		return;
	}
	
	p_node_to_remove = p_linked_list->p_first_node;

	if (p_node_to_remove == NULL) 
	{
		return;
	}

	if (p_linked_list->p_first_node == p_linked_list->p_last_node)
	{
		p_linked_list->p_first_node = NULL;
		p_linked_list->p_last_node = NULL;
	}
	else
	{
		p_linked_list->p_first_node = p_node_to_remove->p_next;
		p_linked_list->p_first_node->p_previous = NULL;
	}

	free (p_node_to_remove);
}

/************************************************************************************/
HT_API BOOL h_list_add_at_back(LINKED_LIST * p_linked_list, void * p_item)
{
	LINKED_NODE * p_node;
	LINKED_NODE * p_previous_node;

	if (p_linked_list == NULL)
	{
		return FALSE;
	}
	
	if (p_item == NULL)
	{
		return FALSE;
	}
	
	p_node = (LINKED_NODE*) malloc(sizeof(LINKED_NODE));
	if (p_node == NULL)
	{
		return FALSE;
	}
	
	p_node->p_next = NULL;
	p_node->p_previous = NULL;
	p_node->p_data = (void*) p_item;

	h_list_get_ownership(p_linked_list);

	if (p_linked_list->p_last_node == NULL)
	{
		p_linked_list->p_last_node = p_node;
		p_linked_list->p_first_node = p_node;
	
		p_node->p_next = NULL;
		p_node->p_previous = NULL;
	}
	else
	{
		p_previous_node = p_linked_list->p_last_node;

		p_node->p_next = NULL;
		p_node->p_previous = p_previous_node;

		p_previous_node->p_next = p_node;

		p_linked_list->p_last_node = p_node;
	}

	h_list_giveup_ownership(p_linked_list);

	return TRUE;
}

/************************************************************************************/
HT_API void h_list_remove_from_back(LINKED_LIST * p_linked_list)
{
	LINKED_NODE * p_node_to_remove;

	if (p_linked_list == NULL)
	{
		return;
	}
	
	h_list_get_ownership(p_linked_list);

	if (p_linked_list->p_last_node == NULL)	
	{
		h_list_giveup_ownership(p_linked_list);
		return;
	}
		
	if (p_linked_list->p_first_node == p_linked_list->p_last_node)
	{
		p_node_to_remove = p_linked_list->p_first_node;

		p_linked_list->p_first_node = NULL;
		p_linked_list->p_last_node = NULL;

		free(p_node_to_remove);

		h_list_giveup_ownership(p_linked_list);
		return;
	}
		
	p_node_to_remove = p_linked_list->p_last_node;
	
	p_linked_list->p_last_node = p_node_to_remove->p_previous;

	p_linked_list->p_last_node->p_next = NULL;
	
	free(p_node_to_remove);

	p_node_to_remove = NULL;
	
	h_list_giveup_ownership(p_linked_list);
}

HT_API BOOL h_list_remove(LINKED_LIST * p_linked_list, LINKED_NODE * p_node)
{
	LINKED_NODE * p_previous_node;
	LINKED_NODE * p_next_node;

	if ((p_linked_list == NULL) || (p_node == NULL))
	{
		return FALSE;
	}

	p_previous_node = p_node->p_previous;
	
	p_next_node = p_node->p_next;
	
	if (p_previous_node != NULL)
	{
		p_previous_node->p_next = p_next_node;
	}	
	else
	{
		p_linked_list->p_first_node = p_next_node;
	}

	if (p_next_node != NULL)
	{
		p_next_node->p_previous = p_previous_node;
	}	
	else
	{
		p_linked_list->p_last_node = p_previous_node;
	}
	
	free (p_node);

	return TRUE;				
}

HT_API BOOL h_list_remove_data(LINKED_LIST * p_linked_list, void * p_data)
{
	LINKED_NODE * p_previous_node;
	LINKED_NODE * p_next_node;
	LINKED_NODE * p_node;

	if ((p_linked_list == NULL) || (p_data == NULL))
	{
		return FALSE;
	}
	
	h_list_get_ownership(p_linked_list);

	p_node = p_linked_list->p_first_node;

	while (p_node != NULL)
	{
		if (p_data == p_node->p_data)
		{
			break;
		}
		
		p_node = p_node->p_next;
	}

	if (p_node == NULL)
	{
		h_list_giveup_ownership(p_linked_list);
		return FALSE;
	}

	p_previous_node = p_node->p_previous;
	
	p_next_node = p_node->p_next;
	
	if (p_previous_node != NULL)
	{
		p_previous_node->p_next = p_next_node;
	}	
	else
	{
		p_linked_list->p_first_node = p_next_node;
	}

	if (p_next_node != NULL)
	{
		p_next_node->p_previous = p_previous_node;
	}	
	else
	{
		p_linked_list->p_last_node = p_previous_node;
	}
	
	free(p_node);

	h_list_giveup_ownership(p_linked_list);

	return TRUE;				
}

/************************************************************************************/
HT_API uint32 h_list_get_number_of_nodes(LINKED_LIST * p_linked_list)
{
	uint32 number_of_nodes;
	LINKED_NODE * p_node;

    if (NULL == p_linked_list)
    {
        return 0;
    }
    
	h_list_get_ownership(p_linked_list);

	p_node = p_linked_list->p_first_node;

	number_of_nodes = 0;
	
	while (p_node !=  NULL)
	{
		++ number_of_nodes;
		p_node = p_node->p_next;
	}

	h_list_giveup_ownership(p_linked_list);

	return number_of_nodes;
}

/************************************************************************************/
HT_API BOOL h_list_insert(LINKED_LIST * p_linked_list, LINKED_NODE * p_pre_node, void *p_item)
{
	if ((p_linked_list == NULL) || (p_item == NULL))
	{
		return FALSE;
	}
	
	if (p_pre_node == NULL)
	{
		h_list_add_at_front(p_linked_list, p_item);
	}	
	else
	{
		if (p_pre_node->p_next == NULL)
		{
			h_list_add_at_back(p_linked_list, p_item);
		}	
		else
		{
			LINKED_NODE * p_node = (LINKED_NODE *)malloc(sizeof(LINKED_NODE));
			if (NULL == p_node)
			{
			    return FALSE;
			}
		
			h_list_get_ownership(p_linked_list);

			p_node->p_data = p_item;
			p_node->p_next = p_pre_node->p_next;
			p_node->p_previous = p_pre_node;
			p_pre_node->p_next->p_previous = p_node;
			p_pre_node->p_next = p_node;
		
			h_list_giveup_ownership(p_linked_list);
		}
	}

	return TRUE;
}

/***********************************************************************/
HT_API LINKED_NODE * h_list_lookup_start(LINKED_LIST * p_linked_list)
{
	if (p_linked_list == NULL)
	{
		return NULL;
	}
	
	h_list_get_ownership(p_linked_list);

	if (p_linked_list->p_first_node)
	{
		return p_linked_list->p_first_node;
	}

	return NULL;
}

HT_API LINKED_NODE * h_list_lookup_next(LINKED_LIST * p_linked_list, LINKED_NODE * p_node)
{
	if (p_node == NULL)
	{
		return NULL;
	}
	
	return p_node->p_next;
}

HT_API void h_list_lookup_end(LINKED_LIST * p_linked_list)
{
	if (p_linked_list == NULL)
	{
		return;
	}
	
	h_list_giveup_ownership(p_linked_list);
}

HT_API LINKED_NODE * h_list_lookback_start(LINKED_LIST * p_linked_list)
{
	if (p_linked_list == NULL)
	{
		return NULL;
    }
    
	h_list_get_ownership(p_linked_list);

	if (p_linked_list->p_last_node)
	{
		return p_linked_list->p_last_node;
	}

	return NULL;
}

HT_API LINKED_NODE * h_list_lookback_next(LINKED_LIST * p_linked_list, LINKED_NODE * p_node)
{
	if (p_node == NULL)
	{
		return NULL;
    }
    
	return p_node->p_previous;
}

HT_API void h_list_lookback_end(LINKED_LIST * p_linked_list)
{
	if (p_linked_list == NULL)
	{
		return;
    }
    
	h_list_giveup_ownership(p_linked_list);
}

HT_API LINKED_NODE * h_list_get_from_front(LINKED_LIST * p_list)
{
	if (p_list == NULL)
	{
		return NULL;
	}
	
	return p_list->p_first_node;
}

HT_API LINKED_NODE * h_list_get_from_back(LINKED_LIST * p_list)
{
	if (p_list == NULL)
	{
		return NULL;
	}
	
	return p_list->p_last_node;
}

HT_API BOOL h_list_is_empty(LINKED_LIST * p_list)
{
	if (p_list == NULL)
	{
		return TRUE;
	}
	
    return (p_list->p_first_node == NULL);
}



