package player

import (
	"container/list"
	"context"
	"encoding/json"
	"fmt"
	. "netease_control_center/globals"
	"netease_control_center/interfaces/eventPoint"
	"netease_control_center/interfaces/innerTask"
	"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"
	_ "sync"
	"time"
)

const (
	NOTICE_PLAYER_TASK       = "notice"
	NOTICE_PLAYER_PARAMS_LEN = 6
)

var (
	ctxNoticePlayer, ctxCancleNoticePlayer = context.WithCancel(context.Background())
	interruptctx, interruptctxcancel       = context.WithCancel(context.Background())

	ttsList *TtsList = &TtsList{List: list.New(), CurPos: nil}

	//ttsPlayLock sync.Mutex // 对外接口的互斥锁，所有对外的控制接口都需要获取到锁才能执行

	noticePlayerIsActive  bool = false
	curNoticePlayerParams *schedule.NoticePlayerParams
)

func initInterruptctx() {
	interruptctx, interruptctxcancel = context.WithTimeout(context.Background(), 50*time.Millisecond)
}

/********************
 * notice api for outside
 */
func NoticePlayerSendSCmd(scmd schedule.SCMD, tts string, uuid string, srcType AdSrcType, level TtsLevel, needRepeat bool, keep bool) {
	setNoticePlayerActive(true)

	schedule.SendSCmd(scmd, tts, uuid, srcType, level, needRepeat, keep)
}

func GetDefaultNoticePlayerParams() schedule.NoticePlayerParams {
	var tmp schedule.NoticePlayerParams

	tmp.NeedRepeat = true
	tmp.Keep = false
	tmp.IconIn.Name = ""
	tmp.IconOut.Name = ""
	tmp.LedOn.EffectId = atservice.GUIDE_EFFECT_ID_DO_NOTHING
	tmp.LedOff.EffectId = atservice.GUIDE_EFFECT_ID_DO_NOTHING

	tmp.Level = TTS_LEVEL_1
	tmp.SpecialCmd = ""
	tmp.SpecialFunc = func(arg schedule.TtsPlayerArg) {}
	tmp.BeforeSpecialFunc = func() {}
	tmp.BeforeExecFunc = func() {}
	tmp.Uuid = CONST_AUDIO_ID

	return tmp
}
func NoticePlayerSendSCmd2(scmd schedule.SCMD, params schedule.NoticePlayerParams) {
	setNoticePlayerActive(true)

	schedule.SendSCmd(scmd, params)
}

/********************
 * direct tts api
 */
func noticeTtsStart(id uint32, srcType AdSrcType, src string, uuid string) {
	noticePlayCmdSync(true, CMD_PLAYER_AUDIO_PLAY_NOTICE,
		id, src, uuid, srcType, TTS_LEVEL_HIGH, false, false)
}

func noticeTtsStop(id uint32) {
	noticePlayCmdSync(true, CMD_PLAYER_AUDIO_STOP,
		id, "", "", AdSrcType_STOP_REPLAY_MUSIC, TTS_LEVEL_HIGH, false, false)
}

/********************
 * task api
 */
func noticeInterrupt(byWho string) {
	log.Println(log.DebugLog, "noticeInterrupt by:", byWho)

	if active := noticeIsActive(); active {
		initInterruptctx()
		ctxCancleNoticePlayer()
		<-interruptctx.Done()
	} else {
		ctxCancleNoticePlayer()
	}

	ttsList.Init()

	setNoticePlayerActive(false)
}

func noticeResume() {
	log.Println(log.DebugLog, "noticeResume")
	//todo
	if ttsList.GetLen() > 0 {
		execPlayLeftTts()
	} else {
		setNoticePlayerActive(false)
	}
}

/**
 * arg参数列表: id uint32, tts string, srcType AdSrcType, level TtsLevel, needRepeat bool, keep bool
 */
