 /*****************************************************************************
** al_scheduler.c: AL Layer Scheduler Source File
**
** Description: Allows scheduling of events in different modes
**
** Copyright(c) 2011 Sunmedia Technologies - All Rights Reserved
**
** Author : wuliang
**
** $Id: al_scheduler.c 1910 2011-01-24 05:05:35Z anlzhao_c1 $
*****************************************************************************/
#include "gl_task.h"
#include "gl_sema.h"
#include "gl_timer.h"
#include "al_basictypes.h"
#include "al_scheduler.h"
#include "al_event.h"
#include "al_time.h"
#include "ai_debug_config.h"

/* debug and error print */
#if SCH_DBG_FLAG
	#define SCH_DBG(fmt, arg...) \
	do { \
		UMFDBG(0,"[Debug]%s: %d Line ", __FUNCTION__, __LINE__); \
		UMFDBG(0,fmt, ##arg); \
	} while(0)
#else
	#define SCH_DBG(fmt, arg...);
#endif

#if SCH_ERROR_FLAG
	#define SCH_ERROR(fmt, arg...) \
	do { \
		UMFDBG(0,"[Error]%s: %d Line ", __FUNCTION__, __LINE__); \
		UMFDBG(0,fmt, ##arg); \
	} while(0)
#else
	#define SCH_ERROR(fmt, arg...);
#endif

//#define NON_TASKPROCESS

//#define  SKIP_REMINDER

#define RESCHEDULE

#define SCH_ROUND_OFF	(1) /* time in seconds*/
#define TO_ROUND_OFF(time) (time -=  (time % SCH_ROUND_OFF))

#define SCH_SEM_TAKE(sem_id, time) \
	if(GL_SUCCESS != GL_SemaphoreTake(sem_id, time)){\
		SCH_ERROR("SEM TAKE FAILED\n");}\
	else {SCH_DBG("SEM TAKE SUCCESS\n");}

#define SCH_SEM_GIVE(sem_id) \
	if(GL_SUCCESS != GL_SemaphoreGive(sem_id)){\
		SCH_ERROR("SEM GIVE FAILED\n");}\
	else {SCH_DBG("SEM GIVE SUCCESS\n");}

typedef struct _SCH_list{
	AL_SCH_details_t sch_detail;
	AL_SCH_time_t sch_time;
	AL_SCH_sched_id_t sch_id;
	AL_SCH_EVENT_CALLBACK sch_call_back;
	al_bool sch_enable;
	al_uint16 sch_occur;
	struct _SCH_list *sch_next;
}SCH_list_t;

/*********************GLOBAL static VARIABLES***********************/
#ifdef NON_TASKPROCESS
#else
static	GL_Task_t			sch_task_id;		/*TASK ID*/
static	GL_Semaphore_t		sch_sem_id;		/* SCHEDULER SEM ID*/
static volatile al_uint8 thread_flag = 0;
#endif
static 	GL_Timer_t	 		sch_timer_id;	/* Timer ID*/
static	GL_Semaphore_t		sch_prot_id;		/*PROTECTION SEM ID*/
static	SCH_list_t 			*sch_head_list=NULL;
static	al_uint32				sch_event_id;
static	AL_SCH_EVENT_CALLBACK g_app_callbck = NULL;
static al_bool timer_trigger_flag = al_false;
static al_bool sch_init = al_false;
/********************* static FUNCTIONS***********************/
#ifdef NON_TASKPROCESS
static void SCH_handler(void);
#else
static void SCH_task(void *pArg);
#endif
static AL_Return_t SCH_event_occur(void);
static AL_Return_t SCH_get_next_schedule_time(AL_SCH_time_t *sch_time);
static AL_Return_t SCH_insert_event(SCH_list_t * sch_new);
static void SCH_timer_callback(void *pArg);
static AL_Return_t SCH_get_schd_events(al_uint16 *no_of_schd_events);
#if 0
void SCH_dump_list(void)
{
	SCH_list_t *sch_id;
	al_uint16 yr;
	UI8 mon,date,hrs,mins,sec;

	sch_id = sch_head_list;
	printf("\n\n**********************************\n");

	if(CDBIConvertTimeToDate(&sch_id->sch_time , &yr, &mon, &date, &hrs, &mins,&sec) != ATSC_SUCCESS){
		printf(" \n failed to convert the time to date format ");
	}

	 while ( sch_id != NULL ){

		printf("\n %s %d Schdule [%d] Enable [%d] time set [%d] Time Given[%d] Type [0x%X] Snooze [%d] dateformat[%d/%d/%d %d:%d:%d]\n",
			__FUNCTION__, __LINE__, sch_id->sch_id, sch_id->sch_enable, sch_id->sch_time,
			sch_id->sch_detail.sch_time, sch_id->sch_detail.sch_event, sch_id->sch_detail.sch_snooze,
			date, mon, yr,hrs,mins,sec);
		sch_id = sch_id->sch_next;
	}
	printf("\n\n**********************************\n");
}
#endif

#ifdef NON_TASKPROCESS
#else
#define SCH_SEM_NAME "SCH_SEM"
#define SCH_SEM_MAX (1)
#define SCH_SEM_INITIAL (0)
#endif


#define SCH_SEM_LOCK_NAME "SCH_SEM_LOCK"
#define SCH_SEM_LOCK_MAX (1)
#define SCH_SEM_LOCK_INITIAL (1)

#define SCH_TMR_NAME					"SCH_TIMER"
#ifdef NON_TASKPROCESS
#else
#define SCH_TASK_NAME					"SCH_TASK"
#define SCH_STACK_SIZE					(4*1024)
#define SCH_TASK_PRIORITY				(5)
#endif

/************************************************************************
 ** name		:AL_SCH_Init()
 **
 ** description		: Scheduler COMPONENT INIT
 **
 ** input parameters	: NONE
 **
 ** output parameters	:   None
 **
 ** return type		: AL_Return_t SUCCESS or FAILURE
 ************************************************************************/
AL_Return_t AL_SCH_Init(AL_SCH_EVENT_CALLBACK sch_call_back)
{
	if (sch_init == al_true)
	{
		return AL_INITIALIZE_ALREADY;
	}

	/*creating Semaphore for Scheduler Component*/
	GL_Status_t retGL;
#ifdef NON_TASKPROCESS
#else
	if (GL_SUCCESS != GL_SemaphoreCreate(SCH_SEM_NAME, SCH_SEM_MAX, SCH_SEM_INITIAL,  &sch_sem_id))
	{
		SCH_ERROR("GL_SemaphoreCreate() failed \n");
		goto _NEXT;
	}
#endif
	/*creating Semaphore for global variable PROTECTION*/
	if (GL_SUCCESS != GL_SemaphoreCreate(SCH_SEM_LOCK_NAME, SCH_SEM_LOCK_MAX, SCH_SEM_LOCK_INITIAL, &sch_prot_id))
	{
		SCH_ERROR("GL_SemaphoreCreate() failed \n");
		goto _NEXT;
	}
	if (GL_SUCCESS != GL_TimerCreate(SCH_TMR_NAME, SCH_timer_callback, al_null, al_false, 1000, al_false, &sch_timer_id))
	{
		SCH_ERROR("GL_TimerCreate() failed \n");
		goto _NEXT;
	}

	if (sch_call_back != NULL)
	{
		g_app_callbck = sch_call_back;
	}

#ifdef NON_TASKPROCESS
#else
	/* create task for scheduler */
	retGL = GL_TaskCreate(SCH_TASK_NAME, SCH_task, NULL, SCH_TASK_PRIORITY, SCH_STACK_SIZE, TRUE, &sch_task_id);
	if (retGL != GL_SUCCESS)
	{
		SCH_ERROR("Error %d: %s call GL_TaskCreate(%s) failed!\n", retGL, __FUNCTION__, SCH_TASK_NAME);
		goto _NEXT;
	}
#endif

	sch_init = al_true;

	return AL_SUCCESS;

	//1 Error Handling:
_NEXT:
#ifdef NON_TASKPROCESS
#else
	if (sch_sem_id != 0)
	{
		GL_SemaphoreDelete(sch_sem_id);
		sch_sem_id = 0;
	}
#endif

	if (sch_prot_id != 0)
	{
		GL_SemaphoreDelete(sch_prot_id);
		sch_prot_id = 0;
	}

	if (sch_timer_id != 0)
	{
		GL_TimerDelete(sch_timer_id);
		sch_timer_id = 0;
	}
	
	return AL_SUCCESS;
}

/************************************************************************
 ** name		:AL_SCH_Terminate()
 **
 ** description		: Scheduler COMPONENT Terminate
 **
 ** input parameters	: NONE
 **
 ** output parameters	:   None
 **
 ** return type		: AL_Return_t SUCCESS or FAILURE
 ************************************************************************/
AL_Return_t AL_SCH_Terminate(void)
{
	if (sch_init == al_false)
	{
		return AL_UNINITIALIZED;
	}
	
	/* Delete all the schedule in waiting list */
	AL_SCH_SetDefaultSettings();
#ifdef NON_TASKPROCESS
#else
	thread_flag = 1;
	SCH_SEM_GIVE(sch_sem_id);

	while (thread_flag)
	{
		GL_TaskSleep(300);
	}
#endif

	/*Delete Semaphore */
	if (GL_SUCCESS != GL_SemaphoreDelete(sch_prot_id))
	{
		SCH_ERROR("OSAL_sem_delete() failed \n");
	}

	if (GL_SUCCESS != GL_TimerDelete(sch_timer_id))
	{
		SCH_ERROR("OSAL_timer_delete() failed \n");
	}

#ifdef NON_TASKPROCESS
#else
	/* delete Scheduler task */
	if (GL_SUCCESS != GL_TaskDestroyOne(sch_task_id))
	{
		SCH_ERROR("OSAL_task_delete() failed\n");
		/*Semaphores not deleted, as ASSERTION is carried for failure of INIT*/
	}
	
	if (GL_SUCCESS != GL_SemaphoreDelete(sch_sem_id))
	{
		SCH_ERROR("OSAL_sem_delete() failed \n");
	}
#endif
	sch_init = al_false;

	return AL_SUCCESS;
}

/************************************************************************
 ** name		:AL_SCH_Handle_Time_Change()
 **
 ** description		: Again check the schedule list and get the next event.
 **
 ** input parameters	: NONE
 **
 ** output parameters	:   None
 **
 ** return type		: AL_Return_t SUCCESS or FAILURE
 ************************************************************************/
AL_Return_t AL_SCH_Handle_Time_Change( al_uint32 prev_time )
{
	if (sch_init == al_false)
	{
		return AL_UNINITIALIZED;
	}
	
	al_uint32 curr_time = 0;
	al_int32  time_diff = 0;
	SCH_list_t *sch_list = NULL;
	AL_SCH_event_type_t sch_mode = AL_SCH_TYPE_MAX;
#ifdef RESCHEDULE
	AL_SCH_details_t program_det;
	al_uint32 program_id = 0xFFFFFFFF;
	AL_SCH_details_t sleep_det;
	al_uint32 sleep_time_id = 0xFFFFFFFF;
	memset(&program_det, 0, sizeof(AL_SCH_details_t));
	memset(&sleep_det, 0, sizeof(AL_SCH_details_t));
#endif

	if ( prev_time != 0 )
	{
		if (AL_FAILURE == AL_Time_GetCurTimeInSecs(&curr_time))
		{
			SCH_ERROR("AL_Time_GetCurTimeInSecs() Failed \n");
			return AL_FAILURE;
		}

		time_diff = curr_time  - prev_time;
		SCH_SEM_TAKE(sch_prot_id, GL_INFINITE_WAIT);
		sch_list = sch_head_list;

		while(sch_list != NULL)
		{
			sch_mode = ((~AL_SCH_TYPE_MASK) & sch_list->sch_detail.sch_event);
			if ( sch_mode == AL_SCH_TYPE_SLEEP )
			{
#ifdef RESCHEDULE
				sch_list->sch_detail.sch_time += time_diff;
				sleep_time_id = sch_list->sch_id;
				sleep_det.sch_event = sch_list->sch_detail.sch_event;
				sleep_det.sch_snooze = sch_list->sch_detail.sch_snooze;
				sleep_det.app_schd_time[0] = sch_list->sch_detail.app_schd_time[0];
				sleep_det.app_schd_time[1] = sch_list->sch_detail.app_schd_time[1];
				sleep_det.app_schd_time[2] = sch_list->sch_detail.app_schd_time[2];
				break;
#else
				sch_list->sch_detail.sch_time += time_diff ;
				sch_list->sch_time += time_diff ;
#endif

			}
			else if (	sch_mode == AL_SCH_TYPE_PRG_EXPIRE_SCH)
			{
				sch_list->sch_detail.sch_time += time_diff;
				//sch_list->sch_time += time_diff;
				program_id = sch_list->sch_id;
				program_det.sch_event = sch_list->sch_detail.sch_event;
				program_det.sch_snooze = sch_list->sch_detail.sch_snooze;
				program_det.app_schd_time[0] = sch_list->sch_detail.app_schd_time[0];
				program_det.app_schd_time[1] = sch_list->sch_detail.app_schd_time[1];
				program_det.app_schd_time[2] = sch_list->sch_detail.app_schd_time[2];
			}
			sch_list = sch_list->sch_next;
		}

		SCH_SEM_GIVE(sch_prot_id);
	}

#ifdef RESCHEDULE
	if (sch_mode == AL_SCH_TYPE_PRG_EXPIRE_SCH && time_diff != 0)
	{

		AL_SCH_Get_ScheduleTimeRemaining(&program_id, &(program_det.sch_time));
		AL_SCH_ReScheduleEvent(&program_id , &program_det);
	}
	else if ((sleep_time_id != 0xFFFFFFFF) && (time_diff != 0))
	{
		AL_SCH_Get_ScheduleTimeRemaining(&sleep_time_id, &(sleep_det.sch_time));
		AL_SCH_ReScheduleEvent(&sleep_time_id , &sleep_det);
	}
	else
	{
		/* Signal Schedular task */
#ifdef NON_TASKPROCESS
		SCH_handler();
#else
		SCH_SEM_GIVE(sch_sem_id);
#endif
	}
#endif

	return AL_SUCCESS;
}

/************************************************************************
 ** name		:AL_SCH_ScheduleEvent()
 **
 ** description		: Schedules the given event at given time
 **
 ** input parameters	: sch_detail: details structure
 **					sch_call_bck: Callback function required. If this is passed as NULL
 **								framework callback function is passed.
 **
 ** output parameters	:    sch_id: Return the scheduled event id.
 **
 ** return type		: AL_Return_t SUCCESS or FAILURE
 ************************************************************************/
AL_Return_t AL_SCH_ScheduleEvent( AL_SCH_details_t *sch_detail, AL_SCH_sched_id_t *sch_id,
	AL_SCH_EVENT_CALLBACK sch_call_bck)
{
	if (sch_init == al_false)
	{
		return AL_UNINITIALIZED;
	}
	
	al_uint32 curr_time = 0;
	SCH_list_t *sch_new = NULL;
	AL_SCH_event_type_t sch_type = AL_SCH_TYPE_MAX;
	AL_SCH_event_type_t sch_mode = AL_SCH_TYPE_MAX;
	al_uint8 schd_index = 0;
	al_uint16 no_of_schd_events = 0;
	
	if ((sch_detail == NULL) || (sch_id == NULL))
	{
		SCH_ERROR("INVALID PARAM");
		return AL_INVALID_PARAM;
	}

	if (AL_FAILURE == AL_Time_GetCurTimeInSecs(&curr_time))
	{
		SCH_ERROR("FAILED TO GET EVENT TIME\n");
		return AL_FAILURE;
	}
	

	sch_type = AL_SCH_TYPE_MASK & sch_detail->sch_event;
	sch_mode = ((~AL_SCH_TYPE_MASK) & sch_detail->sch_event);

	if (AL_SUCCESS != SCH_get_schd_events(&no_of_schd_events))
	{
		SCH_ERROR("SCH_get_schd_events() failed\n");
		return AL_FAILURE;
	}

	if ( no_of_schd_events >= AL_SCH_MAX_NO_OF_SCHD_EVENT )
	{
		SCH_ERROR("Max no of events are already scheduled\n");
		return AL_FAILURE;
	}

	if ( ( sch_type & AL_SCH_TYPE_REPEAT ) && (sch_detail->sch_snooze == 0 ))
	{
		SCH_ERROR("Invalid snooze time\n");
		return AL_FAILURE;
	}

	if (AL_SCH_TYPE_SLEEP == sch_mode )
	{
		sch_detail->sch_time += curr_time;
	}

	TO_ROUND_OFF(curr_time);
	TO_ROUND_OFF(sch_detail->sch_time);

#ifdef SKIP_REMINDER
	if ((AL_SCH_TYPE_PRG_SCH == sch_mode) || (AL_SCH_TYPE_SRV_SCH == sch_mode))
	{
		if (curr_time >= sch_detail->sch_time - sch_detail->app_schd_time[0])
		{
			SCH_ERROR("CANNOT SCHEDULE LAPSED EVENTS\n");
			return AL_SCHEDULER_LAPSED;
		}
	}
#endif

	if (curr_time >= sch_detail->sch_time)
	{
		SCH_ERROR("CANNOT SCHEDULE LAPSED EVENTS\n");
		return AL_SCHEDULER_LAPSED;
	}

	if ((sch_new = (SCH_list_t*)GL_MemAlloc(sizeof(SCH_list_t)))== NULL)
	{
		SCH_ERROR("CANNOT ALLOCATE MEMORY\n");
		return AL_MEMORY_ALLOC_FAILED;
	}

	sch_new->sch_time = sch_detail->sch_time;

	if ( sch_type & AL_SCH_TYPE_MULTI_EVENT)
	{
		for ( schd_index = 0;schd_index < AL_SCH_MAX_NO_OF_CALLBACK ; schd_index++ )
		{
			if (sch_detail->app_schd_time[schd_index] == 0 )
			{
				sch_new->sch_occur = AL_SCH_MAX_NO_OF_CALLBACK;
				break;
			}
			else if ((sch_detail->sch_time - curr_time) > sch_detail->app_schd_time[schd_index])
			{
				sch_new->sch_occur = schd_index;
				sch_new->sch_time -= sch_detail->app_schd_time[schd_index];
				break;
			}
		}

		if ( schd_index >= AL_SCH_MAX_NO_OF_CALLBACK )
		{
			sch_new->sch_occur = AL_SCH_MAX_NO_OF_CALLBACK;
		}
	}
	else
	{
		sch_new->sch_occur = AL_SCH_MAX_NO_OF_CALLBACK;
	}

	memcpy(&sch_new->sch_detail, sch_detail, sizeof(AL_SCH_details_t));
	sch_new->sch_id = *sch_id = sch_event_id++;
	sch_new->sch_enable = al_true;
	sch_new->sch_call_back = sch_call_bck;
	SCH_SEM_TAKE(sch_prot_id, GL_INFINITE_WAIT);
	SCH_insert_event(sch_new);
	SCH_SEM_GIVE(sch_prot_id);

	/* Signal Schedular task */
#ifdef NON_TASKPROCESS
	SCH_handler();
#else
	SCH_SEM_GIVE(sch_sem_id);
#endif

	return AL_SUCCESS;
}

/************************************************************************
 ** name		:AL_SCH_ReScheduleEvent()
 **
 ** description		: Reschedules the event
 **
 ** input parameters	: sch_id: Schedule ID to be reschedule
 **					sch_details: New details applicable.
 **
 ** output parameters	:   None
 **
 ** return type		: AL_Return_t SUCCESS or FAILURE
 ************************************************************************/
AL_Return_t AL_SCH_ReScheduleEvent(AL_SCH_sched_id_t *sch_id,  AL_SCH_details_t *sch_details)
{
	if (sch_init == al_false)
	{
		return AL_UNINITIALIZED;
	}
	
	SCH_list_t *sch_list = NULL;
	SCH_list_t *prev_sch_list = NULL;
	AL_Return_t ret = AL_FAILURE;
	al_uint32 curr_time = 0;
	AL_SCH_event_type_t sch_mode = AL_SCH_TYPE_MAX;
	AL_SCH_event_type_t sch_type = AL_SCH_TYPE_MAX;
	al_uint8 schd_index = 0;
	
	if((sch_id == NULL) || (sch_details == NULL)){
		SCH_ERROR("INVALID PARAMS");
		return AL_INVALID_PARAM;
	}

	sch_type = AL_SCH_TYPE_MASK & sch_details->sch_event;
	sch_mode = sch_details->sch_event & (~AL_SCH_TYPE_MASK);

	if (( sch_type & AL_SCH_TYPE_REPEAT ) && (sch_details->sch_snooze == 0 ))
	{
		SCH_ERROR("Invalid snooze time\n");
		return AL_FAILURE;
	}

	if (AL_FAILURE == AL_Time_GetCurTimeInSecs(&curr_time))
	{
		SCH_ERROR("FAILED TO GET EVENT TIME\n");
		return AL_FAILURE;
	}
	
	if (AL_SCH_TYPE_SLEEP == sch_mode)
	{
		sch_details->sch_time += curr_time;
	}

	TO_ROUND_OFF(curr_time);
	TO_ROUND_OFF(sch_details->sch_time);

#ifdef SKIP_REMINDER
	if ((AL_SCH_TYPE_PRG_SCH == sch_mode) || (AL_SCH_TYPE_SRV_SCH == sch_mode))
	{
		if (curr_time >= sch_details->sch_time - sch_details->app_schd_time[0])
		{
			SCH_ERROR("CANNOT SCHEDULE LAPSED EVENTS\n");
			return AL_SCHEDULER_LAPSED;
		}
	}
#endif
	

	if (curr_time >= sch_details->sch_time)
	{
		SCH_ERROR("CANNOT SCHEDULE LAPSED EVENTS\n");
		return AL_SCHEDULER_LAPSED;
	}

	SCH_SEM_TAKE(sch_prot_id, GL_INFINITE_WAIT);

	if ((sch_list = sch_head_list) == NULL)
	{
		SCH_ERROR("NO EVENTS SCHEDULED");
		SCH_SEM_GIVE(sch_prot_id);
		return AL_INVALID_SCH_ID;
	}

	prev_sch_list = NULL;

	while (sch_list != NULL)
	{
		if (sch_list->sch_id == *sch_id)
		{
			break;
		}
		prev_sch_list = sch_list;
		sch_list = sch_list->sch_next;
	}

	if (sch_list == NULL)
	{
		SCH_ERROR("INVALID SCHEDULE ID");
		SCH_SEM_GIVE(sch_prot_id);
		return AL_INVALID_SCH_ID;
	}

	/* Remove the node from the link list */
	if ( prev_sch_list == NULL )
	{
		sch_head_list = sch_list->sch_next;
	}
	else
	{
		prev_sch_list->sch_next = sch_list->sch_next;
	}

	sch_list->sch_time = sch_details->sch_time;

	if (sch_details->sch_event & AL_SCH_TYPE_MULTI_EVENT)
	{
		for ( schd_index = 0;schd_index < AL_SCH_MAX_NO_OF_CALLBACK ; schd_index++ )
		{
			if (sch_details->app_schd_time[schd_index] == 0 )
			{
				sch_list->sch_occur = AL_SCH_MAX_NO_OF_CALLBACK;
				break;
			}
			else if ((sch_list->sch_time - curr_time) > sch_details->app_schd_time[schd_index])
			{
				sch_list->sch_occur = schd_index;
				sch_list->sch_time -= sch_details->app_schd_time[schd_index];
				break;
			}
		}
		if ( schd_index >= AL_SCH_MAX_NO_OF_CALLBACK )
		{
			sch_list->sch_occur = AL_SCH_MAX_NO_OF_CALLBACK;
		}
	}
	else
	{
		sch_list->sch_occur = AL_SCH_MAX_NO_OF_CALLBACK;
	}

	memcpy(&sch_list->sch_detail, sch_details, sizeof(AL_SCH_details_t));
	sch_list->sch_enable = al_true;
	ret = SCH_insert_event(sch_list);

	SCH_SEM_GIVE ( sch_prot_id );
	/* Signal schedular task */
#ifdef NON_TASKPROCESS
	SCH_handler();
#else
	SCH_SEM_GIVE(sch_sem_id);
#endif

	return ret;
}

/************************************************************************
 ** name		:AL_SCH_UnScheduleEvent()
 **
 ** description		: Unschedules the event
 **
 ** input parameters	: AL_SCH_sched_id_t *sch_id (Pointer to schedule ID);
 **
 ** output parameters	:   None
 **
 ** return type		: AL_Return_t SUCCESS or FAILURE
 ************************************************************************/
AL_Return_t AL_SCH_UnScheduleEvent(AL_SCH_sched_id_t *sch_id)
{
	if (sch_init == al_false)
	{
		return AL_UNINITIALIZED;
	}
	
	SCH_list_t *sch_list;
	SCH_list_t *sch_prev = NULL;
	
	
	if (sch_id == NULL)
	{
		SCH_ERROR("INVALID PARAMS");
		return AL_INVALID_PARAM;
	}

	SCH_SEM_TAKE(sch_prot_id, GL_INFINITE_WAIT);

	if ((sch_list = sch_head_list) == NULL)
	{
		SCH_ERROR("NO EVENTS SCHEDULED");
		SCH_SEM_GIVE(sch_prot_id);
		return AL_INVALID_SCH_ID;
	}

	while(sch_list != NULL)
	{
		if (sch_list->sch_id == *sch_id)
		{
			break;
		}
		sch_prev = sch_list;
		sch_list = sch_list->sch_next;
	}

	if (sch_list == NULL)
	{
		SCH_ERROR("INVALID SCHEDULE ID");
		SCH_SEM_GIVE(sch_prot_id);
		return AL_INVALID_SCH_ID;
	}

	if (sch_prev == NULL)
	{
		sch_head_list = sch_head_list->sch_next;
	}
	else
	{
		sch_prev->sch_next = sch_list->sch_next;
	}

	GL_MemFree(sch_list);
	SCH_SEM_GIVE(sch_prot_id);

	/* Signal Schedular task */
#ifdef NON_TASKPROCESS
	SCH_handler();
#else
	SCH_SEM_GIVE(sch_sem_id);
#endif

	return AL_SUCCESS;
}

/************************************************************************
 ** name		:AL_SCH_GetScheduleDetails()
 **
 ** description		: Gives the details of given schedule id
 **
 ** input parameters	: AL_SCH_sched_id_t *sch_id , AL_SCH_details_t *sch_details(pointer)
 **
 ** output parameters	: AL_SCH_details_t *sch_details (details r populated)
 **
 ** return type		: AL_Return_t SUCCESS or FAILURE
 ************************************************************************/
AL_Return_t AL_SCH_GetScheduleDetails(AL_SCH_sched_id_t *sch_id, AL_SCH_details_t *sch_details)
{
	if (sch_init == al_false)
	{
		return AL_UNINITIALIZED;
	}
	
	SCH_list_t *sch_list;
	
	if ((sch_id == NULL) || (sch_details == NULL))
	{
		SCH_ERROR("INVALID PARAMS");
		return AL_INVALID_PARAM;
	}

	SCH_SEM_TAKE(sch_prot_id, GL_INFINITE_WAIT);

	if ((sch_list = sch_head_list) == NULL)
	{
		SCH_ERROR("NO EVENTS SCHEDULED");
		SCH_SEM_GIVE(sch_prot_id);
		return AL_INVALID_SCH_ID;
	}

	while (sch_list != NULL)
	{
		if(sch_list->sch_id == *sch_id)
		{
			break;
		}
		sch_list = sch_list->sch_next;
	}
	if (sch_list == NULL)
	{
		SCH_ERROR("INVALID SCHEDULE ID");
		SCH_SEM_GIVE(sch_prot_id);
		return AL_INVALID_SCH_ID;
	}

	memcpy(sch_details, &sch_list->sch_detail, sizeof(AL_SCH_details_t));

	SCH_SEM_GIVE(sch_prot_id);

	return AL_SUCCESS;
}

/************************************************************************
 ** name		:AL_SCH_Get_ScheduleTimeRemaining
 **
 ** description		: Gives the details of given schedule id
 **
 ** input parameters	: AL_SCH_sched_id_t *sch_id , AL_SCH_details_t *sch_details(pointer)
 **
 ** output parameters	: AL_SCH_details_t *sch_details (details r populated)
 **
 ** return type		: AL_Return_t SUCCESS or FAILURE
 ************************************************************************/
AL_Return_t AL_SCH_Get_ScheduleTimeRemaining(AL_SCH_sched_id_t *sch_id, AL_SCH_time_t *sch_time)
{
	if (sch_init == al_false)
	{
		return AL_UNINITIALIZED;
	}
	
	al_uint32 curr_time = 0;
	SCH_list_t *sch_list = NULL;
	
	if ((sch_id == NULL) || (sch_time == NULL))
	{
		SCH_ERROR("Invalid Parameters");
		return AL_FAILURE;
	}

	*sch_time =0;

	if (AL_FAILURE == AL_Time_GetCurTimeInSecs(&curr_time))
	{
		SCH_ERROR("Failed to get current Time");
		return AL_FAILURE;
	}

	SCH_SEM_TAKE(sch_prot_id, GL_INFINITE_WAIT);

	if ((sch_list = sch_head_list) == NULL)
	{
		SCH_ERROR("No Events Scheduled");
		SCH_SEM_GIVE(sch_prot_id);
		return AL_INVALID_SCH_ID;
	}

	while(sch_list != NULL)
	{
		if ( sch_list->sch_id == *sch_id )
		{
			if ( sch_list->sch_enable != al_false)
			{
				if (sch_list->sch_detail.sch_time > curr_time)
				{
					*sch_time = sch_list->sch_detail.sch_time - curr_time;
					SCH_SEM_GIVE(sch_prot_id);
					return AL_SUCCESS;
				}
				else
				{
					SCH_SEM_GIVE(sch_prot_id);
					return AL_SCHEDULER_LAPSED;
				}
			}
			SCH_SEM_GIVE(sch_prot_id);
			return AL_SCHEDULER_LAPSED;
		}
		else
		{
			sch_list = sch_list->sch_next;
		}
	}

	SCH_ERROR("Invalid Schedule ID");
	SCH_SEM_GIVE(sch_prot_id);

	return AL_INVALID_SCH_ID;
}

/************************************************************************
 ** name		:AL_SCH_Set_Schedule_Onoff
 **
 ** description		:
 **
 ** input parameters	:
 **
 ** output parameters	:
 **
 ** return type		: AL_Return_t SUCCESS or FAILURE
 ************************************************************************/
AL_Return_t AL_SCH_Set_Schedule_Onoff(al_bool sch_en)
{
	if (sch_init == al_false)
	{
		return AL_UNINITIALIZED;
	}
	
	SCH_list_t *sch_list;

	sch_list = sch_head_list;
	SCH_SEM_TAKE(sch_prot_id, GL_INFINITE_WAIT);
	
	if (sch_en==0)
	{
		//Disable schedule within schedule list
		while ((sch_list != NULL) && sch_list->sch_enable == al_true)
		{
			SCH_DBG("%s:%d list=%X sch_type=%d\n",
				__FUNCTION__,__LINE__,(al_uint32)sch_list,sch_list->sch_detail.sch_event);

			if (sch_list->sch_detail.sch_event != (AL_SCH_TYPE_SLEEP|AL_SCH_TYPE_MULTI_EVENT))
				sch_list->sch_enable = al_false;
			sch_list = sch_list->sch_next;
		}
	}
	else
	{
		while ((sch_list != NULL) && sch_list->sch_enable == al_false)
		{
			SCH_DBG("%s:%d list=%X sch_type=%d\n",
				__FUNCTION__,__LINE__,(al_uint32)sch_list,sch_list->sch_detail.sch_event);

			if (sch_list->sch_detail.sch_event != (AL_SCH_TYPE_SLEEP|AL_SCH_TYPE_MULTI_EVENT))
				sch_list->sch_enable = al_true;
			sch_list = sch_list->sch_next;
		}
	}

	SCH_SEM_GIVE(sch_prot_id);

	return AL_SUCCESS;
}

/************************************************************************
 ** name		:SCH_Handle_TimerCallback
 **
 ** description		: This API is to handle AL_SCH_TYPE_PRG_EXPIRE_SCH event
 ** when system time fails to work in regular way. (For example, system time does not change.)
 **
 ** input parameters	: None
 **
 ** output parameters	:
 **
 ** return type		: None
 ************************************************************************/
static void SCH_Handle_TimerCallback(void)
{
	SCH_list_t *sch_list;

	/* Only works for timer triggered event */
	if (timer_trigger_flag == al_true)
	{
		SCH_SEM_TAKE(sch_prot_id, GL_INFINITE_WAIT);
		if (sch_head_list != NULL
			&& ((sch_head_list->sch_detail.sch_event & AL_SCH_TYPE_PRG_EXPIRE_SCH) == AL_SCH_TYPE_PRG_EXPIRE_SCH))
		{
			if (sch_head_list->sch_enable == al_true)
			{
				if (sch_head_list->sch_call_back != NULL)
				{
					(sch_head_list->sch_call_back)((sch_head_list->sch_detail.sch_event & (~AL_SCH_TYPE_MASK)),\
						sch_head_list->sch_id, &sch_head_list->sch_detail);
				}
				else if (g_app_callbck != NULL)
				{
					(g_app_callbck)((sch_head_list->sch_detail.sch_event & (~AL_SCH_TYPE_MASK)),
						sch_head_list->sch_id, &sch_head_list->sch_detail);
				}
			}

			sch_list = sch_head_list;
			sch_head_list = sch_head_list->sch_next;

			GL_MemFree(sch_list);
		}

		SCH_SEM_GIVE(sch_prot_id);

		timer_trigger_flag = al_false;
	}
}

#ifdef NON_TASKPROCESS
static void SCH_handler(void)
{
	al_uint32 sch_time = GL_INFINITE_WAIT;
	
	SCH_Handle_TimerCallback();
	
	if (GL_SUCCESS != GL_TimerDisable(sch_timer_id))
	{
		SCH_ERROR("Unable To Disable timer\n");
	}

	if (AL_SUCCESS != SCH_event_occur())
	{
		SCH_ERROR("ERROR IN CALL BACK\n");
	}

	if (AL_SUCCESS != SCH_get_next_schedule_time(&sch_time))
	{
		SCH_ERROR("Cannot process\n");
		sch_time = GL_INFINITE_WAIT;
	}

	if (sch_time != GL_INFINITE_WAIT)
	{
		GL_TimerSchedule(sch_timer_id, sch_time*1000);
		GL_TimerEnable(sch_timer_id);
	}
}
#else
/************************************************************************
 ** name		:SCH_task()
 **
 ** description		: Scheduler Task. Created through ACSH_init
 **
 ** input parameters	: NONE
 **
 ** output parameters	:   None
 **
 ** return type		: AL_Return_t SUCCESS or FAILURE
 ************************************************************************/
static void SCH_task(void *pArg)
{
	al_uint32 sch_time = GL_INFINITE_WAIT;
	al_uint32 curr_time;
	al_bool time_changed;

	while (al_true)
	{
		time_changed = al_false;

		if (AL_FAILURE != AL_Time_GetCurTimeInSecs(&curr_time))
		{
			//AIST_check_time_change( &curr_time, &time_changed);
			time_changed = al_false;
		}

		/* If Time changed has happend then wait for the framework to Sync the time*/
		if ( time_changed == al_false)
		{
			if (AL_SUCCESS != SCH_event_occur())
			{
				SCH_ERROR("ERROR IN CALL BACK\n");
			}

			if (AL_SUCCESS != SCH_get_next_schedule_time(&sch_time))
			{
				SCH_ERROR("Cannot process\n");
				sch_time = 0;
			}
			if (sch_time != 0)
			{
				GL_TimerSchedule(sch_timer_id, sch_time*1000);
				GL_TimerEnable(sch_timer_id);
			}
		}
		SCH_SEM_TAKE(sch_sem_id, GL_INFINITE_WAIT);

		SCH_Handle_TimerCallback();

		if (thread_flag) break;

		if (GL_SUCCESS != GL_TimerDisable(sch_timer_id))
		{
			SCH_ERROR("Unable To Disable timer\n");
		}

		GL_TaskSleep(200);

	}
	thread_flag = 0;
}
#endif

/************************************************************************
 ** name		:SCH_insert_event()
 **
 ** description		: Inserts the given event appropriately based on time
 **
 ** input parameters	: Pointer to SCH_list_t structure
 **
 ** output parameters	:  None
 **
 ** return type		: AL_Return_t SUCCESS or FAILURE
 ************************************************************************/
static AL_Return_t SCH_insert_event(SCH_list_t * sch_new)
{
	SCH_list_t *sch_list, *sch_prev;

	if (sch_new == NULL)
	{
		SCH_ERROR("NULL PARAMS PASSED");
		return AL_FAILURE;
	}

	sch_list = sch_head_list;
	sch_prev = NULL;

	while (sch_list != NULL)
	{
		if (sch_list->sch_time > sch_new->sch_time)
		{
			break;
		}
		else
		{
			sch_prev = sch_list;
			sch_list = sch_list->sch_next;
		}
	}

	if (sch_prev != NULL)
	{
		sch_prev->sch_next = sch_new;
	}
	else
	{
		sch_head_list = sch_new;
	}
	sch_new->sch_next = sch_list;

	return AL_SUCCESS;
}

/************************************************************************
 ** name		:SCH_event_occur()
 **
 ** description		: Invokes registered call back for events occuring in current time.
 **
 ** input parameters	: void
 **
 ** output parameters	:   Time
 **
 ** return type		: AL_Return_t SUCCESS or FAILURE
 ************************************************************************/
AL_Return_t SCH_event_occur(void)
{
	SCH_list_t *sch_list = NULL;
	al_uint32 curr_time = 0;
	al_uint8 schd_index = 0;
	al_uint32 trigger_count = 0;

	if (AL_FAILURE == AL_Time_GetCurTimeInSecs(&curr_time))
	{
		SCH_ERROR("FAILED TO GET CURR TIME");
		return AL_FAILURE;
	}

	TO_ROUND_OFF(curr_time);

	SCH_SEM_TAKE(sch_prot_id, GL_INFINITE_WAIT);

	while ((sch_head_list != NULL) && (sch_head_list->sch_time <= curr_time))
	{
		if (sch_head_list->sch_enable == al_true)
		{
			if (sch_head_list->sch_call_back != NULL)
			{
				(sch_head_list->sch_call_back)((sch_head_list->sch_detail.sch_event & (~AL_SCH_TYPE_MASK)),\
					sch_head_list->sch_id, &sch_head_list->sch_detail);
			}
			else if (g_app_callbck != NULL)
			{
				(g_app_callbck)((sch_head_list->sch_detail.sch_event & (~AL_SCH_TYPE_MASK)),
					sch_head_list->sch_id, &sch_head_list->sch_detail);
			}
		}

		sch_list = sch_head_list;
		sch_head_list = sch_head_list->sch_next;

		if ( ( sch_list->sch_detail.sch_event & AL_SCH_TYPE_MULTI_EVENT) &&
			(sch_list->sch_occur < AL_SCH_MAX_NO_OF_CALLBACK))
		{
			for ( schd_index = sch_list->sch_occur + 1; schd_index < AL_SCH_MAX_NO_OF_CALLBACK ; schd_index++ )
			{
				if (sch_list->sch_detail.app_schd_time[schd_index] == 0 )
				{
					sch_list->sch_occur = AL_SCH_MAX_NO_OF_CALLBACK;
					sch_list->sch_time = sch_list->sch_detail.sch_time;
					break;
				}
				else if ((sch_list->sch_detail.sch_time - curr_time) > sch_list->sch_detail.app_schd_time[schd_index])
				{
					sch_list->sch_time += (sch_list->sch_detail.app_schd_time[sch_list->sch_occur] - sch_list->sch_detail.app_schd_time[schd_index]);
					sch_list->sch_occur = schd_index;
					break;
				}
				else if ((sch_list->sch_detail.sch_time - curr_time) < sch_list->sch_detail.app_schd_time[schd_index]
					&& (sch_list->sch_detail.sch_time - curr_time) > 0)
				{
					//sch_list->sch_time += (sch_list->sch_detail.app_schd_time[sch_list->sch_occur] - sch_list->sch_detail.app_schd_time[schd_index]);
					sch_list->sch_time  = curr_time + 1;
					sch_list->sch_occur = schd_index;
					break;
				}
			}
			if ( schd_index >= AL_SCH_MAX_NO_OF_CALLBACK )
			{
				sch_list->sch_occur = AL_SCH_MAX_NO_OF_CALLBACK;
				sch_list->sch_time = sch_list->sch_detail.sch_time;
			}
			if(AL_SUCCESS != SCH_insert_event(sch_list))
			{
				SCH_ERROR("UN ABLE TO RE-SCHEDULE");
			}
				continue;
		}

		/*check type and reschedule if repetitive*/
		if (sch_list->sch_detail.sch_event & AL_SCH_TYPE_REPEAT)
		{
			trigger_count = ((curr_time - sch_list->sch_time) / sch_list->sch_detail.sch_snooze)+1;
			sch_list->sch_time += (trigger_count * sch_list->sch_detail.sch_snooze);

			sch_list->sch_detail.sch_time = sch_list->sch_time;

			if(sch_list->sch_detail.sch_event & AL_SCH_TYPE_MULTI_EVENT)
			{
				for ( schd_index = 0;schd_index < AL_SCH_MAX_NO_OF_CALLBACK ; schd_index++ )
				{
					if (sch_list->sch_detail.app_schd_time[schd_index] == 0 )
					{
						sch_list->sch_occur = AL_SCH_MAX_NO_OF_CALLBACK;
						break;
					}
					else if ((sch_list->sch_time - curr_time) > sch_list->sch_detail.app_schd_time[schd_index])
					{
						sch_list->sch_occur = schd_index;
						sch_list->sch_time -= sch_list->sch_detail.app_schd_time[schd_index];
						break;
					}
				}
				if( schd_index >= AL_SCH_MAX_NO_OF_CALLBACK )
				{
					sch_list->sch_occur = AL_SCH_MAX_NO_OF_CALLBACK;
				}
			}
			else
			{
				sch_list->sch_occur = AL_SCH_MAX_NO_OF_CALLBACK;
			}

			if(AL_SUCCESS != SCH_insert_event(sch_list))
			{
				SCH_ERROR("UN ABLE TO RE-SCHEDULE");
			}

		}
		else
		{
			GL_MemFree(sch_list);
		}

		/* Again get the current Time */
		if(AL_FAILURE == AL_Time_GetCurTimeInSecs(&curr_time))
		{
			SCH_ERROR("FAILED TO GET CURR TIME");
			return AL_FAILURE;
		}
	}

	SCH_SEM_GIVE(sch_prot_id);

	return AL_SUCCESS;
}

/************************************************************************
 ** name		:SCH_get_next_schedule_time()
 **
 ** description		: returns the time for next schedule
 **
 ** input parameters	: Pointer to time
 **
 ** output parameters	:   Time
 **
 ** return type		: AL_Return_t SUCCESS or FAILURE
 ************************************************************************/
static AL_Return_t SCH_get_next_schedule_time(AL_SCH_time_t *sch_time)
{
	AL_Return_t ret_val = AL_FAILURE;
	al_uint32 curr_time = 0;
	SCH_list_t *sch_list = NULL;

	if (sch_time == NULL)
	{
		SCH_ERROR("INVALID PARAMS");
		return ret_val;
	}

	*sch_time = 0;

	if(AL_FAILURE == AL_Time_GetCurTimeInSecs(&curr_time))
	{
		SCH_ERROR("FAILED TO GET CURR TIME");
		return AL_FAILURE;
	}

	SCH_SEM_TAKE(sch_prot_id, GL_INFINITE_WAIT);

	/* Search for the next time to schedule which are enabled */
	sch_list = sch_head_list;

	while (sch_list != NULL)
	{
		if (sch_list->sch_enable == al_true)
		{
			*sch_time = sch_head_list->sch_time - curr_time;
			ret_val = AL_SUCCESS;
			break;
		}
		sch_list = sch_list->sch_next;
	}

	SCH_SEM_GIVE(sch_prot_id);
	
	return ret_val;
}

/************************************************************************
 ** name		:SCH_sig_handler()
 **
 ** description		: sig_handler api for alarm signal.
 **
 ** input parameters	: NONE
 **
 ** output parameters	:   None
 **
 ** return type		: None
 ************************************************************************/
void SCH_timer_callback(void *pArg)
{
	timer_trigger_flag = al_true;
#ifdef NON_TASKPROCESS
		SCH_handler();
#else
		SCH_SEM_GIVE(sch_sem_id);
#endif
}

/************************************************************************
 ** name				:AL_SCH_SetDefaultSettings()
 **
 ** description		: this API is called during master reset;empties the sch_list;
 **
 ** input parameters	: none
 **
 ** output parameters	: none
 **
 ** return type		: AL_SUCCESS / AL_FAILURE
 ************************************************************************/
AL_Return_t AL_SCH_SetDefaultSettings(void)
{
	SCH_list_t *sch_list;
	SCH_list_t *sch_prev = NULL;

	SCH_SEM_TAKE(sch_prot_id, GL_INFINITE_WAIT);

	sch_list = sch_head_list;

	/* empty the list */
	while (sch_list != NULL)
	{
		sch_prev = sch_list;
		sch_list = sch_list->sch_next;

		GL_MemFree(sch_prev);
	}

	sch_head_list = NULL;

	SCH_SEM_GIVE(sch_prot_id);
#ifdef NON_TASKPROCESS
	SCH_handler();
#else
	SCH_SEM_GIVE(sch_sem_id);
#endif

	return AL_SUCCESS;
}

/************************************************************************
 ** name				: SCH_get_schd_events()
 **
 ** description			: this API gives no of already scheduled events
 **
 ** input parameters	: none
 **
 ** output parameters	: no_of_schd_events
 **
 ** return type			: AL_SUCCESS / AL_FAILURE
 ************************************************************************/
static AL_Return_t SCH_get_schd_events(al_uint16 *no_of_schd_events)
{
	SCH_list_t *sch_list;

	if ( no_of_schd_events == NULL )
	{
		SCH_ERROR("INVALID PARAMETER PASSED");
		return AL_INVALID_PARAM;
	}

	sch_list = sch_head_list;

	while (sch_list != NULL)
	{
		sch_list = sch_list->sch_next;
		*no_of_schd_events += 1;
	}

	return AL_SUCCESS;
}

/******************************************************************************
 ** $Log: AL_scheduler.c,v $
******************************************************************************/
