package player

import (
	"container/list"
	"context"
	"encoding/json"
	"fmt"
	. "netease_control_center/globals"
	"netease_control_center/interfaces/eventPoint"
	"netease_control_center/interfaces/http"
	"netease_control_center/interfaces/log"
	. "netease_control_center/interfaces/schedule"
	"netease_control_center/interfaces/vui"
	_ "netease_control_center/interfaces/vui"
	"netease_control_center/nembd/controller/adau1761"
	"sync"
	"time"
)

type SceneMusicLists struct {
	Lists                   []MusicListInfo `json:"playList"`
	PlayMode                int8            `json:"playMode"`
	IsVoiceGraduateDisapper bool            `json:"isVoiceGraduateDisappear"`
	IsOpenBreatheGuide      bool            `json:"isOpenBreatheGuide"`
	BGId                    string          `json:"backgroundSoundId"` // 背景音id
}

type sceneCtxValType uint8

type sceneListInvalid struct {
	SceneId    string `json:"sceneID"`
	TaskId     int    `json:"taskId"`
	PlayListId string `json:"playListId"`
}

const (
	RESTORE_VOL_KEY = "restoreKey"
)
const (
	RESTORE_VOL sceneCtxValType = iota + 0
	NOT_RESTORE_VOL
)

var (
	scenesNeedSeekToPos bool = false // 在重新拉取url播放的情况下，需要在播放回调的时候，seek到制定位置
	scenesSeekPos       uint32

	needForcePauseScenesPlayer bool = false
	scenePlayMode              int8 = SCENE_PLAY_CYCLE

	sceneCurPosNew *list.Element = nil

	hdCur     SSceneHd
	taskNoCur int

	sceneOrder MusicPlayMode = LIST_ORDER_CYCLE

	timeVoiceLock     sync.Mutex
	timeVoiceGraduate *time.Timer = nil

	ctxLock               sync.Mutex
	sceneCtx, sceneCtxVal context.Context    = nil, nil
	sceneCancle           context.CancelFunc = nil

	recBGId *string = nil // 背景音id

	// 音乐播放状态变化时，需要更新
	scenesPlayerSt = PlayerStatus{PlayerSrc: OTHER_SRC, MusicId: nil, MusicListId: nil, ContinuousTaskSt: MUSIC_ST_IDLE, CurPlayingPos: 0,
		TaskField:    FIELD_NULL,
		SingleTaskSt: AUDIO_ST_IDLE}
)

const (
	SCENE_PLAY_CYCLE int8 = iota + 0
	SCENE_PLAY_ONCE
)

const (
	VOICE_GRADUATE_DISAPPEAR_TIME time.Duration = 5 * time.Minute
)

func sendScenesMusicCmd(hd SSceneHd, cmd uint32, params ...interface{}) error {
	playerID := (PlayerType)(hd.GetPlayerHd().GetPlayerId())
	return sendMusicCmd(playerID ,cmd, params ...)
}

func ScenesPause(hd SSceneHd) {
	ignoreRestoreVol := hd.GetIgnoreVolRestore()
	log.Printf(log.ErrorLog, "ignoreRestoreVol:%+v\n", ignoreRestoreVol)
	if !ignoreRestoreVol {
		EndVoiceGrandateDisappear(RESTORE_VOL)
	}

	sendScenesMusicCmd(hd ,CMD_PLAYER_PAUSE)
}