func noticeDo(canExec bool, scmd schedule.SCMD, arg ...interface{}) {
	switch scmd {
	case schedule.SCMD_SuperTask_Tts_Play,
		schedule.SCMD_SuperTask_Tts_Stop:

		valied, tts, uuid, srcType, level, needRepeat, keep := getNoticePlayerDoParams(arg...)
		log.Println(log.DebugLog, "Get scmd arg in wk module:", valied, tts, uuid, srcType, level, needRepeat, keep)
		if !valied {
			log.Printf(log.ErrorLog, "errmsg:%+v  arg:%+v\n", PARAMS_ERR_INVALID.Error(), arg)
			return
		}

		setNoticePlayerActive(true)

		if canExec {
			switch scmd {
			case schedule.SCMD_SuperTask_Tts_Play:
				ctxNoticePlayer, ctxCancleNoticePlayer = context.WithCancel(context.Background())

				params := schedule.NoticePlayerParams{
					SrcType:    srcType,
					Tts:        tts,
					Uuid:       uuid,
					NeedRepeat: needRepeat,
					Keep:       keep,
				}
				playNoticeAsyncExec(ctxNoticePlayer, &params)

				//if needRepeat {
				//	noticeParams := schedule.GetNoticePlayerParams(tts, uuid, srcType, level, needRepeat, keep)
				//	schedule.SetTtsRepeatInfo(noticeParams)
				//}
			case schedule.SCMD_SuperTask_Tts_Stop:
				log.Printf(log.WarningLog, "noticeDo: SCMD_SuperTask_Tts_Stop.\n\n")
			}
		} else {
			var ttsNode TtsInfo = TtsInfo{Tts: &tts, Uuid: &uuid, SrcType: srcType, Level: level, Keep: keep,
				NeedRepeat: needRepeat, Callback: nil}
			ttsList.Push(&ttsNode)
		}

	case schedule.SCMD_SuperTask_Tts_Ctrl_Play,
		schedule.SCMD_SuperTask_Tts_Ctrl_Stop:

		if 1 == len(arg) {
			switch eVal := arg[0].(type) {
			case schedule.NoticePlayerParams:
				setNoticePlayerActive(true)
				if canExec {
					switch scmd {
					case schedule.SCMD_SuperTask_Tts_Ctrl_Play:
						curNoticePlayerParams = &eVal

						ctxCancleNoticePlayer()
						ctxNoticePlayer, ctxCancleNoticePlayer = context.WithCancel(context.Background())

						//eVal.IconIn.Show()
						//led_mcu.DirectLedControl(eVal.LedOn.EffectId, eVal.LedOn.Action, eVal.LedOn.StartLampNum,
						//	eVal.LedOn.Data)
						//if eVal.BeforeSpecialFunc != nil {
						//	eVal.BeforeSpecialFunc()
						//}

						if eVal.BeforeExecFunc != nil {
							eVal.BeforeExecFunc()
						}

						playNoticeAsyncExec(ctxNoticePlayer, &eVal)

						//if eVal.NeedRepeat {
						//	schedule.SetTtsRepeatInfo(eVal)
						//}
					case schedule.SCMD_SuperTask_Tts_Ctrl_Stop:
						log.Printf(log.WarningLog, "noticeDo: SCMD_SuperTask_Tts_Ctrl_Stop.\n\n")
					}
				}
			default:
				log.Println(log.WarningLog, "noticeDo: err type")
			}
		}

	}

}

/**
 * 当前任务是不是需要执行
 */
func noticeIsActive() bool {
	return noticePlayerIsActive
}

func noticeGetName() string {
	return NOTICE_PLAYER_TASK
}

func noticeGetTaskLevel() schedule.TaskLevel {
	return schedule.TaskLevel_NoticePlayer
}

func noticeGetSupport() []schedule.SCMD {
	return []schedule.SCMD{schedule.SCMD_SuperTask_Tts_Play,
		schedule.SCMD_SuperTask_Tts_Stop, schedule.SCMD_SuperTask_Tts_Ctrl_Play, schedule.SCMD_SuperTask_Tts_Ctrl_Stop}
}

