package controller

import (
	"context"
	"netease_control_center/globals"
	"netease_control_center/interfaces/log"
	. "netease_control_center/interfaces/schedule"
	"netease_control_center/interfaces/vui"
	"netease_control_center/modules/hardware/led_mcu"
	"netease_control_center/modules/hardware/mcu_leds/atservice"
	"netease_control_center/modules/player"
	"netease_control_center/nembd/controller/adau1761"
	"sync"
	"time"
)

const (
	modename_alarmTask  = "alarm_service"
	alarmTotalRingTimes = 3
	alarmDelayMinute    = 10
	alarmLastRingTime   = 60 * time.Second
)

var (
	lock_alarmTask sync.Mutex

	alarmCtx, alarmCtxCancel = context.WithTimeout(context.Background(), 60*time.Second)

	curAlarm *globals.DbusAlarmRemind = nil
	toAlarm  *globals.DbusAlarmRemind = nil

	alarmtaskhd TaskHd
	preVolume   int
)

func alarmTask_Interrupt(ByWho string) {
	log.Println(log.DebugLog, modename_alarmTask, " be interrupted by ", ByWho)
	lock_alarmTask.Lock()
	defer lock_alarmTask.Unlock()
	if curAlarm != nil && ByWho == ModuleName_Key {
		player.SoundEffectPlayCmd(globals.CMD_SE_PLAY, vui.SoundNotice_Scaozuofankui001.GetPath())
	}

	if curAlarm != nil && IsUserEvent(ByWho) == true && alarmtaskhd.GetTaskStatus() == TaskStatus_Running && curAlarm.DelayTimer != nil {
		curAlarm.DelayTimer.Stop()
		log.Println(log.DebugLog, "Alarm is running, do timer stop!")
	} else {
		log.Println(log.DebugLog, "Alarm timer is not stop!")
	}

	alarmCtxCancel()
	curAlarm = nil

	vui.IconLed_IconNaozhong001.Clear()
	led_mcu.DirectLedClear(atservice.GUIDE_EFFECT_ID_EXEC_CMD)
}

func alarmTask_Resume() {
	lock_alarmTask.Lock()
	defer lock_alarmTask.Unlock()
	if tmp := toAlarm; tmp != nil {
		toAlarm = nil
		alarmPlay(tmp, true)
		log.Println(log.DebugLog, modename_alarmTask, " resume !", tmp)
	}
}

func getNextAlarmTime() time.Duration {
	now := time.Now()
	// 设置整点的延迟闹钟, 为了防止提醒和闹钟同时到的情况, 延迟闹钟晚1秒钟
	return time.Duration(60-now.Second()+1)*time.Second + (alarmDelayMinute-1)*time.Minute
}

