//#include "Include.h"

#include "Voice_Scheduler_XK.h"

/////////////////////以下为声音报警相关//////////////////////////////////////////////////////////////////////////////////
/////////////////////以下为声音报警相关//////////////////////////////////////////////////////////////////////////////////
/////////////////////以下为声音报警相关//////////////////////////////////////////////////////////////////////////////////
/////////////////////以下为声音报警相关//////////////////////////////////////////////////////////////////////////////////
/////////////////////以下为声音报警相关//////////////////////////////////////////////////////////////////////////////////
/////////////////////以下为声音报警相关//////////////////////////////////////////////////////////////////////////////////
/////////////////////以下为声音报警相关//////////////////////////////////////////////////////////////////////////////////
/////////////////////以下为声音报警相关//////////////////////////////////////////////////////////////////////////////////
/////////////////////以下为声音报警相关//////////////////////////////////////////////////////////////////////////////////
#define Sound_PlayAgain 1 //同一个声音报警需重新触发
#define Sound_NoAgain 0
//当前需要播放报警的状态
typedef enum
{
	Sound_Play_Waiting = 0, //报警触发了，但是当前还没有在播放
	Sound_PlayIng,			//报警触发了，在播放中,已经开始播放

	//报警播放完毕，不能自动清零，需外部逻辑清除报警后才能重新触发
} _Sound_Play_Result;
//记录各个声音报警当前信息
typedef struct
{
	//	_SoundAlarm   SoundAlarmInd;		//声音报警的序号  //调试时使用
	INT8U PlayTimeRecord; //该报警播放次数记录
	INT8U Voic_Trigger;   //该报警有多少个条件被触发了
} _SoundAlarmVariate;

typedef struct
{
	INT8U SoundAlarmInd; //声音报警的序号    	//_SoundAlarm
	//当前报警应该只有两个状态，等待被播放，正在播放
	//至于完成播放时该报警索引号就会被改变，所以不会有这个状态
	INT8U Play_Result; //当前报警是否被触发，及播放状态    //_Sound_Play_Result
} _SoundAlarm_Current;