func noticePlayerCallback(id uint32, reason schedule.NoticeStopReason, params *schedule.NoticePlayerParams) {
	if reason.IsStop() {
		defer interruptctxcancel()
	}
	switch reason {
	case schedule.NoticeStart_Play:
		if params.Uuid != CONST_AUDIO_ID {
			//log.Println(log.WarningLog, "start srcUuid:", params.Uuid)
			if IsLocalAudioUuid(params.Uuid) {
				eventPoint.AudioStart(params.Uuid, params.Tts, eventPoint.EpOs_Device)
			} else {
				eventPoint.AudioStart(params.Uuid, params.Tts, eventPoint.EpOs_Server)
			}
		}

		if params.NeedRepeat {
			schedule.SetTtsRepeatInfo(*params)
		}

		led_mcu.DirectLedControl(params.LedOn.EffectId, params.LedOn.Action, params.LedOn.StartLampNum,
			params.LedOn.Data)
		params.IconIn.Show()
		if params.BeforeSpecialFunc != nil {
			params.BeforeSpecialFunc()
		}
		return

	case schedule.NoticeStopReason_Interrupt,
		schedule.NoticeStopReason_Cancel,
		schedule.NoticeStopReason_Error:

		if reason == schedule.NoticeStopReason_Error {
			if params.Uuid != CONST_AUDIO_ID {
				//log.Println(log.WarningLog, "end srcUuid:", params.Uuid)
				if IsLocalAudioUuid(params.Uuid) {
					eventPoint.AudioErrorEp(params.Uuid, params.Tts, "error", eventPoint.EpOs_Device)
				} else {
					eventPoint.AudioErrorEp(params.Uuid, params.Tts, "error", eventPoint.EpOs_Server)
				}
			}
		} else {
			if params.Uuid != CONST_AUDIO_ID {
				//log.Println(log.WarningLog, "end srcUuid:", params.Uuid)
				if IsLocalAudioUuid(params.Uuid) {
					eventPoint.AudioEnd(params.Uuid, params.Tts, "break", eventPoint.EpOs_Device)
				} else {
					eventPoint.AudioEnd(params.Uuid, params.Tts, "break", eventPoint.EpOs_Server)
				}
			}
		}

		led_mcu.DirectLedControl(params.LedOff.EffectId, params.LedOff.Action, params.LedOff.StartLampNum,
			params.LedOff.Data)
		params.IconOut.Show()
		if nil != params.SpecialFunc {
			params.SpecialFunc(schedule.TtsPlayerArg{StopReason: reason})
		}

		if params == curNoticePlayerParams {
			setNoticePlayerActive(false)
		}

		//if params.Keep {
		//	log.Println(log.DebugLog, "id:", id, " and session continues!")
		//	schedule.SendSCmd(schedule.SCMD_SuperTask_Voice_ContinuSession)
		//	return
		//}

		if BIRTHDAY_BEFORE_TASK == BirthdayAction {
			BirthdayPlayProcess()
		}

		if ttsList.GetLen() > 0 {
			// 被打断时,队列中等待的任务也清除掉
			ttsList.Init()
		}
	case schedule.NoticeStopReason_Complete:
		if params.Uuid != CONST_AUDIO_ID {
			//log.Println(log.WarningLog, "end srcUuid:", params.Uuid)
			if IsLocalAudioUuid(params.Uuid) {
				eventPoint.AudioEnd(params.Uuid, params.Tts, "end", eventPoint.EpOs_Device)
			} else {
				eventPoint.AudioEnd(params.Uuid, params.Tts, "end", eventPoint.EpOs_Server)
			}
		}

		led_mcu.DirectLedControl(params.LedOff.EffectId, params.LedOff.Action, params.LedOff.StartLampNum,
			params.LedOff.Data)
		params.IconOut.Show()
		if nil != params.SpecialFunc {
			params.SpecialFunc(schedule.TtsPlayerArg{StopReason: reason})
		}

		innerTask.ICmdExecString(params.SpecialCmd)

		if params.Keep && params == curNoticePlayerParams {
			if ttsList.GetLen() <= 0 {
				setNoticePlayerActive(false)
			}

			log.Println(log.DebugLog, "id:", id, " and session continues!")
			schedule.SendSCmd(schedule.SCMD_SuperTask_Voice_ContinuSession)
			return
		} else {
			// 如果有keep参数,则下一个节点忽略不执行
			if execNextNode(params.NextNode) {
				return
			}
		}

		if ttsList.GetLen() <= 0 && params == curNoticePlayerParams {
			setNoticePlayerActive(false)
		}

		if BIRTHDAY_BEFORE_TASK == BirthdayAction {
			BirthdayPlayProcess()
		}

		if ttsList.GetLen() > 0 {
			execPlayLeftTts()
			return
		}
	default:

	}
	NoticeTaskHander.FinishExec()
}

