/*
* co_event.c - contains event routines
*
* Copyright (c) 2012-2023 emotas embedded communication GmbH
*-------------------------------------------------------------------
* $Id: co_event.c 50962 2023-11-24 10:38:11Z boe $
*
*
*-------------------------------------------------------------------
*
*
*/

/********************************************************************/
/**
* \brief event routines
*
* \file co_event.c
* contains event routines
*
*/

/* header of standard C - libraries
---------------------------------------------------------------------------*/
#include <string.h>

/* header of project specific types
---------------------------------------------------------------------------*/
#include <gen_define.h>

#include <co_datatype.h>
#include <co_commtask.h>
#include "ico_commtask.h"
#include "ico_event.h"

/* constant definitions
---------------------------------------------------------------------------*/

/* local defined data types
---------------------------------------------------------------------------*/

/* list of external used functions, if not in headers
---------------------------------------------------------------------------*/

/* list of global defined functions
---------------------------------------------------------------------------*/

/* list of local defined functions
---------------------------------------------------------------------------*/

/* external variables
---------------------------------------------------------------------------*/

/* global variables
---------------------------------------------------------------------------*/

/* local defined variables
---------------------------------------------------------------------------*/
static CO_EVENT_T	*pCoEvent[CO_LINE_ARRAY_DECL];
static CO_EVENT_T	*pCoPendEvent[CO_LINE_ARRAY_DECL];


/***************************************************************************/
/**
* \internal
*
* \brief icoEventStart - start a event
*
* This function add an event at end of the event list
*
* \return
*	RET_T
*/
RET_T icoEventStart(
		CO_LINE_TYPE	CO_LINE,		/* can line */
		CO_EVENT_T	*pEvent,		/* pointer to eventstruct */
		CO_EVENT_FCT_T	ptrToFct,	/* function for event */
		void		*pData				/* pointer for own data */
	)
{
CO_EVENT_T	*pT;

	/* insanity check */
	if (pEvent == NULL)  {
		return(RET_INVALID_PARAMETER);
	}

	/* an event without functionality */
	if (ptrToFct == NULL)  {
		return(RET_NOT_INITIALIZED);
	}

	pEvent->pFct = ptrToFct;
	pEvent->pData = pData;

	/* first event ? */
	if (pCoEvent[CO_LINE] == NULL)  {
		pCoEvent[CO_LINE] = pEvent;
	} else {
		/* add at end of list */
		pT = pCoEvent[CO_LINE];
		while (pT->pNext != NULL)  {
			pT = pT->pNext;
		}
		pT->pNext = pEvent;
	}

	pEvent->pNext = NULL;
	coCommTaskSet(CO_LINE, CO_COMMTASK_EVENT_NEW_EVENT);

	return(RET_OK);
}


/***************************************************************************/
/**
* \brief icoEventStop - stop an event
*
* This function removes an element from the event list
* as well as from the pending events list
*
* \return
*	RET_T
*/
RET_T icoEventStop(
		CO_LINE_TYPE	CO_LINE,	/* can line */
		CO_CONST CO_EVENT_T	*pEvent			/* pointer to event struct */
	)
{
CO_EVENT_T * pCurEvent;
CO_EVENT_T * pLastEvent;

	/* remove from event list */
	pCurEvent = pCoEvent[CO_LINE];
	pLastEvent = NULL;
	while (pCurEvent != NULL)  {
		if (pCurEvent == pEvent)  {
			if (pLastEvent == NULL)  {
				pCoEvent[CO_LINE] = pCurEvent->pNext;
			} else {
				pLastEvent->pNext = pCurEvent->pNext;
			}
		}
		pLastEvent = pCurEvent;
		pCurEvent = pCurEvent->pNext;
	}

	/* remove from pending event list */
	pCurEvent = pCoPendEvent[CO_LINE];
	pLastEvent = NULL;
	while (pCurEvent != NULL)  {
		if (pCurEvent == pEvent)  {
			if (pLastEvent == NULL)  {
				pCoPendEvent[CO_LINE] = pCurEvent->pNext;
			} else {
				pLastEvent->pNext = pCurEvent->pNext;
			}
		}
		pLastEvent = pCurEvent;
		pCurEvent = pCurEvent->pNext;
	}

	return(RET_OK);
}


