/***************************************************************************************
 *
 *  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 "hqueue.h"

/***************************************************************************************/
HT_API HQUEUE * hqCreate(uint32 unit_num, uint32 unit_size, uint32 queue_mode)
{
	uint32 q_len = unit_num * unit_size + sizeof(HQUEUE);

	HQUEUE *phq = (HQUEUE *)malloc(q_len);
	if (phq == NULL)
	{
		log_print(HT_LOG_ERR, "%s, malloc HQUEUE fail\r\n", __FUNCTION__);
		return NULL;
	}
	
	phq->queue_mode = queue_mode;
	phq->unit_size = unit_size;
	phq->unit_num = unit_num;
	phq->front = 0;
	phq->rear = 0;
	phq->count_put_full = 0;
	phq->queue_buffer = sizeof(HQUEUE);

	if (queue_mode & HQ_NO_EVENT)
	{
		phq->queue_nnulEvent = NULL;
		phq->queue_nfulEvent = NULL;
		phq->queue_putMutex = NULL;
	}
	else
	{
		phq->queue_nnulEvent = sys_os_create_sig();
		phq->queue_nfulEvent = sys_os_create_sig();
		phq->queue_putMutex = sys_os_create_mutex();
	}

	return phq;
}

HT_API void hqDelete(HQUEUE * phq)
{
	if (phq == NULL)
	{
		return;
	}
	
	if (phq->queue_mode & HQ_NO_EVENT)
	{
	}
	else
	{
		sys_os_destroy_sig_mutex(phq->queue_nnulEvent);
		sys_os_destroy_sig_mutex(phq->queue_nfulEvent);
		sys_os_destroy_sig_mutex(phq->queue_putMutex);
	}

	free(phq);
}

/***************************************************************************************/
void hqPutMutexEnter(HQUEUE * phq)
{
	if ((phq->queue_mode & HQ_NO_EVENT) == 0)
	{
		sys_os_mutex_enter(phq->queue_putMutex);
	}
}

void hqPutMutexLeave(HQUEUE * phq)
{
	if ((phq->queue_mode & HQ_NO_EVENT) == 0)
	{
		sys_os_mutex_leave(phq->queue_putMutex);
	}
}

HT_API BOOL hqBufPut(HQUEUE * phq, char * buf)
{
	uint32 real_rear, queue_count;
	char * ptr;

	if (phq == NULL || buf == NULL)
	{
		return FALSE;
	}

	hqPutMutexEnter(phq);

hqBufPut_start:

	queue_count = phq->rear - phq->front;

	if (queue_count == (phq->unit_num - 1))
	{
		if ((phq->queue_mode & HQ_NO_EVENT) == 0)
		{
			if (phq->queue_mode & HQ_PUT_WAIT)
			{
				sys_os_sig_wait(phq->queue_nfulEvent);
				goto hqBufPut_start;
			}
			else 
			{
				phq->count_put_full++;
				hqPutMutexLeave(phq);
				
				// log_print(HT_LOG_ERR, "%s, queue_count=%d,full!!!\r\n", __FUNCTION__, queue_count);
				return FALSE;
			}
		}
		else
		{
			hqPutMutexLeave(phq);
            return FALSE;
		}
	}

	real_rear = phq->rear % phq->unit_num;
	ptr = ((char *)phq) + phq->queue_buffer + real_rear*phq->unit_size;
	memcpy((char *)ptr, buf, phq->unit_size);
	phq->rear++;

	if ((phq->queue_mode & HQ_NO_EVENT) == 0)
	{
		sys_os_sig_sign(phq->queue_nnulEvent);
	}

	hqPutMutexLeave(phq);

	return TRUE;
}

HT_API BOOL hqBufGet(HQUEUE * phq, char * buf)
{
	uint32 real_front;

	if (phq == NULL || buf == NULL)
	{
		return FALSE;
	}
	
hqBufGet_start:

	if (phq->front == phq->rear)
	{
		if ((phq->queue_mode & HQ_NO_EVENT) == 0)
		{
			if (phq->queue_mode & HQ_GET_WAIT)
			{
				sys_os_sig_wait(phq->queue_nnulEvent);
				goto hqBufGet_start;
			}
			else 
			{
				return FALSE;
			}
		}
		else
		{
			return FALSE;
		}	
	}

	real_front = phq->front % phq->unit_num;
	memcpy(buf, ((char *)phq) + phq->queue_buffer + real_front*phq->unit_size, phq->unit_size);
	phq->front++;

	if ((phq->queue_mode & HQ_NO_EVENT) == 0)
	{
		sys_os_sig_sign(phq->queue_nfulEvent);
	}

	return TRUE;
}