/********************
 * notice exec api
 */
func SoundEffectPlayCmd(cmd uint32, url string) {
	sendCurPlayerMusicCmd(cmd, &url, &url)
}

func noticePlayCmdSync(canExcel bool, cmd uint32, id uint32, tts string, uuid string, srcType AdSrcType, level TtsLevel,
	needRepeat bool, keep bool) {
	//log.Println(log.DebugLog, "lock")
	//ttsPlayLock.Lock()
	//log.Println(log.DebugLog, "locked")
	defer func() {
		//ttsPlayLock.Unlock()
		//log.Println(log.DebugLog, "unlock")
	}()

	var tosend PlayerCmdStruct
	tosend.PlayerId = PlayerType(id)

	if isNetConnect() {
		if srcType >= AdSrcType_TTS && srcType <= AdSrcType_TTS_MIX {
			tosend.AdSrcType = text2UrlSrc(srcType)

			if len(tts) > 0 {
				if XIAOYA_TTS == TtsSel {
					tosend.Src = XIAOYA_URL + tts + XIAOYA_CFG
				} else if ROKID_TTS_URL == TtsSel {
					tosend.Src = getRokidUrlSrc(tts, uuid)
				}
			}
		} else {
			tosend.AdSrcType = srcType

			if len(tts) > 0 {
				tosend.Src = tts
			}
		}
	} else {
		log.Println(log.WarningLog, "isNetConnect:", isNetConnect())
		if (srcType >= AdSrcType_TTS && srcType <= AdSrcType_TTS_MIX) ||
			(srcType >= AdSrcType_FILE && srcType <= AdSrcType_FILE_NOT_REPLAY_MUSIC && schedule.UrlSrc(tosend.Src)) {
			tosend.AdSrcType = text2UrlSrc(srcType)

			tosend.Src = vui.SoundNotice_Error_Network.GetPath()
		} else {
			tosend.AdSrcType = srcType

			if len(tts) > 0 {
				tosend.Src = tts
			}
		}
	}

	if len(uuid) > 0 {
		tosend.Uuid = uuid
	}

	log.Printf(log.DebugLog, "ttsInfo:%+v\n", tosend)
	tmp, err := json.Marshal(&tosend)
	if err != nil {
		log.Println(log.ErrorLog, err.Error())
		return
	}
	log.Printf(log.DebugLog, "canExcel:%+v, cmd:0x%x, id:%d, tts:%s, srcId:%s srcType:0x%x, level:%d, needRepeat:%+v, keep:%+v\n",
		canExcel, cmd, id, tts, uuid, srcType, level, needRepeat, keep)
	switch cmd {
	case CMD_PLAYER_AUDIO_PLAY_NOTICE:
		sendCmd2Dbus(CMD_PLAYER_AUDIO_PLAY_NOTICE, &tmp)
	case CMD_PLAYER_AUDIO_STOP:
		sendCmd2Dbus(CMD_PLAYER_AUDIO_STOP, &tmp)
	default:
		log.Println(log.WarningLog, "noticePlayCmdSync unknow notice cmd:", cmd)
	}
}

/********************
 * notice exec api
 */
/**
 * 播放队列中的tts
 */