func alarmPlay(alarm *globals.DbusAlarmRemind, canExec bool) {
	log.Printf(log.DebugLog, "coming alarm:%+v\n", *alarm)
	if curAlarm != nil {
		log.Printf(log.DebugLog, "cur alarm:%+v\n", *curAlarm)
	}

	if time.Now().Sub(alarm.SCmdTime) > time.Minute {
		log.Printf(log.DebugLog, "New alarm is timeout > 1min! %+v", *alarm)
		if alarm == curAlarm {
			curAlarm = nil
		}
		return
	}

	//check the alarm ringing if timeout, ringing 0-58s do nothing
	if tmpalarm := curAlarm; tmpalarm != nil && time.Now().Sub(tmpalarm.SCmdTime) >= (alarmLastRingTime-2*time.Second) {
		log.Println(log.DebugLog, "curAlarm is timeout!")
		curAlarm = nil
	}

	//check toAlarm if timeout
	if tmpalarm := toAlarm; tmpalarm != nil && time.Now().Sub(tmpalarm.SCmdTime) >= (alarmLastRingTime-2*time.Second) {
		log.Println(log.DebugLog, "toAlarm is timeout!")
		toAlarm = nil
	}

	if tmpalarm := curAlarm; tmpalarm != nil && alarm.RingTimes > tmpalarm.RingTimes {
		log.Println(log.DebugLog, "the alarm coming ringtimes > curalarm! do exit!")
		return
	}

	//if tmpalarm := curAlarm; tmpalarm != nil && alarm.RingTimes <= tmpalarm.RingTimes && tmpalarm.DelayTimer != nil {
	//	log.Println(log.DebugLog, "the alarm coming ringtimes <= curalarm! stop curAlarm timer!")
	//	tmpalarm.DelayTimer.Stop()
	//}

	if canExec == false {
		log.Printf(log.DebugLog, "Alarm can not play(not privilige):%+v\n", *alarm)

		toAlarm = alarm
		curAlarm = nil

		if alarm.RingTimes < (alarmTotalRingTimes - 1) {
			nextAlarm := *alarm
			nextAlarm.RingTimes++
			nextTime := getNextAlarmTime()
			nextAlarm.DelayTimer = nil
			log.Println(log.DebugLog, "Will set ", nextTime, " after timer!")
			toAlarm.DelayTimer = time.AfterFunc(nextTime, func() {
				log.Printf(log.DebugLog, "alarm timer is timeout:%+v\n", *alarm)
				nextAlarm.SCmdTime = time.Now()
				SendSCmd(SCMD_SuperTask_Alarm_Ring, nextAlarm)
			})
		}

		return
	}

	log.Printf(log.DebugLog, "Will play alarm: %+v\n", *alarm)

	var showpath string
	switch alarm.ResType {
	case "voiceId":
		showpath = vui.SoundNotice(alarm.ResValue).GetPath()
	}
	if len(showpath) == 0 {
		showpath = vui.SoundNotice_Snaozhong001.GetPath()
	}

	if alarm.RingTimes < (alarmTotalRingTimes-1) && alarm.DelayTimer == nil {
		nextAlarm := *alarm
		nextAlarm.RingTimes++
		nextTime := getNextAlarmTime()
		log.Println(log.DebugLog, "Will set ", nextTime, " after timer!")
		nextAlarm.DelayTimer = nil
		alarm.DelayTimer = time.AfterFunc(nextTime, func() {
			nextAlarm.SCmdTime = time.Now()
			SendSCmd(SCMD_SuperTask_Alarm_Ring, nextAlarm)
		})
	} else {
		log.Println(log.DebugLog, "Do not set next alarm!")
	}

	var AlarmCb func(id uint32, reason NoticeStopReason)
	AlarmCb = func(id uint32, reason NoticeStopReason) {
		if reason.IsStart() {
			return
		}

		lock_alarmTask.Lock()
		defer lock_alarmTask.Unlock()

		log.Println(log.DebugLog, "Alarm callback!", *alarm, "task:", alarmtaskhd.GetTaskStatus(), "reason:", reason)
		if (reason == NoticeStopReason_Complete || reason == NoticeStopReason_Error) && alarmtaskhd.GetTaskStatus() == TaskStatus_Running {
			log.Printf(log.DebugLog, "Cur ring alarm:%+v\n", *alarm)
			PlayNoticeAsync(globals.AdSrcType_FILE, showpath, globals.CONST_AUDIO_ID, false, alarmCtx, AlarmCb)
		} else {
			log.Println(log.DebugLog, "Alarm is finished!")
			if alarm == curAlarm || curAlarm == nil {
				curAlarm = nil
				//here equal means cur play alarm is last set alarm, and after finish restore the volume, clear icon
				curVol, _ := adau1761.GetVol()
				if curVol != globals.DefaultVol.GetCfgListVui() && preVolume != curVol {
					log.Println(log.DebugLog, "Restore alarm, do nothing, maybe volume change:", curVol)
				} else {
					log.Println(log.DebugLog, "Alarm Volume has restore:", preVolume)
					VolSet(preVolume)
				}
				vui.IconLed_IconNaozhong001.Clear()
				led_mcu.DirectLedClear(atservice.GUIDE_EFFECT_ID_EXEC_CMD)
				curAlarm = nil
				SendEventByYunxin(DeviceEventType_Alarm, DeviceEventAction_End, map[string]interface{}{"id": alarm.Id, "timestamp": alarm.ComingTime.Unix() * 1000, "ringtimestamp": time.Now().Unix() * 1000})
			} else {
				log.Println(log.DebugLog, "Alarm reset newly!!")
			}

			SendSCmd(SCMD_SChedule)
		}
	}

	SendEventByYunxin(DeviceEventType_Alarm, DeviceEventAction_Start, map[string]interface{}{"id": alarm.Id, "timestamp": alarm.ComingTime.Unix() * 1000, "ringtimestamp": time.Now().Unix() * 1000})
	if curAlarm == nil {
		//to alarm is nil means cur no alarm ring and need backup volume!
		time.Sleep(10 * time.Millisecond)
		preVolume, _ = adau1761.GetVol()
		if preVolume > globals.DefaultVol.GetCfgListVui() {
			VolSet(preVolume)
		} else {
			VolSet(globals.DefaultVol.GetCfgListVui())
		}
		tmpvol, _ := adau1761.GetVol()
		log.Println(log.DebugLog, "Alarm set vol:", tmpvol)
	}
	vui.IconLed_IconNaozhong001.Show()
	led_mcu.DirectLedControl(atservice.GUIDE_EFFECT_ID_EXEC_CMD, atservice.GUIDE_ACTION_START, 0, []byte{})

	curAlarm = alarm
	alarmCtxCancel()

	alarmCtx, alarmCtxCancel = context.WithTimeout(context.Background(), alarmLastRingTime)
	avui := alarmVui(alarm.RingTimes)
	if globals.InternetConnect == true && len(avui) > 0 {
		log.Println(log.DebugLog, "will play alarm:", avui)
		PlayNoticeAsync(globals.AdSrcType_TTS, avui, globals.CONST_AUDIO_ID, false, alarmCtx, AlarmCb)
	} else {
		log.Println(log.DebugLog, "will play default alarm!", avui)
		PlayNoticeAsync(globals.AdSrcType_FILE, alarmOfflineVui(alarm.RingTimes), globals.CONST_AUDIO_ID, false, alarmCtx, AlarmCb)
	}
}