func ScenesPlay(startType PlayerStartType, position int, hd SSceneHd, taskno int) {
	defer func() {
		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()
	hdCur = hd
	taskNoCur = taskno
	log.Println(log.DebugLog, "startType:", startType, " position:", position, " taskno:", taskno)
	switch startType {
	case PlayerStartType_New:
		ttsNoMusic = false
		recBGId = nil

		if arg, err := hd.GetCurServerScenetask(); nil == err {
			log.Printf(log.DebugLog, "arg:%+v\n", arg)
			sceneOrder = arg.PlayMode
			switch SSTaskId(arg.LocalId) {
			case SSTaskId_PlayPlayList:
				if 0 == len(arg.Args) {
					hd.Finish(taskno, true)
					return
				}
				var sceneTmp SceneMusicLists
				if err := json.Unmarshal([]byte(arg.Args), &sceneTmp); nil != err {
					hd.Finish(taskno, true)
					return
				}
				if "" != sceneTmp.BGId && len(sceneTmp.BGId) > 0 {
					recBGId = &sceneTmp.BGId
				}

				log.Printf(log.DebugLog, "scene playListInfo:%+v\n", sceneTmp)
				list, err := getMusicListOfSceneNew(PlayerType(hdCur.GetPlayerHd().GetPlayerId()), true, http.MusicListServerType{
					Src:     sceneTmp.Lists[0].Type,
					Id:      &sceneTmp.Lists[0].Id,
					Params:  getServiceParams(sceneTmp.Lists[0].Type),
					Start:   0,
					Size:    PAGE_SIZE,
					WantTts: true,

					Mode: sceneOrder,
					// todo: 后续这些字段，服务器下发
					Oftag:        ScenesPlayerListSrc.TagName,
					OftagForShow: ScenesPlayerListSrc.TagNameShow,
					CloudId:      ScenesPlayerListSrc.CloudId,
					ModeId:       ScenesPlayerListSrc.ModeId,
				})
				//list, err := getMusicListOfScene(PlayerType(hdCur.GetPlayerHd().GetPlayerId()), sceneTmp.Lists[0].Type,
				//	&sceneTmp.Lists[0].Id, getServiceParams(sceneTmp.Lists[0].Type),
				//	0, PAGE_SIZE, true, sceneOrder)

				if nil == err {
					scenePlayMode = sceneTmp.PlayMode
					scenesPlayApi(list)

					// 测试
					//sceneTmp.IsVoiceGraduateDisapper = true
					if sceneTmp.IsVoiceGraduateDisapper {
						processVoicdGraduateDisappear(hd.GetRemainTime())
					}
				} else {
					log.Println(log.ErrorLog, err.Error())

					sceneList := sceneListInvalid{
						SceneId:    hdCur.GetScene().Id,
						TaskId:     taskNoCur,
						PlayListId: sceneTmp.Lists[0].Id,
					}
					updateListInvalid(sceneList)

					hd.Finish(taskno, true)
					return
				}
			case SSTaskId_PlaySongList:
				if 0 == len(arg.Args) {
					hd.Finish(taskno, true)
					return
				}
				var sceneTmp SceneMusicLists
				if err := json.Unmarshal([]byte(arg.Args), &sceneTmp); nil != err {
					hd.Finish(taskno, true)
					return
				}
				log.Printf(log.DebugLog, "scene playSongInfo:%+v\n", sceneTmp)

				var tmp ServerMusicList
				tmp.HasMore = false
				tmp.PlayMode = LIST_ORDER_CYCLE
				tmp.MusicList = sceneTmp.Lists[0]

				scenePlayMode = sceneTmp.PlayMode
				scenesPlayApi(&tmp)

				// 测试
				//sceneTmp.IsVoiceGraduateDisapper = true
				if sceneTmp.IsVoiceGraduateDisapper {
					processVoicdGraduateDisappear(hd.GetRemainTime())
				}
			case SSTaskId_QueryBirthDay:
				if res, _, _, err := http.GetBirthdayInfo(); nil == err {

					log.Printf(log.DebugLog, "UserBirthdayInfo:%+v\n", *res)
					if res.HasBirthDay {
						var tmp ServerMusicList
						tmp.HasMore = false
						tmp.PlayMode = MODE_NULL
						tmp.MusicList.SongList = res.SongList

						scenePlayMode = SCENE_PLAY_ONCE
						scenesPlayApi(&tmp)
						return
					}
				}
				// 没有生日，触发下一个任务
				hd.Finish(taskno, true)
			default:
				log.Println(log.ErrorLog, "err type of localId:", arg.LocalId)
				return
			}
		} else {
			// 错误处理
			hd.Finish(taskno, true)
			return
		}
	case PlayerStartType_PlayerChange:
		// 拉取url重新播放
		songId := hd.GetPlayerHd().GetPlayerStatus().Uuid
		songInfo := SongInfo{Id: songId, Duration: hd.GetPlayerHd().GetPlayerStatus().Duration}
		scenesPlayApi(&songInfo)
		if PLAY_FROM_START_POS != position {
			// 收到play的消息后， 需要seek到指定位置
			scenesNeedSeekToPos = true
			scenesSeekPos = uint32(position)
		}
	case PlayerStartType_Continue:
		if PLAY_FROM_START_POS == position {
			// 拉去url重新播放
			songId := hd.GetPlayerHd().GetPlayerStatus().Uuid
			songInfo := SongInfo{Id: songId, Duration: hd.GetPlayerHd().GetPlayerStatus().Duration}
			scenesPlayApi(&songInfo)
		} else {
			// 直接发送播放命令
			scenesPlayApi(nil)
		}
	}
}

func ScenesStop(hd SSceneHd) {
	EndVoiceGrandateDisappear(RESTORE_VOL)

	sendScenesMusicCmd(hd ,CMD_PLAYER_STOP)
}

func ScenesPlayNext(hd SSceneHd) {
	scenesNextApi(false)
}

func ScenesPlayPre(hd SSceneHd) {
	scenesPreApi()
}

func ScenesPlaySeek(hd SSceneHd, pos uint32) {
	sendScenesMusicCmd(hd ,CMD_PLAYER_SEEKTO, pos)
}

func ScenesPlayerStatusChange(status PlayerDetail, hd SSceneHd) {
	scenesPlayerStHd(status)
}

func ScenesAppCtrlPlay(e interface{}) {
	scenesPlayApi(e)
}

func scenesPlayApi(e interface{}) {
	log.Printf(log.DebugLog, "scene params:%+v\n", e)
	if nil == e {
		sendScenesMusicCmd(hdCur ,CMD_PLAYER_PLAY)
		return
	}

	switch param := e.(type) {
	case *ServerMusicList:
		scenesListPlay(e)
	case *SongInfo:
		lowLevelScenesPlayApi(commonPlayApiArg{
			prepare:            false,
			songId:             param,
			songUrl:            nil,
			tts:                "",
			ttsUrl:             "",
			ttsNow:             true,
			needChangeNHDIndex: false,
			srcType:            SINGLE_SONG,
			rspId:              "",
		})
	case *AppPlayReq:
		scenesPlayAppMusic(param)
	default:
		log.Println(log.WarningLog, "err type of play")
	}
}

/**
播放 app 下发的play命令
*/
func scenesPlayAppMusic(appReq *AppPlayReq) (MusicAction, error) {
	defer func() {
		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()
	if nil == appReq {
		log.Println(log.WarningLog, "param nil when play from app")
		return APP_NIL_ID_ACTION, MusicList_ERR_MUSICINFO_NULL
	}

	//　当前播放列表中歌曲
	songinfo := SongInfo{Id: (*appReq).Id}
	var songId *SongInfo = &songinfo
	if nil == songId || len(songId.Id) < 1 {
		log.Println(log.ErrorLog, "songId err:", (*appReq).Id)
		return APP_NIL_ID_ACTION, MusicList_ERR_MUSICINFO_NULL
	}

	if 0 >= LocalMusicList[SCENES_MUSIC_LIST].List.Len() {
		log.Println(log.ErrorLog, "cur list is nil")
		return APP_NIL_ID_ACTION, List_ERR_NULL
	}

	songInfoTmp, _, hasSongId := LocalMusicList[SCENES_MUSIC_LIST].Search(songId, true)
	var action MusicAction
	if hasSongId {
		songId = songInfoTmp
		// 列表中有该歌曲，直接播放该歌曲，且将curpos定位到该歌曲
		log.Println(log.DebugLog, "play list has this songId:", songId.Id)
		err = lowLevelScenesPlayApi(commonPlayApiArg{
			prepare:            false,
			songId:             songId,
			songUrl:            nil,
			tts:                "",
			ttsUrl:             "",
			ttsNow:             true,
			needChangeNHDIndex: false,
			srcType:            appReq.Type,
			rspId:              "",
		})

		if err != nil {
			log.Println(log.ErrorLog, err.Error())
			return APP_ERR_ACTION, err
		}
		action = APP_CUR_PAGE_ACTION
	} else {
		// 列表中没有该歌曲，拉取该歌曲对应的页
		index := GetPageIndexFromIndex((*appReq).Index, PAGE_SIZE)
		srcType := getMusicSrcType(PlayerType(hdCur.GetPlayerHd().GetPlayerId()))
		id := getSenceServiceId(srcType)
		serverList, err := getMusicListOfSceneNew(PlayerType(hdCur.GetPlayerHd().GetPlayerId()), true, http.MusicListServerType{
			Src:     srcType,
			Id:      id,
			Params:  nil,
			Start:   index,
			Size:    PAGE_SIZE,
			WantTts: true,

			Mode: CurPlayingMode,
			// todo: 后续这些字段，服务器下发
			Oftag:        ScenesPlayerListSrc.TagName,
			OftagForShow: ScenesPlayerListSrc.TagNameShow,
			CloudId:      ScenesPlayerListSrc.CloudId,
			ModeId:       ScenesPlayerListSrc.ModeId,
		})
		//serverList, err := getMusicListOfScene(PlayerType(hdCur.GetPlayerHd().GetPlayerId()), srcType, id, nil, index,
		//	PAGE_SIZE, true, CurPlayingMode)
		if nil != err {
			log.Println(log.ErrorLog, err.Error())
			return APP_ERR_ACTION, err
		}

		if songId.Id == serverList.MusicList.SongList[serverList.MusicList.Index].Id {
			// 推送的url和当前需要播放歌曲的url同，直接播放
			songId.Gain = serverList.MusicList.Gain
		} else {
			// 需要在此跟服务器拉取播放信息
			song, err := getSongDetailInfo(PlayerType(PlayerType(hdCur.GetPlayerHd().GetPlayerId())), &(songId.Id), true)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				return APP_ERR_ACTION, err
			} else if "" == song.Song.Url {
				log.Println(log.ErrorLog, "song url is nil")
				return APP_ERR_ACTION, err
			}
			serverList.MusicList.Url = song.Song.Url
			serverList.MusicList.Gain = song.Song.Gain

			songId = &song.Song.SongInfo
		}
		// 更新本地播放列表
		updateScenesPlayListAndPage(serverList, appReq.Index, true)

		// 更新当前播放列表来源信息
		updateScenesPlayListSrcRec(serverList)

		// 定位当前歌曲记录节点到播放歌曲位置
		_, _, found := LocalMusicList[SCENES_MUSIC_LIST].Search(songId, true)
		if !found {
			PlayNoticeAsync(AdSrcType_TTS, TTS_APP_NOT_SEARCH_SONG, CONST_AUDIO_ID, true, nil, nil)
			return APP_ERR_ACTION, err
		}

		// 播放歌曲
		err = lowLevelScenesPlayApi(commonPlayApiArg{
			prepare:            false,
			songId:             songId,
			songUrl:            &(serverList.MusicList.Url),
			tts:                "",
			ttsUrl:             "",
			ttsNow:             true,
			needChangeNHDIndex: false,
			srcType:            appReq.Type,
			rspId:              "",
		})

		if nil != err {
			log.Println(log.WarningLog, err.Error())
			return APP_ERR_ACTION, err
		}
		action = APP_NEXT_PAGE_ACTION
	}
	// 定位当前歌曲记录节点到播放歌曲位置
	LocalMusicList[SCENES_MUSIC_LIST].Search(songId, true)
	return action, nil
}

func scenesPreApi() (MusicChangeAction, error) {
	defer func() {
		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	if nil == LocalMusicList[SCENES_MUSIC_LIST].GetCurPos() {
		PlayNoticeAsync(AdSrcType_TTS, List_ERR_NULL.Error(), CONST_AUDIO_ID, true, nil, nil)

		return ACTION_NULL, List_ERR_NULL
	}
	action := getNextPrevActionByScenesPlayer(false)
	if UintTest {
		return action, nil
	}

	var songId *SongInfo = nil
	var songUrl *string = nil

	var index MusicLenType = 0
	switch action {
	case ACTION_NULL:
		// 当前列表空
	case ACTION_PLAY_DIRECT:
		LocalMusicList[SCENES_MUSIC_LIST].GetValidPre()
	case ACTION_PLAY_BACK:
		tmpCurPos, err := LocalMusicList[SCENES_MUSIC_LIST].GetLast()
		if nil != err {
			log.Println(log.ErrorLog, err.Error())
			return ACTION_NULL, err
		}
		LocalMusicList[SCENES_MUSIC_LIST].SetCurPos(tmpCurPos)

		if LocalMusicList[SCENES_MUSIC_LIST].GetCurPos().Value.(*SongInfo).CanPlay {

		} else {
			LocalMusicList[SCENES_MUSIC_LIST].GetValidPre()
		}
	case ACTION_NEXT_PAGE_REPLACE_LIST, ACTION_MISS_PAGE_REPLACE_LIST, ACTION_LAST_PAGE:
		if ACTION_NEXT_PAGE_REPLACE_LIST == action {
			// 还有剩余分页，拉取分页
			if val, err := NormalListMap[SCENES_LIST_PAGE].GetLast(); nil == err {
				index = val.Value.(*PageNode).PageIndex * PAGE_SIZE
			}
		} else if ACTION_MISS_PAGE_REPLACE_LIST == action {
			// 有缺失的分页，拉取分页
			index = (NormalListMap[SCENES_LIST_PAGE].GetMissPage() - 1) * PAGE_SIZE
		} else {
			// 分页全部拉取完毕，重新拉取第一页
			NormalListMap[SCENES_LIST_PAGE].Init()
		}

		srcType := getMusicSrcType(PlayerType(hdCur.GetPlayerHd().GetPlayerId()))
		list, err := getMusicListOfSceneNew(PlayerType(hdCur.GetPlayerHd().GetPlayerId()), true, http.MusicListServerType{
			Src:     srcType,
			Id:      getSenceServiceId(srcType),
			Params:  getServiceParams(srcType),
			Start:   index,
			Size:    PAGE_SIZE,
			WantTts: true,

			Mode: sceneOrder,
			// todo: 后续这些字段，服务器下发
			Oftag:        ScenesPlayerListSrc.TagName,
			OftagForShow: ScenesPlayerListSrc.TagNameShow,
			CloudId:      ScenesPlayerListSrc.CloudId,
			ModeId:       ScenesPlayerListSrc.ModeId,
		})
		//list, err := getMusicListOfScene(PlayerType(hdCur.GetPlayerHd().GetPlayerId()), srcType, getSenceServiceId(srcType),
		//	getServiceParams(srcType), index, PAGE_SIZE, true, sceneOrder)
		if nil != err {
			log.Println(log.ErrorLog, err.Error())
			return ACTION_NULL, err
		}
		updateScenesPlayListAndPage(list, index, true)

		tmpCurPos, err := LocalMusicList[SCENES_MUSIC_LIST].GetLast()
		if nil != err {
			log.Println(log.ErrorLog, err.Error())
			return ACTION_NULL, err
		}
		LocalMusicList[SCENES_MUSIC_LIST].SetCurPos(tmpCurPos)

		if LocalMusicList[SCENES_MUSIC_LIST].GetCurPos().Value.(*SongInfo).CanPlay {

		} else {
			LocalMusicList[SCENES_MUSIC_LIST].GetValidPre()
		}
	default:
		log.Println(log.WarningLog, "err action")
		return ACTION_NULL, Normal_ERR_ACTION
	}
	if ACTION_NULL != action {
		songId = LocalMusicList[SCENES_MUSIC_LIST].GetCurPos().Value.(*SongInfo)
	}

	return action, lowLevelScenesPlayApi(commonPlayApiArg{
		prepare:            false,
		songId:             songId,
		songUrl:            songUrl,
		tts:                "",
		ttsUrl:             "",
		ttsNow:             true,
		needChangeNHDIndex: false,
		srcType:            ScenesPlayerListSrc.MusicSrc,
		rspId:              "",
	})
}

func scenesNextApi(prepare bool) (MusicChangeAction, error) {
	defer func() {
		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()
	if prepare {
		SceneCurPosRecTmp = LocalMusicList[SCENES_MUSIC_LIST].GetCurPos()
	}

	defer func() {
		if prepare {
			if nil != SceneCurPosRecTmp {
				log.Println(log.DebugLog, "before restore:", LocalMusicList[SCENES_MUSIC_LIST].GetCurPos().Value.(*SongInfo).Id)
				sceneCurPosNew = LocalMusicList[SCENES_MUSIC_LIST].GetCurPos()
				LocalMusicList[SCENES_MUSIC_LIST].SetCurPos(SceneCurPosRecTmp)

				log.Println(log.DebugLog, "after restore:", LocalMusicList[SCENES_MUSIC_LIST].GetCurPos().Value.(*SongInfo).Id)
				SceneCurPosRecTmp = nil
			}
		}

		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	if nil == LocalMusicList[SCENES_MUSIC_LIST].GetCurPos() {
		PlayNoticeAsync(AdSrcType_TTS, List_ERR_NULL.Error(), CONST_AUDIO_ID, true, nil, nil)
		return ACTION_NULL, List_ERR_NULL
	}
	action := getNextPrevActionByScenesPlayer(true)
	if UintTest {
		return action, nil
	}

	var index MusicLenType
	var songId *SongInfo = nil
	var songUrl *string = nil

	switch action {
	case ACTION_NULL:
		// 当前列表空
	case ACTION_STOP_PLAYER:
		log.Println(log.DebugLog, "scenes player auto stop, taskNoCur:", taskNoCur)
		sendScenesMusicCmd(hdCur ,CMD_PLAYER_PAUSE)

		hdCur.GetPlayerHd().ReportOffline()
		// todo scenes play auto stop, process your things
		hdCur.Finish(taskNoCur, true)

		return ACTION_STOP_PLAYER, nil
	case ACTION_PLAY_DIRECT:
		LocalMusicList[SCENES_MUSIC_LIST].GetValidNext()
		songId = LocalMusicList[SCENES_MUSIC_LIST].GetCurPos().Value.(*SongInfo)
	case ACTION_PLAY_FRONT:
		tmpCurPos, err := LocalMusicList[SCENES_MUSIC_LIST].GetFirst()
		if nil != err {
			log.Println(log.ErrorLog, err.Error())
			return ACTION_NULL, err
		}
		LocalMusicList[SCENES_MUSIC_LIST].SetCurPos(tmpCurPos)

		if LocalMusicList[SCENES_MUSIC_LIST].GetCurPos().Value.(*SongInfo).CanPlay {

		} else {
			LocalMusicList[SCENES_MUSIC_LIST].GetValidNext()
		}

		songId = LocalMusicList[SCENES_MUSIC_LIST].GetCurPos().Value.(*SongInfo)
	case ACTION_NEXT_PAGE_REPLACE_LIST, ACTION_MISS_PAGE_REPLACE_LIST, ACTION_LAST_PAGE:
		var replace bool = true
		var pageSize MusicLenType = PAGE_SIZE
		if ACTION_NEXT_PAGE_REPLACE_LIST == action {
			// 还有剩余分页，拉取分页
			if val, err := NormalListMap[SCENES_LIST_PAGE].GetLast(); nil == err {
				index = val.Value.(*PageNode).PageIndex * PAGE_SIZE
			}
		} else if ACTION_MISS_PAGE_REPLACE_LIST == action {
			// 有缺失的分页，拉取分页
			index = (NormalListMap[SCENES_LIST_PAGE].GetMissPage() - 1) * PAGE_SIZE
		} else {
			NormalListMap[SCENES_LIST_PAGE].Init()
			// 分页全部拉取完毕，重新拉取第一页
			index = 0
		}

		srcType := getMusicSrcType(PlayerType(hdCur.GetPlayerHd().GetPlayerId()))
		list, err := getMusicListOfSceneNew(PlayerType(hdCur.GetPlayerHd().GetPlayerId()), true, http.MusicListServerType{
			Src:     srcType,
			Id:      getSenceServiceId(srcType),
			Params:  getServiceParams(srcType),
			Start:   index,
			Size:    pageSize,
			WantTts: true,

			Mode: sceneOrder,
			// todo: 后续这些字段，服务器下发
			Oftag:        ScenesPlayerListSrc.TagName,
			OftagForShow: ScenesPlayerListSrc.TagNameShow,
			CloudId:      ScenesPlayerListSrc.CloudId,
			ModeId:       ScenesPlayerListSrc.ModeId,
		})
		//list, err := getMusicListOfScene(PlayerType(hdCur.GetPlayerHd().GetPlayerId()), srcType, getSenceServiceId(srcType),
		//	getServiceParams(srcType), index, pageSize, true, sceneOrder)
		if nil != err {
			log.Println(log.ErrorLog, err.Error())
			return ACTION_NULL, err
		}
		updateScenesPlayListAndPage(list, index, replace)

		songId, songUrl = getCanPlaySong(list)
	default:
		log.Println(log.DebugLog, "err action")
		return ACTION_NULL, Normal_ERR_ACTION
	}

	return action, lowLevelScenesPlayApi(commonPlayApiArg{
		prepare:            prepare,
		songId:             songId,
		songUrl:            songUrl,
		tts:                "",
		ttsUrl:             "",
		ttsNow:             true,
		needChangeNHDIndex: false,
		srcType:            ScenesPlayerListSrc.MusicSrc,
		rspId:              "",
	})
}

func getNextPrevActionByScenesPlayer(needNextAction bool) MusicChangeAction {
	defer func() {
		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	if nil == LocalMusicList[SCENES_MUSIC_LIST].GetCurPos() {
		return ACTION_NULL
	}

	if needNextAction {
		log.Println(log.DebugLog, "Next...")
	} else {
		log.Println(log.DebugLog, "Prev...")
	}
	var ret MusicChangeAction
	back, _ := NormalListMap[SCENES_LIST_PAGE].GetLast()
	if needNextAction {
		if nil != LocalMusicList[SCENES_MUSIC_LIST].GetValidFrontBackSong(false) &&
			LocalMusicList[SCENES_MUSIC_LIST].GetCurPos().Value.(*SongInfo).Id != LocalMusicList[SCENES_MUSIC_LIST].GetValidFrontBackSong(false).Id {
			ret = ACTION_PLAY_DIRECT
		} else if SCENE_PLAY_ONCE == scenePlayMode {
			ret = ACTION_STOP_PLAYER
		} else if NormalListMap[SCENES_LIST_PAGE].HasMore {
			// 还有分页
			ret = ACTION_NEXT_PAGE_REPLACE_LIST
		} else if nil != back &&
			back.Value.(*PageNode).PageIndex != MusicLenType(NormalListMap[SCENES_LIST_PAGE].List.Len()) {
			// 有缺失的分页，拉取分页
			ret = ACTION_MISS_PAGE_REPLACE_LIST
		} else {
			if 1 == MusicLenType(NormalListMap[SCENES_LIST_PAGE].List.Len()) ||
				MUSIC_LIST_NULL == ScenesPlayerListSrc.MusicSrc {
				//如果只有1页 / 如果都是单曲插入，直接播放第1首
				ret = ACTION_PLAY_FRONT
			} else {
				ret = ACTION_LAST_PAGE
			}
		}
	} else {
		if nil != LocalMusicList[SCENES_MUSIC_LIST].GetValidFrontBackSong(true) &&
			LocalMusicList[SCENES_MUSIC_LIST].GetCurPos().Value.(*SongInfo).Id != LocalMusicList[SCENES_MUSIC_LIST].GetValidFrontBackSong(true).Id {
			ret = ACTION_PLAY_DIRECT
		} else if PRIVATE_FM == ScenesPlayerListSrc.MusicSrc || MUSIC_SHARE_RCMD == ScenesPlayerListSrc.MusicSrc {
			ret = ACTION_PLAY_BACK
		} else if NormalListMap[SCENES_LIST_PAGE].HasMore {
			// 还有分页
			ret = ACTION_NEXT_PAGE_REPLACE_LIST
		} else if nil != back &&
			back.Value.(*PageNode).PageIndex != MusicLenType(NormalListMap[SCENES_LIST_PAGE].List.Len()) {
			// 有缺失的分页，拉取分页
			ret = ACTION_MISS_PAGE_REPLACE_LIST
		} else {
			if 1 == MusicLenType(NormalListMap[SCENES_LIST_PAGE].List.Len()) ||
				MUSIC_LIST_NULL == ScenesPlayerListSrc.MusicSrc {
				// 如果只有1页 / 如果都是单曲插入，直接播放最后1首
				ret = ACTION_PLAY_BACK
			} else {
				ret = ACTION_LAST_PAGE
			}
		}
	}
	log.Println(log.DebugLog, "action:", ret, "   MusicSrc:", ScenesPlayerListSrc.MusicSrc)
	return ret
}

func scenesPlayerStHd(status PlayerDetail) error {
	defer func() {
		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	switch status.Status {
	case MUSIC_ST_PLAYING:
		if "" != status.Uuid {
			if scenesNeedSeekToPos {
				scenesNeedSeekToPos = false
				sendScenesMusicCmd(hdCur ,CMD_PLAYER_SEEKTO, scenesSeekPos)
			}

			updateScenesPlayerStatus(status)

			if ttsMusicUuid != status.Uuid {
				ttsNoMusic = false
			}
		}
	case MUSIC_ST_FOR_NEXT:
		_, err = scenesNextApi(true)
		if nil != err {
			log.Println(log.ErrorLog, err.Error())
		} else {
			log.Println(log.DebugLog, "prepare for next...")
		}
		// for debug
		//log.Println(log.DebugLog, "trig to next task......")
		//hdCur.Finish(taskNoCur)
	case MUSIC_ST_COMPLETED:
		if nil != sceneCurPosNew {
			LocalMusicList[SCENES_MUSIC_LIST].SetCurPos(sceneCurPosNew)

			sceneCurPosNew = nil
			log.Println(log.DebugLog, "complete:", LocalMusicList[SCENES_MUSIC_LIST].GetCurPos().Value.(*SongInfo).Id)
		}

		if ttsNoMusic {
			ttsNoMusic = false
			forcePauseScenesPlayer()
			PlayNoticeAsync(AdSrcType_TTS, ttsInfo, CONST_AUDIO_ID, true, nil, nil)
			log.Println(log.DebugLog, "tts when music end:", ttsInfo)
		}
		if "" != status.Uuid {
			updateScenesPlayerStatus(status)
		}
	case MUSIC_ST_PAUSED, MUSIC_ST_IDLE:
		if "" != status.Uuid {
			updateScenesPlayerStatus(status)
		}
	case MUSIC_ST_PREPARING:
		log.Println(log.DebugLog, "preparing...")
	case MUSIC_ST_PREPARED:
		log.Println(log.DebugLog, "prepared")
	case MUSIC_ST_PREPARING_TIMEOUT:
		log.Println(log.ErrorLog, "scenes player perparing timeout")
		PlayNoticeAsync(AdSrcType_FILE, vui.SoundNotice_Song_Get_Timeout.GetPath(), CONST_AUDIO_ID, true, nil, nil)
		eventPoint.PrepareTimeout(GetScenePlayerId())
	case PLAYER_ERR_TINA_NOTIFY:
		log.Println(log.DebugLog, "...tinaplayer err...")

		select {
		case <-time.After(500 * time.Millisecond):
			if hdCur.GetPlayerHd().IsPlaying() {
				log.Println(log.DebugLog, "...tinaplayer err...but player is playing")
			} else {
				scenesNextApi(false)
			}
		}
	default:
		log.Printf(log.WarningLog, "invalid st:0x%x\n", status.Status)
	}

	return nil
}

func updateScenesPlayerStatus(status PlayerDetail) uint8 {
	defer func() {
		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	if status.Status < MUSIC_ST_MAX && status.Status >= MUSIC_ST_IDLE {
		scenesPlayerSt.PlayerSrc = CurPlayerSrc
		// 连续任务状态
		scenesPlayerSt.MusicId = &status.Uuid
		scenesPlayerSt.ContinuousTaskSt = status.Status
		scenesPlayerSt.CurPlayingPos = status.CurPos
		scenesPlayerSt.MusicListId = ScenesPlayerListSrc.MusicListId
		scenesPlayerSt.Duration = status.Duration
		scenesPlayerSt.ContinuousStChangeTime = time.Now()
		scenesPlayerSt.Type = ScenesPlayerListSrc.MusicSrc
		scenesPlayerSt.TaskField = FIELD_MUSIC
	}

	var precent uint8
	if 0 == scenesPlayerSt.Duration {
		precent = 0
	} else {
		precent = uint8(scenesPlayerSt.CurPlayingPos * 100 / scenesPlayerSt.Duration)
	}

	var printStr string = ""
	if nil != scenesPlayerSt.MusicId && len(*scenesPlayerSt.MusicId) > 0 {
		printStr += fmt.Sprintf("musicId:%s ", *scenesPlayerSt.MusicId)
	}
	if nil != scenesPlayerSt.MusicListId && len(*scenesPlayerSt.MusicListId) > 0 {
		printStr += fmt.Sprintf("listId:%s ", *scenesPlayerSt.MusicListId)
	}
	printStr += fmt.Sprintf("field:%d cSt:0x%x cur:%ds dur:%ds src:%d id:%d(%d%%)",
		scenesPlayerSt.TaskField,
		scenesPlayerSt.ContinuousTaskSt,
		status.CurPos/1000,
		status.Duration/1000,
		scenesPlayerSt.PlayerSrc,
		PlayerType(hdCur.GetPlayerHd().GetPlayerId()),
		precent)
	if MUSIC_ST_PLAYING == status.Status {
		log.Printf(log.DebugLog, "%s\r", printStr)
	} else {
		log.Printf(log.DebugLog, "%s\n", printStr)
	}
	return precent
}

func scenesListPlay(e interface{}) MusicAction {
	defer func() {
		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	var srcType MusicSrcType
	var songList []SongInfo
	switch eVal := e.(type) {
	case *ServerMusicList:
		// 歌单
		srcType = eVal.MusicList.Type
		songList = eVal.MusicList.SongList
	case *ServerMusicLists:
		listIndex = 0 // 歌单记录索引清零
		// 歌单列表
		srcType = eVal.MusicLists[0].Type
		songList = eVal.MusicLists[0].SongList
	default:
		log.Println(log.ErrorLog, "err params when play music")
		return MUSIC_ERR_ACTION
	}
	// 检查歌单是否为空
	if nil == songList || len(songList) == 0 {
		log.Println(log.ErrorLog, "nil songlist from server")
		return MUSIC_ERR_ACTION
	}

	log.Println(log.DebugLog, "type:", srcType)

	scenesListPlayExec(e)

	return MUSIC_LISTS_ACTION
}

/**
e 的格式为：*ServerMusicList 或者 *ServerMusicLists
*/
func scenesListPlayExec(e interface{}) (MusicAction, error) {
	defer func() {
		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	var songId *SongInfo = nil
	var songUrl *string = nil
	// 歌单id不同，或者更新时间变，或者当前播放列表空，需要替换歌单
	songId, songUrl = getCanPlaySong(e)

	// 更新页信息,且第一次推下来，肯定时第1页
	NormalListMap[SCENES_LIST_PAGE].Init()

	updateScenesPlayListAndPage(e, 0, true)
	// 定位当前歌曲记录节点到播放歌曲位置
	if nil != songId {
		LocalMusicList[SCENES_MUSIC_LIST].Search(songId, true)
	}
	// 更新当前播放列表来源信息
	updateScenesPlayListSrcRec(e)

	var tts, ttsUrl string = "", ""
	var rspId string = ""
	switch eVal := e.(type) {
	case *ServerMusicList:
		tts = eVal.InnerTts
		ttsUrl = eVal.InnerTtsUrl
		rspId = eVal.RspId
	case *ServerMusicLists:
		tts = eVal.InnerTts
		ttsUrl = eVal.InnerTtsUrl
		rspId = eVal.RspId
	default:

	}

	err = lowLevelScenesPlayApi(commonPlayApiArg{
		prepare:            false,
		songId:             songId,
		songUrl:            songUrl,
		tts:                tts,
		ttsUrl:             ttsUrl,
		ttsNow:             true,
		needChangeNHDIndex: false,
		srcType:            ScenesPlayerListSrc.MusicSrc,
		rspId:              rspId,
	})

	if err != nil {
		log.Println(log.ErrorLog, err.Error())
		return NORMAL_ERR_ACTION, err
	}

	return NORMAL_MORE_T1_UPDATE, nil
}

func updateScenesPlayListAndPage(e interface{}, index MusicLenType, replace bool) {
	defer func() {
		if playerRecoverLevel <= HIGH_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	var songs *[]SongInfo
	var lenType MusicLenType = 0
	var topIndex MusicLenType
	var hasMore bool = false // 查看歌曲是否还有更多

	switch eVal := e.(type) {
	case *ServerMusicList:
		songs = &(eVal.MusicList.SongList)
		lenType = MusicLenType(len(e.(*ServerMusicList).MusicList.SongList))
		topIndex = MusicLenType(eVal.MusicList.Index)
		hasMore = eVal.MusicList.HasMore

		log.Println(log.DebugLog, "Has more musics:", eVal.MusicList.HasMore)
	case *ServerMusicLists:
		songs = &(eVal.MusicLists[0].SongList)
		lenType = MusicLenType(len(e.(*ServerMusicLists).MusicLists[0].SongList))
		topIndex = MusicLenType(eVal.MusicLists[0].Index)
		hasMore = eVal.MusicLists[0].HasMore

		log.Println(log.DebugLog, "Has more musics:", eVal.MusicLists[0].HasMore)
		log.Println(log.DebugLog, "Has more Lists:", ScenesPlayerListSrc.HasMore)
	default:
		log.Println(log.WarningLog, "err params")
		return
	}

	pageNode := GetPageFromIndex(index, PAGE_SIZE)
	err := NormalListMap[SCENES_LIST_PAGE].Push(pageNode)
	if nil != err {
		log.Println(log.WarningLog, err.Error())
	}
	NormalListMap[SCENES_LIST_PAGE].HasMore = hasMore
	if !hasMore {
		// 获取最大的页面值
		NormalListMap[SCENES_LIST_PAGE].MaxPage = pageNode.PageIndex
		log.Println(log.DebugLog, "MaxPage:", pageNode.PageIndex)
	}

	if NormalListMap[SCENES_LIST_PAGE].MaxPage == MusicLenType(NormalListMap[SCENES_LIST_PAGE].List.Len()) {
		NormalListMap[SCENES_LIST_PAGE].HasMore = false
		NormalListMap[SCENES_LIST_PAGE].MaxPage = 0
		log.Println(log.DebugLog, "reset music hasMore")
	}

	if replace {
		log.Println(log.DebugLog, "replace list, lenType:", lenType, "  topIndex:", topIndex)
		// 更新播放列表信息
		LocalMusicList[SCENES_MUSIC_LIST].ReplaceBySlice(songs, lenType, 0, topIndex)
	} else {
		var offset MusicLenType = GetPageIndexFromIndex(index, PAGE_SIZE)
		log.Println(log.DebugLog, "add list, lenType:", lenType, "  offset:", offset,
			"  topIndex:", topIndex)
		// 更新播放列表信息
		LocalMusicList[SCENES_MUSIC_LIST].AddBySlice(songs, lenType, 0, topIndex)
	}
}

func updateScenesPlayListSrcRec(e interface{}) {
	defer func() {
		if playerRecoverLevel <= HIGH_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	ScenesPlayerListSrc.HasInsert = false
	switch eVal := e.(type) {
	case *ServerMusicList:
		ScenesPlayerListSrc.MusicListId = &(eVal.MusicList.Id)
		ScenesPlayerListSrc.TagName = &(eVal.Oftag)
		ScenesPlayerListSrc.TagNameShow = &eVal.OftagForShow
		ScenesPlayerListSrc.UpdateTime = eVal.MusicList.UpdateTime

		if (USER_LISTS == ScenesPlayerListSrc.MusicSrc || USER_LISTS_COLLECT == ScenesPlayerListSrc.MusicSrc ||
			ALBUM_ARTIST_ID == ScenesPlayerListSrc.MusicSrc || USER_LISTS_CREATE == ScenesPlayerListSrc.MusicSrc ||
			MUSIC_LIST_WORD == ScenesPlayerListSrc.MusicSrc || MUSIC_LIST_SERACH_WORD == ScenesPlayerListSrc.MusicSrc) &&
			(ScenesPlayerListSrc.HasMore || MUSIC_LIST_NULL == eVal.MusicList.Type) {
			//如果之前状态是歌单列表，且还有更多歌单，不要替换
		} else {
			ScenesPlayerListSrc.MusicSrc = eVal.MusicList.Type
			// 推送下来的时一个歌单，没有歌单列表，因此需要清除歌单列表hasMore
			ScenesPlayerListSrc.HasMore = false

			ScenesPlayerListSrc.MusicListIdIndex = INVALID_MUSIC_LEN
			ScenesPlayerListSrc.MusicListIds = nil
			ScenesPlayerListSrc.MusicListIdsLen = INVALID_MUSIC_LEN
			ScenesPlayerListSrc.MusicListMaxLen = INVALID_MUSIC_LEN
		}
	case *ServerMusicLists:
		ScenesPlayerListSrc.MusicListId = &(eVal.MusicLists[0].Id)
		ScenesPlayerListSrc.TagName = &(eVal.Oftag)
		ScenesPlayerListSrc.TagNameShow = &eVal.OftagForShow
		ScenesPlayerListSrc.MusicListIdIndex = 0
		if playUserLists {
			// 如果当前播放的是我的歌单
			ScenesPlayerListSrc.MusicSrc = USER_LISTS
		} else {
			ScenesPlayerListSrc.MusicSrc = eVal.MusicLists[0].Type
		}
		ScenesPlayerListSrc.UpdateTime = e.(*ServerMusicLists).MusicLists[0].UpdateTime
		ScenesPlayerListSrc.HasMore = e.(*ServerMusicLists).HasMore

		ScenesPlayerListSrc.MusicListIdsLen = MusicLenType(len(eVal.MusicLists))
		var listIds []ListIds = make([]ListIds, ScenesPlayerListSrc.MusicListIdsLen)
		for k, v := range eVal.MusicLists {
			listIds[k].Id = &(eVal.MusicLists[k].Id)
			listIds[k].UpdateTime = v.UpdateTime
		}
		ScenesPlayerListSrc.MusicListIds = &listIds

		log.Println(log.DebugLog, "MusicListIdIndex: ", ScenesPlayerListSrc.MusicListIdIndex,
			" ScenesPlayerListSrc.MusicListIdsLen:", ScenesPlayerListSrc.MusicListIdsLen)
		for k, v := range *ScenesPlayerListSrc.MusicListIds {
			log.Println(log.DebugLog, k, ": ", *v.Id, " updateTime:", v.UpdateTime)
		}

		listIndex += ScenesPlayerListSrc.MusicListIdsLen

		if ScenesPlayerListSrc.HasMore {
			ScenesPlayerListSrc.MusicListMaxLen = listIndex
		}
	default:
		log.Println(log.WarningLog, "err params")
	}
	log.Println(log.DebugLog, "MusicListId:", *ScenesPlayerListSrc.MusicListId, "  MusicListIdIndex:",
		ScenesPlayerListSrc.MusicListIdIndex,
		"  MusicSrc:", ScenesPlayerListSrc.MusicSrc, "  UpdateTime:", ScenesPlayerListSrc.UpdateTime)
}

/* 会对歌单进行鉴权保护　*/
func lowLevelScenesPlayApi(arg commonPlayApiArg) error {
	defer func() {
		if playerRecoverLevel <= HIGH_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}

		if needForcePauseScenesPlayer {
			forcePauseScenesPlayer()
		}
	}()
	needForcePauseScenesPlayer = false

	var tts *string = nil
	var ttsAdSrcType AdSrcType
	if len(arg.ttsUrl) > 0 {
		tts = &arg.ttsUrl
		ttsAdSrcType = AdSrcType_FILE_MIX
	} else if len(arg.tts) > 0 {
		tts = &arg.tts
		ttsAdSrcType = AdSrcType_TTS_MIX
	}

	var musicCnt MusicLenType = 0
	var pageCnt MusicLenType = 0
	var needComfirm bool = false
	var songDetail *ServerSongDetail = nil
	var err error
	if nil == arg.songUrl || len(*arg.songUrl) == 0 {
	searchMusic:
		if nil != arg.songId && len(arg.songId.Id) > 0 {
			songDetail, err = getSongDetailInfo(PlayerType(hdCur.GetPlayerHd().GetPlayerId()), &arg.songId.Id, arg.ttsNow)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				if Server_ERR_RET == err {
					needForcePauseScenesPlayer = true
					return err
				}
				if SINGLE_SONG == arg.srcType {
					// 如果是单曲，服务端需要保证该单曲是可以播放的，因此不会进入该分之，如果进入，拉取下一首播放
				}
				needComfirm = true
			} else if "" == songDetail.Song.Url {
				needComfirm = true
			} else {
				needComfirm = false
				arg.songUrl = &songDetail.Song.Url
			}
		} else {

			needComfirm = true
		}
		if needComfirm {
			// 播放失败，需要做拉取并鉴权处理
			found, step := LocalMusicList[SCENES_MUSIC_LIST].GetValidNext()
			log.Println(log.DebugLog, "found:", found, " step:", step)
			if !found || musicCnt >= MusicLenType(LocalMusicList[SCENES_MUSIC_LIST].List.Len()) {
			searchPage:
				// case 1: 歌单中还有分页需要处理
				if NormalListMap[SCENES_LIST_PAGE].HasMore ||
					pageCnt < NormalListMap[SCENES_LIST_PAGE].MaxPage {
					log.Printf(log.DebugLog, "has more page to pull.%d  %+v\n", arg.srcType, NormalListMap[SCENES_LIST_PAGE].HasMore)
					// 还有分页
					arg.songId, err = hasPageNeedComfirmByScenes()
					pageCnt++
					if nil != err {
						goto searchPage
					} else {
						musicCnt = 0
						goto searchMusic
					}
				}
				// case 2:　错误信息播报
				log.Printf(log.DebugLog, "\n can not play because of lists is invalid:%v srcType:%d\n",
					MusicErrTtsMap[string(arg.srcType)], arg.srcType)

				forcePauseScenesPlayer()

				PlayNoticeAsync(AdSrcType_TTS, MusicErrTtsMap[string(arg.srcType)], CONST_AUDIO_ID, true, nil, nil)

				sceneList := sceneListInvalid{
					SceneId:    hdCur.GetScene().Id,
					TaskId:     taskNoCur,
					PlayListId: *getSenceServiceId(arg.srcType),
				}
				updateListInvalid(sceneList)

				return MusicList_ERR_MUSIC_NULL

			} else {
				musicCnt += step
				arg.songId = LocalMusicList[SCENES_MUSIC_LIST].GetCurPos().Value.(*SongInfo)
				goto searchMusic
			}
		}
	}
	if "" != arg.songId.BGId {
		song, err := getSongPlayInfo(PlayerType(hdCur.GetPlayerHd().GetPlayerId()),
			&arg.songId.BGId,
			true)
		if nil != err {
			log.Println(log.ErrorLog, err.Error())
			return err
		}
		arg.songId.BGUrl = &song.SongPlay.Url
	}

	if arg.prepare {
		sendScenesMusicCmd(hdCur ,CMD_PLAYER_PERPARE_NEXT, &arg.songId.Id, arg.songUrl, tts, ttsAdSrcType,
			arg.songId.Duration, arg.songId.Gain, arg.songId.BGUrl)
	} else {
		sendScenesMusicCmd(hdCur ,CMD_PLAYER_PLAY, &arg.songId.Id, arg.songUrl, tts, ttsAdSrcType,
			arg.songId.Duration, arg.songId.Gain, arg.songId.BGUrl)
	}
	LocalMusicList[PLAYSONG_MUSIC_LIST].Push(arg.songId, true)

	if nil == songDetail {
		songDetail, err = getSongDetailInfo(PlayerType(hdCur.GetPlayerHd().GetPlayerId()), &arg.songId.Id, arg.ttsNow)
		if nil != err {
			log.Println(log.ErrorLog, err.Error())
			return err
		}
		fmt.Printf("songDetail:%v\n", songDetail)
	}
	LocalMusicList[SCENES_MUSIC_LIST].GetCurPos().Value.(*SongInfo).IsLiked = songDetail.Song.IsLiked
	// not change icon
	//if songDetail.Song.IsLiked {
	//	vui.IconLed_iconMusic003.Show();
	//} else {
	//	vui.IconLed_iconMusic001.Show();
	//}

	return nil
}

// 鉴权时，处理还有分页待拉取的情况
func hasPageNeedComfirmByScenes() (songId *SongInfo, err error) {
	defer func() {
		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	var index MusicLenType = 0
	var pageSize MusicLenType = PAGE_SIZE
	var replace bool = true
	if NormalListMap[SCENES_LIST_PAGE].HasMore {
		// 还有分页
		if val, err := NormalListMap[SCENES_LIST_PAGE].GetLast(); nil == err {
			index = val.Value.(*PageNode).PageIndex * PAGE_SIZE
		}
	} else {
		// 从第１页开始拉取
		NormalListMap[SCENES_LIST_PAGE].Init()
		index = 0
	}
	srcType := getMusicSrcType(PlayerType(hdCur.GetPlayerHd().GetPlayerId()))
	list, err := getMusicListOfSceneNew(PlayerType(hdCur.GetPlayerHd().GetPlayerId()), true, http.MusicListServerType{
		Src:     srcType,
		Id:      getSenceServiceId(srcType),
		Params:  getServiceParams(srcType),
		Start:   index,
		Size:    pageSize,
		WantTts: true,

		Mode: sceneOrder,
		// todo: 后续这些字段，服务器下发
		Oftag:        ScenesPlayerListSrc.TagName,
		OftagForShow: ScenesPlayerListSrc.TagNameShow,
		CloudId:      ScenesPlayerListSrc.CloudId,
		ModeId:       ScenesPlayerListSrc.ModeId,
	})
	//list, err := getMusicListOfScene(PlayerType(hdCur.GetPlayerHd().GetPlayerId()), srcType, getSenceServiceId(srcType), getServiceParams(srcType),
	//	index, pageSize, true, sceneOrder)
	if nil != err {
		log.Println(log.ErrorLog, err.Error())
		return nil, err
	}
	updateScenesPlayListAndPage(list, index, replace)

	if nil != LocalMusicList[SCENES_MUSIC_LIST].GetCurPos() {
		return LocalMusicList[SCENES_MUSIC_LIST].GetCurPos().Value.(*SongInfo), nil
	} else {
		return nil, MusicList_ERR_MUSIC_NULL
	}
}

func forcePauseScenesPlayer() {
	if MUSIC_ST_PAUSED != hdCur.GetPlayerHd().GetPlayerStatus().Status &&
		MUSIC_ST_IDLE != hdCur.GetPlayerHd().GetPlayerStatus().Status {
		wifiPauseApi()
		status := hdCur.GetPlayerHd().GetPlayerStatus()
		status.Status = MUSIC_ST_PAUSED
		status.CurPos = 0
		log.Println(log.DebugLog, "forcePauseScenesPlayer set CurPos to 0")
		hdCur.GetPlayerHd().SetPlayerStatus(status)
	}
	log.Println(log.DebugLog, "curSceneTask finish, id:", taskNoCur)
	hdCur.Finish(taskNoCur, true)
}

func processVoicdGraduateDisappear(curDuation time.Duration) {
	EndVoiceGrandateDisappear(RESTORE_VOL)

	lessTime := curDuation - VOICE_GRADUATE_DISAPPEAR_TIME

	log.Printf(log.WarningLog, "duration:%+v  lesstime:%+v\n", curDuation, time.Duration(lessTime))

	ctxLock.Lock()
	defer ctxLock.Unlock()

	sceneCtx, sceneCancle = context.WithCancel(context.Background())
	sceneCtxVal = context.WithValue(sceneCtx, RESTORE_VOL_KEY, RESTORE_VOL)
	//// todo 测试
	//lessTime = time.Second * 10

	if lessTime <= 0 {
		go voiceGraduateDisappear(&sceneCtxVal, int64(curDuation))
	} else {
		timeVoiceLock.Lock()
		if nil != timeVoiceGraduate {
			timeVoiceGraduate.Stop()
		}
		timeVoiceGraduate = time.AfterFunc(lessTime, voiceGraduateDisappearCb)
		timeVoiceLock.Unlock()
	}
}

func voiceGraduateDisappearCb() {
	timeVoiceLock.Lock()
	timeVoiceGraduate = nil
	timeVoiceLock.Unlock()

	go voiceGraduateDisappear(&sceneCtxVal, int64(VOICE_GRADUATE_DISAPPEAR_TIME))
}

func voiceGraduateDisappear(ctx *context.Context, timeLast int64) {
	defer func() {
		ctxLock.Lock()
		sceneCtxVal = nil
		sceneCtx = nil
		sceneCancle = nil
		ctxLock.Unlock()

		log.Println(log.DebugLog, "end to exec voiceGraduateDisappear......")

		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	if volSave, err := adau1761.GetVol(); nil == err {
		volCur := volSave
		timePeroid := timeLast / int64(volCur)
		timeMod := timeLast % int64(volCur)

		log.Printf(log.DebugLog, "begin to exec voiceGraduateDisappear......volSave:%d timePeriod:%v timeMod:%d timeLast:%v\n",
			volSave, time.Duration(timePeroid), timeMod, time.Duration(timeLast))

		if 0 != timeMod {
			volCur--
			adau1761.SetVol(volCur)
			log.Println(log.DebugLog, "begin to exec voiceGraduateDisappear......", volCur)
		}

		for {
			select {
			case <-time.After(time.Duration(timePeroid)):
				if volRead, err := adau1761.GetVol(); nil == err {
					log.Println(log.DebugLog, "volRead:", volRead, "  volCur:", volCur)
					if volRead != volCur {
						return
					}
				}

				if volCur > 1 {
					volCur--
					adau1761.SetVol(volCur)
					log.Println(log.DebugLog, "begin to exec voiceGraduateDisappear......", volCur)
				}
			case <-(*ctx).Done():
				val := (*ctx).Value(RESTORE_VOL_KEY)
				if nil != val {
					switch eVal := val.(type) {
					case sceneCtxValType:
						switch eVal {
						case RESTORE_VOL:
							adau1761.SetVol(volSave)
							log.Println(log.DebugLog, "restore vol.")
						case NOT_RESTORE_VOL:

						default:
							log.Println(log.ErrorLog, "unknow val")
						}
					default:
						log.Println(log.ErrorLog, "err type")
					}
				} else {
					adau1761.SetVol(volSave)
					log.Println(log.DebugLog, "restore vol.")
				}
				return
			}
		}

	} else {
		log.Println(log.ErrorLog, "get vol err:", err.Error())
	}
}

func EndVoiceGrandateDisappear(val sceneCtxValType) bool {
	ctxLock.Lock()
	defer ctxLock.Unlock()

	if nil != sceneCancle && nil != sceneCtxVal {
		if nil == sceneCtxVal.Err() {
			sceneCtxVal = context.WithValue(sceneCtx, RESTORE_VOL_KEY, val)

			sceneCancle()
			return true
		} else {
			log.Println(log.WarningLog, "not in voiceGrandateDisappear")
		}
	}
	return false
}

func updateListInvalid(params sceneListInvalid) {
	if data, err := json.Marshal(params); nil == err {
		http.InformMsgToServer(http.Type_Inform_Scene_List_Invalid, string(data))
	} else {
		log.Println(log.ErrorLog, err.Error())
	}
}

//func getMusicListOfScene(playerType PlayerType, src MusicSrcType, id *string, params *string, start MusicLenType,
//	size MusicLenType, ttsNow bool, mode MusicPlayMode) (*ServerMusicList, error) {
//
//	list, err := GetMusicListNew(playerType, ttsNow, http.MusicListServerType{
//		Src:     src,
//		Id:      id,
//		Params:  params,
//		Start:   start,
//		Size:    size,
//		WantTts: true,
//
//		Oftag:        NormalPlayerListSrc.TagName,
//		OftagForShow: NormalPlayerListSrc.TagNameShow,
//		Mode:         mode,
//		CloudId:      NormalPlayerListSrc.CloudId,
//		ModeId:       NormalPlayerListSrc.ModeId,
//	})
//	//list, err := getMusicList(playerType, src, id, params, start, size, ttsNow, mode)
//	if nil != recBGId {
//		for k := range list.MusicList.SongList {
//			list.MusicList.SongList[k].BGId = *recBGId
//		}
//	}
//	return list, err
//}

func getMusicListOfSceneNew(playerType PlayerType, ttsNow bool, params http.MusicListServerType) (*ServerMusicList, error) {
	list, err := GetMusicListNew(playerType, ttsNow, params)
	//list, err := getMusicList(playerType, src, id, params, start, size, ttsNow, mode)
	if nil != recBGId {
		for k := range list.MusicList.SongList {
			list.MusicList.SongList[k].BGId = *recBGId
		}
	}
	return list, err
}