INT8U   Sound_Num_MAX =0;
_SoundAlarmVariate SoundAlarmList[SoundAlarm_MAX];
_SoundAlarm_Current SoundAlarm_Current;
INT8U Sound_Play_Switch; //声音报警播放是否受声音调度控制标志
INT8U Sound_Replay;		 //该声音报警需重新触发   //或者每个报警增加一个变量控制当前是否需要清零标志
//声音报警相关参数初始化//上30电调用一次即可
void Sound_Scheduler_Init(_SoundAlarm MAX_NUM)
{
	INT8U i = 0;
	Sound_Num_MAX =  MAX_NUM;
	Sound_Play_Switch = Sound_Play_Control;
	Sound_Replay = Sound_NoAgain;
	for (i = 0; i < Sound_Num_MAX; i++)
	{
		SoundAlarmList[i].PlayTimeRecord = 0;
		SoundAlarmList[i].Voic_Trigger = 0;
	}
	SoundAlarm_Current.SoundAlarmInd = Sound_Num_MAX;

	SoundAlarm_Current.Play_Result = Sound_Play_Waiting;
}
//声音播放受声音调度控制时执行一次  Sound_Play_Control
//声音播放不受声音调度控制时执行一次      Sound_Play_NOTControl     //例如2F时   自检期间
//初始化默认受控
void Sound_Play_Switch_Handle(_Sound_Play_Control ON_OFF)
{
	Sound_Play_Switch = ON_OFF;
}
//查找当前需要播放的报警//上一个报警播放完了或者被打断了删除了
void Sound_Seek(void)
{
	INT8U i = 0;
	_SoundAlarm Index_buf = Sound_Num_MAX;
	//查找优先级最高的为下一个要播放的声音报警，没有则返回无效值
	for (i = 0; i < Sound_Num_MAX; i++)
	{
		//已经触发的声音报警，没有播放完成//有条件触发，且播放次数没到最大
		if ((SoundAlarmList[i].Voic_Trigger) && (SoundAlarmList[i].PlayTimeRecord < SoundAlarmAttribute[i].PlayNumMax))
		{
			//已经获取到一个有效的报警索引号
			if (Index_buf < Sound_Num_MAX)
			{
				//查找优先级最高的报警
				if (SoundAlarmAttribute[Index_buf].Prio > SoundAlarmAttribute[i].Prio)
				{
					Index_buf = i;
				}
			}
			else //首次进入
			{
				Index_buf = i;
			}
		}
	}
	//获取下一个要播放的报警序号
	SoundAlarm_Current.SoundAlarmInd = Index_buf;
	//获取到的报警有效，则设置状态为等待播放
	//无效的时候也应该是等待播放状态//只有两个状态
	//if(SoundAlarm_Current.SoundAlarmInd<Sound_Num_MAX)
	{
		SoundAlarm_Current.Play_Result = Sound_Play_Waiting;
	}
}
void Sound_Delete(_SoundAlarm AlarmVoice, _Alarm_Trigger_Enum Voice_Trigger)
{
	//删除的声音报警有效
	if (AlarmVoice < Sound_Num_MAX)
	{
		//删除的报警有触发条件存在则删除，没有那删除个什么
		if (SoundAlarmList[AlarmVoice].Voic_Trigger)
		{
			SoundAlarmList[AlarmVoice].Voic_Trigger &= (~Voice_Trigger);
			if (0 == SoundAlarmList[AlarmVoice].Voic_Trigger)
			{
				SoundAlarmList[AlarmVoice].PlayTimeRecord = 0;
				//要删除的就是当前播放的报警
				if (SoundAlarm_Current.SoundAlarmInd == AlarmVoice)
				{
					//获取下一个要播放的报警序号
					Sound_Seek();
				}
			}
		}
	}
}
//某个声音报警完整播放完一次之后调用一次
//输入声音报警索引号
//当前播放完成的可能不是当前逻辑判断出来需要播放的
//举例：当前的被新的打断，那么新的就是时当前需要拨的，老的才是传入的报警
void Sound_Play_Once_Done(_SoundAlarm AlarmVoice)
{
	if (AlarmVoice >= Sound_Num_MAX)
	{
		return;
	}
	//声音播放不受调度控制时，不执行内部操作
	if (Sound_Play_NOTControl == Sound_Play_Switch)
	{
		return;
	}
	if (Sound_Replay == Sound_PlayAgain) //当前播放的报警被强制重新播放
	{
		Sound_Replay = Sound_NoAgain;
		SoundAlarmList[AlarmVoice].PlayTimeRecord = 0;
		return;
	}
	//需要一直播放的报警不进行次数累加
	if (PlayForeverNUM == SoundAlarmAttribute[AlarmVoice].PlayNumMax)
	{
		return;
	}
	if (SoundAlarmList[AlarmVoice].PlayTimeRecord < SoundAlarmAttribute[AlarmVoice].PlayNumMax)
	{
		SoundAlarmList[AlarmVoice].PlayTimeRecord++;
	}
	//播放次数达到上限
	if (SoundAlarmList[AlarmVoice].PlayTimeRecord >= SoundAlarmAttribute[AlarmVoice].PlayNumMax)
	{
		//正常播放完毕时根据触发类型不同   边沿类的触发一次删除操作，不必外部删除报警，持续类的不删除，设置标志表示播放完成
		if (SoundAlarmAttribute[AlarmVoice].TYPE_Trigger == Trigger_Edge)
		{
			//删除之后也会查找下一个要播放的报警
			Sound_Delete(AlarmVoice, Alarm_Trigger_ALL);
		}
		else if (SoundAlarmAttribute[AlarmVoice].TYPE_Trigger == Trigger_Continue)
		{
			//获取下一个要播放的报警序号
			Sound_Seek();
		}
	}
}
//具体声音播放驱动调用
//当前播放的声音报警，开始播出声音了，
void Sound_Play_Begin(_SoundAlarm AlarmVoice)
{
	//当前播放的声音报警，一定和表示当前报警变量中的序号一样
	//声音驱动那就是获取的这个变量来进行声音播放的
	if (AlarmVoice != SoundAlarm_Current.SoundAlarmInd)
	{
		return;
	}
	//声音播放不受调度控制时，不执行内部操作
	if (Sound_Play_NOTControl == Sound_Play_Switch)
	{
		return;
	}
	SoundAlarm_Current.Play_Result = Sound_PlayIng; //真正在播放声音已经开始了
}
//当某个报警被打断时执行的操作    //是否区分打断的是不是当前播放的
//高优先级在播放，低优先级触发不能播放也算被打断
//被打断的报警索引//该报警是否有新条件触发1有。0无
void Sound_Play_Break(_SoundAlarm AlarmVoice, INT8U NEW)
{
	if (AlarmVoice >= Sound_Num_MAX)
	{
		return;
	}
	//被打断，且该报警是不可恢复
	if (SoundAlarmAttribute[AlarmVoice].TYPE_Recover == Recover_Disable)
	{
		//边沿类报警//播放完成后需要自动删除
		if (SoundAlarmAttribute[AlarmVoice].TYPE_Trigger == Trigger_Edge)
		{
			Sound_Delete(AlarmVoice, Alarm_Trigger_ALL);
		}
		else //持续类
		{
			//播放次数赋值最大值
			SoundAlarmList[AlarmVoice].PlayTimeRecord = SoundAlarmAttribute[AlarmVoice].PlayNumMax;
		}
	}
	else //该报警可恢复
	{
		//该报警需要重新计数，所以清零
		if (SoundAlarmAttribute[AlarmVoice].Break_AgainOrContinue == Break_Sound_Again)
		{
			SoundAlarmList[AlarmVoice].PlayTimeRecord = 0;
			//当前播放的报警被打断，且该报警可恢复， //且需从零开始
			if (SoundAlarm_Current.SoundAlarmInd == AlarmVoice)
			{
				Sound_Replay = Sound_PlayAgain; //报警重新播放
			}
		}
		else //     //报警可恢复//恢复后续播
		{
			if (NEW)
			{
				//被打断时来了新条件计数清零
				if (Break_Condition_Sound_Again == SoundAlarmAttribute[AlarmVoice].Break_Condition_AgainOrContinue)
				{
					SoundAlarmList[AlarmVoice].PlayTimeRecord = 0;
				}
				// else        //不对播放计数有影响，不动作
			}
			//else///无新条件，那么次数无变化，因此处是续播
		}
		//不重新计数的，继续播放的则计数变量保持不变即可
		//不必考虑边沿类，还是持续类，都一样
	}
}
//声音请求立即播放  //当前声音播放时，重复请求同一声音。则忽略请求。
//不同优先级的请求在参与比较之后会播放优先级最高的
//一般用于没有界面报警时调用   仅声音报警。或配合LED报警时。
//单次请求，触发一次调用一次
void Sound_Request(_SoundAlarm AlarmVoice, _Alarm_Trigger_Enum Voice_Trigger)
{
	//触发的的声音报警无效
	if (AlarmVoice >= Sound_Num_MAX)
	{
		return;
	}
	///////对报警条件的处理////////////////
	//新触发条件触发了
	if (0 == (SoundAlarmList[AlarmVoice].Voic_Trigger & Voice_Trigger))
	{
		SoundAlarmList[AlarmVoice].Voic_Trigger |= Voice_Trigger;
	}
	else //没有新条件触发，那么直接退出
	{
		return;
	}
	////////////////新触发条件对报警次数的处理//////////////////////////

	//仅能表示当前申请的是即将要播放的，是否在播放内部继续判断
	if (SoundAlarm_Current.SoundAlarmInd == AlarmVoice)
	{
		//当前报警已经在播放了
		//此处是为了防止，两个条件同时成立，程序上连续执行请求
		//第一个条件请求完成之后，当前报警已经是这个了，第二个条件再次请求时
		//发现申请的报警就是当前报警，它会等当前一个声音周期播放完成之后清零，这样实际效果就是多一声
		//通过判断当前报警是否在播放来决定是否清零动作
		//当前播放那么一定播放次数没有达到上限
		if (SoundAlarm_Current.Play_Result == Sound_PlayIng)
		{
			if (Condition_Sound_Again == SoundAlarmAttribute[SoundAlarm_Current.SoundAlarmInd].Condition_AgainOrContinue)
			{
				//在当前一个完整周期播放完成之后再清零播放计数变量
				Sound_Replay = Sound_PlayAgain;
			}
		}
		// else    //表示还没播放     这时次数一定是零，所以不必等待播放完成后再次清零

		return;
	}
	//当前申请的一定不是当前在播放的   //此时还没进行优先级比较有可能当前报警为空
	//或者该报警没有被打断或者打断其他报警//所以除非次数上限了，其他的情况留到打断处理函数内添加
	else
	{
		//播放次数达到上限  //有新条件触发，那么计数清零
		if (SoundAlarmList[AlarmVoice].PlayTimeRecord >= SoundAlarmAttribute[AlarmVoice].PlayNumMax)
		{
			SoundAlarmList[AlarmVoice].PlayTimeRecord = 0;
		}
		else //播放次数没有达到上限    //可能没开始播放过 ，也可能播放一半后被打断
		{
			//此处逻辑在打断函数内处理
		}
	}
	////////////报警互相打断的处理
	//表示当前没有声音报警播放//或者已经播放完毕，当前空闲
	if ((SoundAlarm_Current.SoundAlarmInd >= Sound_Num_MAX))
	{
		SoundAlarm_Current.SoundAlarmInd = AlarmVoice;
		SoundAlarm_Current.Play_Result = Sound_Play_Waiting;
		//理论上此时播放次数一定为零//报警从无到有的过程
		//SoundAlarmList[AlarmVoice].PlayTimeRecord = 0;
	}
	else //当前有需要播放的报警
	{
		//当前播放报警 优先级低于刚刚触发的。数值小优先级高
		//当前播放的被打断
		if ((SoundAlarmAttribute[SoundAlarm_Current.SoundAlarmInd].Prio >
			 SoundAlarmAttribute[AlarmVoice].Prio))
		{
			Sound_Play_Break(SoundAlarm_Current.SoundAlarmInd, 0);
			SoundAlarm_Current.Play_Result = Sound_Play_Waiting;
			SoundAlarm_Current.SoundAlarmInd = AlarmVoice;
			//理论上此时播放次数一定为零//新报警打断了当前的报警
			//SoundAlarmList[AlarmVoice].PlayTimeRecord = 0;
		}
		//不是当前在播放的报警，如果是前面已经return出去了
		else if ((SoundAlarmAttribute[SoundAlarm_Current.SoundAlarmInd].Prio ==
				  SoundAlarmAttribute[AlarmVoice].Prio))
		{
			//优先级相同，报警可能不同，不能打断
			Sound_Play_Break(AlarmVoice, 1);
		}
		else //现在申请的报警优先级低于当前播放的，不能立即播放
		{
			Sound_Play_Break(AlarmVoice, 1);
		}
	}
}