func alarmTask_exec(canExec bool, scmd SCMD, arg ...interface{}) {
	defer func() {
		if err := recover(); err != nil {
			log.Println(log.ErrorLog, err)
		}
	}()

	switch scmd {
	case SCMD_SuperTask_Alarm_Ring:
		log.Println(log.DebugLog, "alarm ring cmd come:", canExec)
		if alarm, ok := arg[0].(globals.DbusAlarmRemind); ok == true {
			lock_alarmTask.Lock()
			defer lock_alarmTask.Unlock()

			alarmPlay(&alarm, canExec)
		} else {
			log.Println(log.ErrorLog, "alarm data format error!")
		}
	}
}

func Alarm_Close() {
	log.Println(log.DebugLog, "Begin to cancle alarm!")
	alarmTask_Interrupt(ModuleName_Key)
}

func alarmVui(times int) string {
	switch times {
	case 0:
		return vui.ParseDefaultTemplate(`现在是{{timename}}{{now "3:04"}}，你有个闹钟哦`)

	case 1:
		return vui.ParseDefaultTemplate(`现在是{{timename}}{{now "3:04"}}，你原定在十分钟前设了个闹钟哦`)

	case 2:
		return vui.ParseDefaultTemplate(`现在是{{timename}}{{now "3:04"}}，你原定在二十分钟前设了个闹钟哦`)

	default:
		log.Println(log.ErrorLog, "Can not get vui, times beyond:", times, " max:", alarmTotalRingTimes)
	}

	return ""
}

func alarmOfflineVui(times int) string {
	switch times {
	case 0:
		return vui.SoundNotice_SnaozhongInternetDown1.GetPath()

	case 1:
		return vui.SoundNotice_SnaozhongInternetDown2.GetPath()

	case 2:
		return vui.SoundNotice_SnaozhongInternetDown3.GetPath()

	default:
		log.Println(log.ErrorLog, "Can not get offline vui, times beyond:", times)
	}

	return ""
}

func AlarmTaskinit() {
	var tmp RegistedTask
	var err error

	tmp.Interrupt = alarmTask_Interrupt

	tmp.GetName = func() string {
		return modename_alarmTask
	}

	tmp.IsActive = func() bool {
		return curAlarm != nil || toAlarm != nil
	}

	tmp.GetTaskLevel = func() TaskLevel {
		return TaskLevel_AlarmService
	}

	tmp.Resume = alarmTask_Resume

	tmp.GetSupportedSCmd = func() []SCMD {
		return []SCMD{SCMD_SuperTask_Alarm_Ring}
	}

	tmp.GetScheduleDomain = func() ScheduleDomain {
		return ScheduleDomain_Alarm
	}

	tmp.Do = alarmTask_exec
	tmp.CanBeInterruptByKey = func() bool {
		return true
	}

	if err, alarmtaskhd = RegisteSTask(tmp); err == nil {
		log.Printf(log.DebugLog, "Registe %s task successfully!!\n", tmp.GetName())
	} else {
		log.Printf(log.ErrorLog, "Registe %s task fail!!\n", tmp.GetName())
	}
}