HT_API BOOL hqBufIsEmpty(HQUEUE * phq)
{
	if (phq == NULL)
	{
		return TRUE;
	}
	
	if (phq->front == phq->rear)
	{
		return TRUE;
	}
	
	return FALSE;
}

HT_API BOOL hqBufIsFull(HQUEUE * phq)
{
	uint32 queue_count;

	if (phq == NULL)
	{
		return FALSE;
	}
	
	queue_count = phq->rear - phq->front;

	if (queue_count == (phq->unit_num - 1))
	{
		return TRUE;
	}

	return FALSE;
}

HT_API char * hqBufGetWait(HQUEUE * phq)
{
	uint32 real_front;
	char * ptr = NULL;

	if (phq == NULL)
	{
		return NULL;
	}
	
hqBufGet_start:

	if (phq->front == phq->rear)
	{
		if ((phq->queue_mode & HQ_NO_EVENT) == 0)
		{
			if (phq->queue_mode & HQ_GET_WAIT)
			{
				sys_os_sig_wait(phq->queue_nnulEvent);
				goto hqBufGet_start;
			}
			else 
			{
				return NULL;
			}
		}
		else
		{
			return NULL;
		}	
	}

	real_front = phq->front % phq->unit_num;

	ptr = ((char *)phq) + phq->queue_buffer + real_front*phq->unit_size;
	return ptr;
}

HT_API void hqBufGetWaitPost(HQUEUE * phq)
{
	if (phq == NULL)
	{
		return;
	}
	
	phq->front++;

	if ((phq->queue_mode & HQ_NO_EVENT) == 0)
	{
		sys_os_sig_sign(phq->queue_nfulEvent);
	}
}

HT_API char * hqBufPutPtrWait(HQUEUE * phq)
{
	uint32 real_rear,queue_count;
	char * ptr;

	if (phq == NULL)
	{
		return NULL;
	}
	
	hqPutMutexEnter(phq);

hqBufPutPtr_start:

	queue_count = phq->rear - phq->front;

	if (queue_count == (phq->unit_num - 1))
	{
		if ((phq->queue_mode & HQ_NO_EVENT) == 0)
		{
			if (phq->queue_mode & HQ_PUT_WAIT)
			{
				sys_os_sig_wait(phq->queue_nfulEvent);
				goto hqBufPutPtr_start;
			}
			else 
			{
				phq->count_put_full++;
				hqPutMutexLeave(phq);
				return FALSE;
			}
		}
		else
		{
			hqPutMutexLeave(phq);
            return FALSE;
		}
	}

	real_rear = phq->rear % phq->unit_num;
	ptr = ((char *)phq) + phq->queue_buffer + real_rear*phq->unit_size;

	return ptr;
}

HT_API void hqBufPutPtrWaitPost(HQUEUE * phq, BOOL bPutFinish)
{
	if (phq == NULL)
	{
		return;
	}
	
	if (bPutFinish)
	{
		phq->rear++;
	}
	
	if ((phq->queue_mode & HQ_NO_EVENT) == 0)
	{
		sys_os_sig_sign(phq->queue_nnulEvent);
	}

	hqPutMutexLeave(phq);
}

HT_API BOOL hqBufPeek(HQUEUE * phq, char * buf)
{
	uint32 real_front;

	if (phq == NULL || buf == NULL)
	{
		return FALSE;
	}
	
hqBufPeek_start:

	if (phq->front == phq->rear)
	{
		if ((phq->queue_mode & HQ_NO_EVENT) == 0)
		{
			if (phq->queue_mode & HQ_GET_WAIT)
			{
				sys_os_sig_wait(phq->queue_nnulEvent);
				goto hqBufPeek_start;
			}
			else 
			{
				return FALSE;
			}
		}
		else
		{
			return FALSE;
		}	
	}

	real_front = phq->front % phq->unit_num;
	memcpy(buf, ((char *)phq) + phq->queue_buffer + real_front*phq->unit_size, phq->unit_size);

	return TRUE;
}