//返回声音报警索引号，播放次数。
//有2F服务时一样返回需播放的报警声音。外部逻辑判断当时是否播放报警声音，还是播放2F需求的声音
_SoundAlarm Popup_Get_Current_Sound(void)
{
	//声音播放不受调度控制时，固定返回最大值，不进行声音播放
	if (Sound_Play_NOTControl == Sound_Play_Switch)
	{
		return  Sound_Num_MAX;
	}
	return SoundAlarm_Current.SoundAlarmInd;
}

/*

//声音报警固定信息结构体      
typedef struct
{
 //优先级         //数字小的优先级高   //原则上不能有优先级相等的报警除非报警条件互斥
	INT8U     Prio;   	 
//该报警需要播放时长   //最大播放次数，播放完整一个报警声音的次数
//需要一直播放时填PlayForeverNUM	
	INT8U     PlayNumMax;
//跳变沿类的报警在触发之后策略内会自行删除（待播放完毕之后），外部应用不必删除。
//需要再次触发报警时直接再次申请即可
//持续类的，需手动删除报警后方可再次触发
	INT8U      TYPE_Trigger:1;     // 报警触发类型	
//高优先级在播放，低优先级无法立即播放，待高优先级播放完之后    条件仍存在,
//或者声音报警播放过程中被打断，高优先级报警播放完毕后，被打断报警触发条件仍存在,
//是否允许再次触发报警   不可恢复的 需要条件取消后再次触发   
//一般跳边沿触发的在比较优先级失败后不再恢复。持续类的再高优先级消失后可恢复。  
	INT8U      TYPE_Recover  :1;
//再次触发报警时是继续之前的报警次数播放，还是从0开始
//报警取消之后再次触发，那播放次数一定是从0开始	    
	INT8U      Break_AgainOrContinue:1;      //	  
//某个报警播放时被打断，且可恢复且是续播//被打断期间有新条件触发，此时是否清零计数变量    	
	INT8U      Break_Condition_AgainOrContinue:1;   	
//当报警正在播放时，又有新的条件触发报警此时
//是清零播放次数，还是保持计数不变
//注意当一个条件触发，但播放次数已经达到最大之后又有新的条件触发那么此时一定重新计数
	INT8U      Condition_AgainOrContinue:1;  
   
	INT8U       :3;

} _SoundAlarmAttribute; 
*/
/////////////////////以上为声音报警相关//////////////////////////////////////////////////////////////////////////////////
/////////////////////以上为声音报警相关//////////////////////////////////////////////////////////////////////////////////
/////////////////////以上为声音报警相关//////////////////////////////////////////////////////////////////////////////////
/////////////////////以上为声音报警相关//////////////////////////////////////////////////////////////////////////////////
/////////////////////以上为声音报警相关//////////////////////////////////////////////////////////////////////////////////
/////////////////////以上为声音报警相关//////////////////////////////////////////////////////////////////////////////////
/////////////////////以上为声音报警相关//////////////////////////////////////////////////////////////////////////////////