func execPlayLeftTts() {
	ttsInfo, err := ttsList.GetRemoveBack()
	if nil == err {
		ttsmsg := ttsInfo.Value.(*TtsInfo)
		if nil == ttsmsg.Callback {
			params := schedule.NoticePlayerParams{
				SrcType:    ttsmsg.SrcType,
				Tts:        *ttsmsg.Tts,
				Uuid:       *ttsmsg.Uuid,
				NeedRepeat: ttsmsg.NeedRepeat,
				Keep:       ttsmsg.Keep,
			}
			playNoticeAsyncExec(nil, &params)
		} else {
			// not come here now
			ttsmsg.Callback(ttsmsg.CbInterface)
		}
	}
}

func playNoticeAsyncExec(ctx context.Context, params *schedule.NoticePlayerParams) {
	if curNoticePlayerParams != params {
		curNoticePlayerParams = params
	}

	if params.Uuid != CONST_AUDIO_ID {
		//log.Println(log.WarningLog, "start srcUuid:", params.Uuid)
		if IsLocalAudioUuid(params.Uuid) {
			eventPoint.SendPlayCmd2IhwPlayer(params.Uuid, params.Tts, eventPoint.EpOs_Device, params.Uuid)
		} else {
			eventPoint.SendPlayCmd2IhwPlayer(params.Uuid, params.Tts, eventPoint.EpOs_Server, params.Uuid)
		}
	}

	schedule.PlayNoticeAsync(params.SrcType, params.Tts, params.Uuid, false, ctx,
		func(id uint32, reason schedule.NoticeStopReason) {
			noticePlayerCallback(id, reason, params)
		})
}

/**
 * 参数列表: tts string, srcType AdSrcType, level TtsLevel, needRepeat bool
 */
func getNoticePlayerDoParams(arg ...interface{}) (valied bool,
	tts string, uuid string, srcType AdSrcType, level TtsLevel, needRepeat bool, keep bool) {
	if NOTICE_PLAYER_PARAMS_LEN != len(arg) {
		return false, "", "", 0, 0, false, false
	}

	switch eVal0 := arg[0].(type) {
	case string:
		tts = eVal0
	default:
		return false, "", "", 0, 0, false, false
	}
	switch eVal1 := arg[1].(type) {
	case string:
		uuid = eVal1
	default:
		return false, "", "", 0, 0, false, false
	}
	switch eVal2 := arg[2].(type) {
	case AdSrcType:
		srcType = eVal2
	default:
		return false, "", "", 0, 0, false, false
	}

	switch eVal3 := arg[3].(type) {
	case TtsLevel:
		level = eVal3
	default:
		return false, "", "", 0, 0, false, false
	}

	switch eVal4 := arg[4].(type) {
	case bool:
		needRepeat = eVal4
	default:
		return false, "", "", 0, 0, false, false
	}
	switch eVal5 := arg[5].(type) {
	case bool:
		keep = eVal5
	default:
		return false, "", "", 0, 0, false, false
	}
	return true, tts, uuid, srcType, level, needRepeat, keep
}

func noticePlayerDebug() string {
	var tmp string

	tmp += fmt.Sprintln(" ttsLen:", ttsList.GetLen())

	for e := ttsList.List.Front(); e != nil; e = e.Next() {
		if ttsmsg, ok := e.Value.(*TtsInfo); ok {
			tmp += fmt.Sprintf(" tts:%+v, ttscontent:%s\n", *ttsmsg, *(ttsmsg.Tts))
		}
	}

	return tmp
}

func setNoticePlayerActive(active bool) {
	noticePlayerIsActive = active
	if active {
		log.Println(log.DebugLog, "Notice player is active")
	} else {
		log.Println(log.DebugLog, "Notice is not active")
	}
}

func noticeCanBeInterruptByKey() bool {
	return true
}

func execNextNode(e interface{}) bool {
	switch eType := e.(type) {
	case *schedule.NoticePlayerParams:
		if nil == eType {
			return false
		} else {
			log.Printf(log.WarningLog, "nextMode:%+v\n", *eType)
			NoticePlayerSendSCmd2(schedule.SCMD_SuperTask_Tts_Ctrl_Play, *eType)
			return true
		}
	default:
		return false
	}
}