/***************************************************************************/
/**
* \internal
*
* \brief icoEventPend - pends a event
*
* This function adds an event at end of the event pending list. The events
* in that list are not active.
*
* \return
*	RET_T
*/
RET_T icoEventPend(
		CO_LINE_TYPE	CO_LINE,		/* can line */
		CO_EVENT_T	*pEvent,			/* pointer to eventstruct */
		CO_EVENT_FCT_T	ptrToFct,		/* function for event */
		void		*pData,				/* pointer for own data */
		CO_EVENT_TYPE_T	type			/* type of pending event */
	)
{
CO_EVENT_T	*pT;

	/* insanity check */
	if (pEvent == NULL)  {
		return(RET_INVALID_PARAMETER);
	}

	/* an event without functionality */
	if (ptrToFct == NULL)  {
		return(RET_NOT_INITIALIZED);
	}

	pEvent->pFct = ptrToFct;
	pEvent->pData = pData;
	pEvent->type = type;

	/* first event ? */
	if (pCoPendEvent[CO_LINE] == NULL)  {
		pCoPendEvent[CO_LINE] = pEvent;
	} else {
		/* add at end of list */
		pT = pCoPendEvent[CO_LINE];
		while (pT->pNext != NULL)  {
			pT = pT->pNext;
		}
		pT->pNext = pEvent;
	}

	pEvent->pNext = NULL;

	return(RET_OK);
}


/***************************************************************************/
/**
* \internal
*
* \brief icoEventIsActive - check if event is active
*
* With this function can be ckecked,
* if a event is currently in the event list.
*
* \return BOOL_T
* \retval CO_TRUE
*	event is active
* \retval CO_FALSE
*	event is not active
*
*/
BOOL_T icoEventIsActive(
		CO_LINE_TYPE	CO_LINE,		/* can line */
		CO_CONST CO_EVENT_T	*pEvent		/* pointer to event struct */
	)
{
CO_EVENT_T	*pT = pCoEvent[CO_LINE];

	while (pT != NULL)  {
		if (pT == pEvent)  {
			return(CO_TRUE);
		}
		pT = pT->pNext;
	}

	return(CO_FALSE);
}


/***************************************************************************/
/**
* \internal
*
* \brief icoEventIsPending - check if event is in the pending list
*
* With this function can be ckecked,
* if a event is currently in the pending event list.
*
* \return BOOL_T
* \retval CO_TRUE
*	event is in the list
* \retval CO_FALSE
*	event is not in the list
*
*/
BOOL_T icoEventIsPending(
		CO_LINE_TYPE	CO_LINE,		/* can line */
		CO_CONST CO_EVENT_T	*pEvent		/* pointer to event struct */
	)
{
CO_EVENT_T	*pT = pCoPendEvent[CO_LINE];

	while (pT != NULL)  {
		if (pT == pEvent)  {
			return(CO_TRUE);
		}
		pT = pT->pNext;
	}

	return(CO_FALSE);
}


/***************************************************************************/
/**
* \internal
*
* \brief icoEventsAvailable - check for events
*
* \return BOOL_T
*
*/
BOOL_T icoEventsAvailable(
		CO_LINE_TYPE	CO_LINE		/**< can line */
	)
{
BOOL_T retVal = CO_TRUE;

	if (pCoEvent[CO_LINE] == NULL)  {
		retVal = CO_FALSE;
	}

	return(retVal);
}


/***************************************************************************/
/**
* \internal
*
* \brief icoEventCheck - check next event
*
*
*/
void icoEventCheck(
		CO_LINE_TYPE	CO_LINE		/**< can line */
	)
{
CO_EVENT_T	*pAct = NULL;
CO_EVENT_T	*pEv = pCoEvent[CO_LINE];

	pCoEvent[CO_LINE] = NULL;

	while (pEv != NULL)  {
		pAct = pEv;

		/* remove event from list */
		pEv = pAct->pNext;
		pAct->pNext = NULL;

		/* call event functions */
		if (pAct->pFct != NULL)  {
			pAct->pFct(CO_LINE, pAct->pData);
		}
	}
}


/***************************************************************************/
/**
* \internal
*
* \brief icoEventPost - post event for a given type to the active list
*
*
*/
void icoEventPost(
		CO_LINE_TYPE	CO_LINE,			/* can line */
		CO_EVENT_TYPE_T	type				/* event type */
	)
{
CO_EVENT_T	*pAct = NULL;
CO_EVENT_T	*pEv = pCoPendEvent[CO_LINE];

	if (pEv == NULL)  {
		return;
	}

	while (pEv->pNext != NULL)  {
		pAct = pEv->pNext;
		if (pAct->type == type)  {
			pEv->pNext = pAct->pNext;
			(void)icoEventStart(CO_LINE, pAct, pAct->pFct, pAct->pData);
		} else {
			pEv = pAct;
		}
	}

	/* test the head */
	pAct = pCoPendEvent[CO_LINE];

	if (pAct->type == type)  {
		pCoPendEvent[CO_LINE] = pAct->pNext;
		(void)icoEventStart(CO_LINE, pAct, pAct->pFct, pAct->pData);
	}
}


/***************************************************************************/
/**
* \internal
*
* \brief icoEventInit - init event interval
*
* This function initializes the internal event handling.
*
*
*/
void icoEventInit(
		void
	)
{
CO_LINE_TYPE	CO_LINE;

	for (CO_LINE = 0u; CO_LINE < CO_MULTI_LINE_CNT; CO_LINE++)
	{
		pCoEvent[CO_LINE] = NULL;
		pCoPendEvent[CO_LINE] = NULL;
	}
}
