package player

import (
	"encoding/json"
	"fmt"
	"math"
	. "netease_control_center/globals"
	"netease_control_center/interfaces"
	"netease_control_center/interfaces/cserveraddr"
	"netease_control_center/interfaces/http"
	"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/storage"
	"netease_control_center/nembd/controller/adau1761"
	"strconv"
	"sync"
	"time"
	"netease_control_center/interfaces/eventPoint"
)

var (
	rokidUrlId uint16 = 0

	hasProcessVol bool = false
	againVol           = make(chan bool, 1)
	outVol             = make(chan bool, 1)

	volPer, sysVolPer int
	volProcessLock    sync.Mutex
)

type MusicCmdExtra struct {
	RespId string
}

func sendCurPlayerMusicCmd(cmd uint32, params ...interface{}) error {
    playerID := schedule.GetCurPlayerId()
	return sendMusicCmd(playerID ,cmd, params ...)
}

func sendMusicCmd(playerId PlayerType,cmd uint32, params ...interface{}) error {
	defer func() {
		if err := recover(); err != nil {
			log.Println(log.ErrorLog, err)
		}
	}()

	var tmp []byte
	var err error

	var tosend PlayerCmdStruct
	tosend.PlayerId = playerId
	if playerId >= PlayerType_Other || playerId < PlayerType_Wifi {
		log.Printf(log.ErrorLog, "playerId error:%d\n", playerId)
		return PARAMS_ERR_INVALID
	}

	switch cmd {
	case CMD_SE_PLAY:
		if 2 == len(params) {
			// params : songId *string, songUrl *string
			tosend.Uuid = *(params[0].(*string))
			tosend.Src = *(params[1].(*string))
		} else {
			log.Println(log.DebugLog, PARAMS_ERR_INVALID.Error())
			return PARAMS_ERR_INVALID
		}
	case CMD_AIRPLAYER_PLAY:
		if 2 == len(params) {
			// params: arg AirParams2Play, musicUUid *string
			switch eVal := params[0].(type) {
			case AirParams2Play:
				if len(eVal.Fifo) > 0 {
					tosend.Fifo = eVal.Fifo
					tosend.Bytes = eVal.Bits / 8
					tosend.Channel = eVal.Channel
					tosend.SampleRate = eVal.SampleRate
				} else {
					return PARAMS_ERR_INVALID
				}
			default:
				log.Println(log.ErrorLog, "err airplay type")
				return PARAMS_ERR_INVALID
			}

			tosend.Uuid = *(params[1].(*string))
		} else {
			// 暂时没有反向控制，该处用于replay
			//return PARAMS_ERR_INVALID
		}
	case CMD_PLAYER_PLAY, CMD_PLAYER_PERPARE_NEXT:
		// params : songId *string, songUrl *string, tts *string, ttsType AdSrcType, duration uint32 , gain float32, bgurl *string, extraConfig MusicCmdExtra
		if len(params) >= 7 {
			var extraConfig MusicCmdExtra
			if len(params) >= 8 {
				extraConfig = params[7].(MusicCmdExtra)
			}
			// 参数不为空时，play 指令
			tosend.Uuid = *(params[0].(*string))
			tosend.Src = *(params[1].(*string))
			tosend.AdSrcType = params[3].(AdSrcType)
			switch tts := params[2].(type) {
			case *string:
				if nil != tts && len(*tts) > 0 {
					var repeatSrcType AdSrcType
					switch tosend.AdSrcType {
					case AdSrcType_FILE_MIX:
						repeatSrcType = AdSrcType_FILE
					case AdSrcType_TTS_MIX:
						repeatSrcType = AdSrcType_TTS
					default:
						log.Println(log.ErrorLog, "err AdSrcType:", tosend.AdSrcType)
					}

					tosend.Tts = *tts
					repeatcontent := *tts

					if isNetConnect() {
						if !schedule.UrlSrc(*tts) {
							if XIAOYA_TTS == TtsSel || ROKID_TTS_URL == TtsSel {
								tosend.AdSrcType = AdSrcType_FILE_MIX
								if XIAOYA_TTS == TtsSel {
									tosend.Tts = XIAOYA_URL + *tts + XIAOYA_CFG
								} else {
									tosend.Tts = getRokidUrlSrc(*tts, extraConfig.RespId)
								}
							}
							repeatSrcType = AdSrcType_TTS
						}
					} else {
						log.Println(log.WarningLog, "isNetConnect:", isNetConnect())
						repeatSrcType = AdSrcType_FILE
						tosend.AdSrcType = AdSrcType_FILE_MIX
						tosend.Src = vui.SoundNotice_Error_Network.GetPath()
						repeatcontent = tosend.Src
					}

					tosend.VolRestoreTime = VolMixRestoreTime.GetCfgListVui()
					if vol, err := adau1761.GetVol(); nil == err {
						volPer := GetPerByVol(vol)
						if volPer <= VolLowThreshold.GetCfgListVui() {
							tosend.VolBegin = uint8(VolLowVal.GetCfgListVui())
						} else if volPer <= VolMiddleThreshold.GetCfgListVui() {
							tosend.VolBegin = uint8(VolMiddleVal.GetCfgListVui())
						} else if volPer <= VolHighThreshold.GetCfgListVui() {
							tosend.VolBegin = uint8(VolHighVal.GetCfgListVui())
						}
					} else {
						tosend.VolBegin = uint8(VolMixBegin.GetCfgListVui())
					}

					ttsInfo := schedule.GetNoticePlayerParams(repeatcontent, CONST_AUDIO_ID, repeatSrcType,
						TTS_LEVEL_1, true, false)
					schedule.SetTtsRepeatInfo(ttsInfo)
				}
			default:

			}
			tosend.Duration = params[4].(uint32)

			tosend.Gain = params[5].(float32)

			bgurl := params[6].(*string)
			if nil != bgurl && len(*bgurl) > 0 {
				tosend.BGUrl = *bgurl
			}

			//调用播放器埋点
			eventPoint.SendPlayCmd2IhwPlayer(tosend.Uuid, nil, eventPoint.EpOs_Server, RspId)

		} else {
			// nil params for continue play
		}
	case CMD_PLAYER_SEEKTO, CMD_PLAYER_SEEKTO_PLAY:
		// params : seekTime int
		if 1 == len(params) {
			tosend.SkTime = int(params[0].(uint32))
		} else {
			log.Println(log.DebugLog, PARAMS_ERR_INVALID.Error())
			return PARAMS_ERR_INVALID
		}
	default:
		// nil params
	}
	tmp, err = json.Marshal(&tosend)
	if nil != err {
		log.Println(log.ErrorLog, err.Error())
		return err
	}
	sendCmd2Dbus(cmd, &tmp)
	return nil
}

func sendCmd2Dbus(cmd uint32, data *[]byte) {
	interfaces.SendCommand(MODULE_CONTROLLER, MODULE_PLAYER, cmd, data)
}

/**
only for airplay dlna blue vol change
*/
func VolChangeByPercent(percent int) {
	newPer := percent
	vol := GetVolByPer(newPer)
	log.Println(log.DebugLog, "vol:", vol, "  VolPer:", newPer)
	adau1761.SetVol(int(vol))

	CurVboxVolSt.VolumeScale = MaxVolume
	CurVboxVolSt.VolumePer = uint8(newPer)

	var st VboxBroadcastSt = VboxBroadcastSt{
		//PlayerSt:  CurPlayerSt,
		//VboxVolSt: CurVboxVolSt,
		//ScenesSt:  CurScenesSt,
		PlayerStPtr:   &CurPlayerSt,
		VboxVolStPtr:  &CurVboxVolSt,
		ScenesStPtr:   &CurScenesSt,
		CurrentStatus: CurCurrentStatus,
	}
	st.VboxVolStPtr.Action = VOL_SRC_CTRL_ACTION
	TrigBroadcastStatus(st)
}

/**
only for airplay dlna blue vol change
*/
func VolChangeBroadcast(vol int) {
	log.Println(log.DebugLog, "vol:", vol)
	//adau1761.SetVol(vol)

	CurVboxVolSt.VolumeScale = MaxVolume
	CurVboxVolSt.VolumePer = uint8(GetPerByVol(vol))

	var st VboxBroadcastSt = VboxBroadcastSt{
		PlayerStPtr:   &CurPlayerSt,
		VboxVolStPtr:  &CurVboxVolSt,
		ScenesStPtr:   &CurScenesSt,
		CurrentStatus: CurCurrentStatus,
	}
	st.VboxVolStPtr.Action = VOL_SRC_CTRL_ACTION
	TrigBroadcastStatus(st)
}

func GetSystemVolPer() (int, error) {
	if vol, err := adau1761.GetVol(); nil == err {
		volPerTmp := GetPerByVol(vol)
		log.Println(log.DebugLog, "sysCurVol:", vol, "  sysCurVolPer:", volPerTmp)
		return volPerTmp, nil
	} else {
		return 0, err
	}
}

/**
从服务器拉取的结果中获取可以播放的内容
1、如果下发的url有效，播放该url
2、如果url无效，从下发的list中寻找第一个能播放的歌曲
*/
func getCanPlaySong(e interface{}) (*SongInfo, *string) {
	defer func() {
		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

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

	var songList *[]SongInfo = nil
	switch eVal := e.(type) {
	case *ServerMusicList:
		songId = &(eVal.MusicList.SongList[eVal.MusicList.Index])
		songUrl = &(eVal.MusicList.Url)
		songId.Gain = eVal.MusicList.Gain

		songList = &(eVal.MusicList.SongList)
	case *ServerMusicLists:
		songId = &(eVal.MusicLists[0].SongList[eVal.MusicLists[0].Index])
		songUrl = &eVal.MusicLists[0].Url
		songId.Gain = eVal.MusicLists[0].Gain

		songList = &(eVal.MusicLists[0].SongList)
	default:

	}
	if nil == songUrl || len(*songUrl) == 0 {
		log.Println(log.WarningLog, "server songUrl invalid")
		// 下发歌曲的url无效，从列表中找一首能播放的歌曲，如果没有，返回nil
		// 正常流程中，服务器需要保证下发的歌曲的url一定要有效，因此如果走到此分之，记录榜单、每日推荐的索引不考虑变化
		var found bool = false
		if nil != songList && len(*songList) > 0 {
			for _, v := range *songList {
				if v.CanPlay {
					found = true
					songId = &v
					break
				}
			}
		}

		if !found {
			log.Println(log.ErrorLog, "no music can play")
			return nil, nil
		}
	}

	return songId, songUrl
}

func getMusicSrcType(playerId PlayerType) MusicSrcType {
	var srcType MusicSrcType = PlayListSrcRec.MusicSrc
	if PlayerType_Wifi == playerId {
		srcType = PlayListSrcRec.MusicSrc
	} else if PlayerType_Other == playerId {
		// 场景播放时，获取到的id就是PlayerType_Other
		//srcType = NormalPlayerListSrc.MusicSrc
		srcType = ScenesPlayerListSrc.MusicSrc
	} else {
		srcType = ScenesPlayerListSrc.MusicSrc
	}
	switch srcType {
	case USER_LISTS, USER_LISTS_COLLECT, USER_LISTS_CREATE, MUSIC_LIST_WORD, MUSIC_LIST_SERACH_WORD:
		srcType = MUSIC_LIST_ID
	case ALBUM_ARTIST_ID:
		srcType = ALBUM_ID
	case DAILY_FM_MIX:
		srcType = DAILY_PUSH
	default:
	}
	log.Println(log.DebugLog, "getMusicSrcType srcType:", srcType)
	return srcType
}

func forcePausePlayer(playerType PlayerType) {
	switch playerType {
	case PlayerType_Wifi, PlayerType_Dlna, PlayerType_Airplay, PlayerType_Bt:
		if MUSIC_ST_PAUSED != PlayerHander[playerType].GetPlayerStatus().Status &&
			MUSIC_ST_IDLE != PlayerHander[playerType].GetPlayerStatus().Status {
			wifiPauseApi()
			status := PlayerHander[playerType].GetPlayerStatus()
			status.Status = MUSIC_ST_PAUSED
			status.CurPos = 0
			log.Println(log.DebugLog, "forcePausePlayer set CurPos to 0")
			PlayerHander[playerType].SetPlayerStatus(status)
		}
	default:
		// scene player
		forcePauseScenesPlayer()
	}

}

func getSongPlayInfo(playerType PlayerType, id *string, ttsNow bool) (*ServerSongPlay, error) {
	defer func() {
		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	song, _, _, err := http.GetSongPlayInfoDo(id)
	if nil != err {
		if len(err.Error()) > 0 {
			if ttsNow {
				forcePausePlayer(playerType)
				mscToastFile(vui.SoundNotice_PullFromServer_Timeout.GetPath(), CONST_AUDIO_ID, true, false, nil)
			} else {
				ttsNoMusic = true
				ttsMusicUuid = *wifiPlayerSt.MusicId
				ttsInfo = err.Error()
			}
		}
		log.Println(log.ErrorLog, err.Error())
		return nil, err
	}
	if nil == song {
		log.Println(log.WarningLog, Server_ERR_RET.Error())

		if ttsNow {
			forcePausePlayer(playerType)
			mscToastFile(vui.SoundNotice_PullFromServer_Timeout.GetPath(), CONST_AUDIO_ID, true, false, nil)
		} else {
			if nil != wifiPlayerSt.MusicId && len(*wifiPlayerSt.MusicId) > 0 {
				ttsNoMusic = true
				ttsMusicUuid = *wifiPlayerSt.MusicId
				ttsInfo = Server_ERR_RET.Error()
			}
		}
		return nil, Server_ERR_RET
	} else {
		log.Printf(log.DebugLog, "playInfo:%+v\n", song)
	}
	return song, nil
}

//获取音乐单个歌单的歌曲数目
func GetMusicPlayListInfo(playerType PlayerType, ttsNow bool, listinfo http.PlayListServerType) (*MusicPlayListInfo, error) {
	defer func() {
		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	listInfo, _, _, err := http.GetMusicPlayListInfoDo(listinfo)
	if nil != err {
		log.Println(log.ErrorLog, err.Error())
		return nil, err
	}
	if nil == listInfo {
		log.Println(log.WarningLog, Server_ERR_RET.Error())
		return nil, Server_ERR_RET
	} else {
		log.Printf(log.DebugLog, "music playList Info:%+v\n", listInfo)
	}
	return listInfo, nil
}

//获取一个电台的总节目数
func GetRadioPlayListInfo(playerType PlayerType, ttsNow bool, listinfo http.PlayListServerType) (*RadioPlayListInfo, error) {
	defer func() {
		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	listInfo, _, _, err := http.GetRadioPlayListInfoDo(listinfo)
	if nil != err {
		log.Println(log.ErrorLog, err.Error())
		return nil, err
	}
	if nil == listInfo {
		log.Println(log.WarningLog, Server_ERR_RET.Error())
		return nil, Server_ERR_RET
	} else {
		log.Printf(log.DebugLog, "radio playList Info:%+v\n", listInfo)
	}
	return listInfo, nil
}

func GetSongDetail(playerType PlayerType, id *string, ttsNow bool) (*ServerSongDetail, error) {
	return getSongDetailInfo(playerType, id, ttsNow)
}

func getSongDetailInfo(playerType PlayerType, id *string, ttsNow bool) (*ServerSongDetail, error) {
	defer func() {
		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	song, _, _, err := http.GetSongDetailInfoDo(id)
	if nil != err {
		if len(err.Error()) > 0 {
			if ttsNow {
				forcePausePlayer(playerType)
				mscToastFile(vui.SoundNotice_PullFromServer_Timeout.GetPath(), CONST_AUDIO_ID, true, false, nil)
			} else {
				if nil != wifiPlayerSt.MusicId && len(*wifiPlayerSt.MusicId) > 0 {
					ttsNoMusic = true
					ttsMusicUuid = *wifiPlayerSt.MusicId
					ttsInfo = err.Error()
				}
			}
		}
		log.Println(log.ErrorLog, err.Error())
		return nil, err
	}

	if nil == song {
		log.Println(log.WarningLog, Server_ERR_RET.Error())

		if ttsNow {
			forcePausePlayer(playerType)
			mscToastFile(vui.SoundNotice_PullFromServer_Timeout.GetPath(), CONST_AUDIO_ID, true, false, nil)
		} else {
			if nil != wifiPlayerSt.MusicId && len(*wifiPlayerSt.MusicId) > 0 {
				ttsNoMusic = true
				ttsMusicUuid = *wifiPlayerSt.MusicId
				ttsInfo = Server_ERR_RET.Error()
			}
		}
		return nil, Server_ERR_RET
	} else {
		if song.Song.Code != SONG_CAN_PLAY {
			return nil, No_Permission_Err
		}

		log.Println(log.DebugLog, "get songDetail:", song.Song.Id)
		fmt.Printf("songDetail:%+v\n", song)
	}
	return song, nil
}

//todo: 调用该接口需要判断返回的歌单数据是否为空
//func getMusicList(playerType PlayerType, src MusicSrcType, id *string, params *string, start MusicLenType,
//	size MusicLenType, ttsNow bool, mode MusicPlayMode) (*ServerMusicList, error) {
//	defer func() {
//		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
//			if err := recover(); err != nil {
//				log.Println(log.ErrorLog, err)
//			}
//		}
//	}()
//
//	list, code, tts, err := http.GetMusicListDo(src, id, params, start, size, mode)
//	if nil != err {
//		if http.SERVER_RET_NIL_OR_INVALID_LIST == code {
//			log.Println(log.WarningLog, "nil music list")
//			forcePausePlayer(playerType)
//			return nil, MusicList_ERR_MUSICINFO_NULL
//		}
//
//		if nil != tts && len(*tts) > 0 {
//			if ttsNow {
//				forcePausePlayer(playerType)
//				//NoticePlayerSendSCmd(schedule.SCMD_SuperTask_Tts_Play, *tts, CONST_AUDIO_ID, AdSrcType_TTS, TTS_LEVEL_1, true, false)
//				mscToastTts(*tts, CONST_AUDIO_ID, true, false, nil)
//			} else {
//				ttsNoMusic = true
//				ttsMusicUuid = *wifiPlayerSt.MusicId
//				ttsInfo = *tts
//			}
//		}
//		log.Println(log.ErrorLog, err.Error())
//		return nil, err
//	}
//
//	if nil == list || nil == list.MusicList.SongList {
//		log.Println(log.WarningLog, Server_ERR_RET.Error())
//
//		if ttsNow {
//			forcePausePlayer(playerType)
//			//NoticePlayerSendSCmd(schedule.SCMD_SuperTask_Tts_Play, Server_ERR_RET.Error(), CONST_AUDIO_ID, AdSrcType_TTS,
//			//	TTS_LEVEL_1, true, false)
//			mscToastTts(Server_ERR_RET.Error(), CONST_AUDIO_ID, true, false, nil)
//		} else {
//			if nil != wifiPlayerSt.MusicId && len(*wifiPlayerSt.MusicId) > 0 {
//				ttsNoMusic = true
//				ttsMusicUuid = *wifiPlayerSt.MusicId
//				ttsInfo = Server_ERR_RET.Error()
//			}
//		}
//		return nil, Server_ERR_RET
//	} else {
//		log.Printf(log.DebugLog, "list:%+v\n", list)
//	}
//	return list, nil
//}

func GetMusicListNew(playerType PlayerType, ttsNow bool, serverList http.MusicListServerType) (*ServerMusicList, error) {
	defer func() {
		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()
	list, code, _, err := http.GetMusicListDoNew(serverList)
	if nil != err {
		if http.SERVER_RET_NIL_OR_INVALID_LIST == code {
			log.Println(log.WarningLog, "nil music list")
			forcePausePlayer(playerType)
			return nil, MusicList_ERR_MUSICINFO_NULL
		}

		if http.SERVER_RET_TRIG_CMD_OK == code {
			log.Println(log.WarningLog, "server is return OK")
			return nil, nil
		}

		if len(err.Error()) > 0 {
			if ttsNow {
				forcePausePlayer(playerType)
				mscToastFile(vui.SoundNotice_PullFromServer_Timeout.GetPath(), CONST_AUDIO_ID, true, false, nil)
			} else {
				ttsNoMusic = true
				ttsMusicUuid = *wifiPlayerSt.MusicId
				ttsInfo = err.Error()
			}
		}
		log.Println(log.ErrorLog, err.Error())
		return nil, err
	}

	//收到服务器推送，首先检查是否服务器err tts，如果有err tts，就播收到的
	if nil != list && list.ErrTts != "" {
		log.Printf(log.WarningLog, "receive server err tts:%s , list:%+v\n", list.ErrTts, list)
		return list, MusicList_ERR_MUSIC_NULL
	}

	if nil == list || nil == list.MusicList.SongList {
		log.Println(log.WarningLog, Server_ERR_RET.Error())

		if ttsNow {
			forcePausePlayer(playerType)
			mscToastFile(vui.SoundNotice_PullFromServer_Timeout.GetPath(), CONST_AUDIO_ID, true, false, nil)
		} else {
			if nil != wifiPlayerSt.MusicId && len(*wifiPlayerSt.MusicId) > 0 {
				ttsNoMusic = true
				ttsMusicUuid = *wifiPlayerSt.MusicId
				ttsInfo = Server_ERR_RET.Error()
			}
		}
		return nil, Server_ERR_RET
	} else {
		fmt.Printf("\nGet Music List from server:%+v\n", list)
	}

	return list, nil
}
func GetMusicListsNew(playerType PlayerType, ttsNow bool, serverList http.MusicListServerType) (*ServerMusicLists, error) {
	defer func() {
		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()
	log.Println(log.DebugLog, "start index of song: ", serverList.Start)

	var hasTry bool = false
	var hasBack bool = false
try:
	if playUserLists {
		log.Println(log.DebugLog, "play user lists, type:", serverList.Src)
	}
	list, _, _, err := http.GetMusicListsDoNew(serverList)
	if nil != err {
		if len(err.Error()) > 0 {
		back:
			if playUserLists && !hasTry {
				log.Println(log.WarningLog, "try get music lists")
				// 如果当前播放我的歌单，拉取创建或是收藏时失败，拉取另外一个歌单
				if USER_LISTS_CREATE == curUserListsType {
					curUserListsType = USER_LISTS_COLLECT
				} else {
					curUserListsType = USER_LISTS_CREATE
				}
				serverList.Src = curUserListsType
				hasTry = true
				goto try
			}
			if !hasBack {
				// 可能存在用户只有创建歌单或是收藏歌单中的一个，因此try拉取的时候，可能失败，此时，再次拉取上一次的歌单列表
				log.Println(log.DebugLog, "playUserLists go back to the old lists")
				hasTry = false
				hasBack = true
				goto back
			}

			if ttsNow {
				forcePausePlayer(playerType)
				mscToastFile(vui.SoundNotice_PullFromServer_Timeout.GetPath(), CONST_AUDIO_ID, true, false, nil)
			} else {
				ttsNoMusic = true
				ttsMusicUuid = *wifiPlayerSt.MusicId
				ttsInfo = err.Error()
			}
		}
		log.Println(log.ErrorLog, err.Error())
		return nil, err
	}
	if nil == list || nil == list.MusicLists || nil == list.MusicLists[0].SongList {
		log.Println(log.WarningLog, Server_ERR_RET.Error())

		if ttsNow {
			forcePausePlayer(playerType)
			mscToastFile(vui.SoundNotice_PullFromServer_Timeout.GetPath(), CONST_AUDIO_ID, true, false, nil)
		} else {
			if nil != wifiPlayerSt.MusicId && len(*wifiPlayerSt.MusicId) > 0 {
				ttsNoMusic = true
				ttsMusicUuid = *wifiPlayerSt.MusicId
				ttsInfo = Server_ERR_RET.Error()
			}
		}
		return nil, Server_ERR_RET
	} else {
		fmt.Printf("\nGet Music Lists from server:%+v\n", list)
	}
	return list, nil
}

//func getMusicLists(playerType PlayerType, src MusicSrcType, id *string, params *string, start MusicLenType,
//	size MusicLenType, ttsNow bool, mode MusicPlayMode) (*ServerMusicLists, error) {
//	defer func() {
//		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
//			if err := recover(); err != nil {
//				log.Println(log.ErrorLog, err)
//			}
//		}
//	}()
//	log.Println(log.DebugLog, "start: ", start)
//
//	var hasTry bool = false
//	var hasBack bool = false
//try:
//	if playUserLists {
//		log.Println(log.DebugLog, "play user lists, type:", src)
//	}
//	//list, tts, err := http.GetMusicLists(src, id, params, start, size)
//	list, _, tts, err := http.GetMusicListsDo(src, id, params, start, size, mode)
//	if nil != err {
//		if nil != tts && len(*tts) > 0 {
//		back:
//			if playUserLists && !hasTry {
//				log.Println(log.WarningLog, "try get music lists")
//				// 如果当前播放我的歌单，拉取创建或是收藏时失败，拉取另外一个歌单
//				if USER_LISTS_CREATE == curUserListsType {
//					curUserListsType = USER_LISTS_COLLECT
//				} else {
//					curUserListsType = USER_LISTS_CREATE
//				}
//				src = curUserListsType
//				hasTry = true
//				goto try
//			}
//			if !hasBack {
//				// 可能存在用户只有创建歌单或是收藏歌单中的一个，因此try拉取的时候，可能失败，此时，再次拉取上一次的歌单列表
//				log.Println(log.DebugLog, "playUserLists go back to the old lists");
//				hasTry = false
//				hasBack = true
//				goto back
//			}
//
//			if ttsNow {
//				forcePausePlayer(playerType)
//				//NoticePlayerSendSCmd(schedule.SCMD_SuperTask_Tts_Play, *tts, CONST_AUDIO_ID, AdSrcType_TTS, TTS_LEVEL_1, true, false)
//				mscToastTts(*tts, CONST_AUDIO_ID, true, false, nil)
//			} else {
//				ttsNoMusic = true
//				ttsMusicUuid = *wifiPlayerSt.MusicId
//				ttsInfo = *tts
//			}
//		}
//		log.Println(log.ErrorLog, err.Error())
//		return nil, err
//	}
//	if nil == list || nil == list.MusicLists || nil == list.MusicLists[0].SongList {
//		log.Println(log.WarningLog, Server_ERR_RET.Error())
//
//		if ttsNow {
//			forcePausePlayer(playerType)
//			//NoticePlayerSendSCmd(schedule.SCMD_SuperTask_Tts_Play, Server_ERR_RET.Error(), CONST_AUDIO_ID, AdSrcType_TTS,
//			//	TTS_LEVEL_1, true, false)
//			mscToastTts(Server_ERR_RET.Error(), CONST_AUDIO_ID, true, false, nil)
//		} else {
//			if nil != wifiPlayerSt.MusicId && len(*wifiPlayerSt.MusicId) > 0 {
//				ttsNoMusic = true
//				ttsMusicUuid = *wifiPlayerSt.MusicId
//				ttsInfo = Server_ERR_RET.Error()
//			}
//		}
//		return nil, Server_ERR_RET
//	} else {
//		log.Printf(log.DebugLog, "lists:%+v\n", list)
//	}
//	return list, nil
//}

func BirthdayPlayProcess() {
	if !HasCheckBirthdayInfo {
		if res, _, _, err := http.GetBirthdayInfo(); nil == err {
			HasCheckBirthdayInfo = true
			var birthdaySql BirthdaySQL
			birthdaySql.HasCheck = true
			birthdaySql.Date = GetDateStr()
			err := storage.Config_set(MODULE_CONTROLLER, SQL_KEY_BIRTHDAY, birthdaySql)
			if nil != err {
				log.Println(log.DebugLog, err.Error())
			}

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

				schedule.SetPlayerArgs(PlayerType_Other, &tmp, true)
				PlayerHander[PlayerType_Other].RequestPlay_Async(nil)
			}
		} else {
			log.Println(log.DebugLog, err.Error())
		}
	}
}

func getSysVol() VboxVolSt {
	if vol, err := adau1761.GetVol(); nil == err {
		CurVboxVolSt.VolumeScale = MaxVolume
		volPer := uint8(GetPerByVol(vol))
		CurVboxVolSt.VolumePer = uint8(volPer)
		CurVboxVolSt.MuteMic = false
		CurVboxVolSt.MuteVol = false
	} else {
		log.Println(log.ErrorLog, err.Error())
	}
	return CurVboxVolSt
}

func isNetConnect() bool {
	return InternetConnect && CurBootState != BootStage_WIFIPARING
}

func text2UrlSrc(srcType AdSrcType) AdSrcType {
	switch srcType {
	case AdSrcType_TTS:
		return AdSrcType_FILE
	case AdSrcType_TTS_NOT_REPLAY_MUSIC:
		return AdSrcType_FILE_NOT_REPLAY_MUSIC
	case AdSrcType_TTS_MIX:
		return AdSrcType_FILE_MIX
	}
	return AdSrcType_FILE
}

func ProjectorVolProcess(newVolPer int) {
	log.Println(log.DebugLog, "ProjectorVolProcess lock")
	volProcessLock.Lock()
	defer func() {
		volProcessLock.Unlock()
		log.Println(log.DebugLog, "ProjectorVolProcess unLock")
	}()

	volPer = newVolPer
	if sysVolPer, err = GetSystemVolPer(); nil == err {
		if math.Abs(float64(sysVolPer-volPer)) < VOL_THREADSHOLD_PHONE2BOX {
			VolChangeByPercent(volPer)
			if hasProcessVol {
				outVol <- true
			}
		} else {
			if hasProcessVol {
				againVol <- true
			} else {

				go func() {
					var goalVerPer int
					var oldVerPer int
					step := int(float32(VOL_PRE_CHANGE_STEP) * MaxVolume / 100)
					hasProcessVol = true
				Agian_Vol:
					goalVerPer = GetVolByPer(volPer)
					oldVerPer = GetVolByPer(sysVolPer)
					for {
						select {
						case <-againVol:
							goto Agian_Vol
						//PV1-2838 音量线性增加 从500ms修改为200ms
						case <-time.After(200 * time.Millisecond):
							//case <-time.After(2 * time.Second):
							tmpPerVol, err := adau1761.GetVol()
							if nil == err {
								if tmpPerVol != oldVerPer {
									log.Println(log.WarningLog, "out because of vol change,cur:", tmpPerVol,
										"  old:", oldVerPer)
									goto Out_Vol
								}
							}

							if goalVerPer == oldVerPer {
								log.Println(log.WarningLog, "finis vol change")
								VolChangeBroadcast(goalVerPer)
								goto Out_Vol
							} else if goalVerPer > oldVerPer {
								oldVerPer = oldVerPer + step
							} else {
								oldVerPer = oldVerPer - step
							}
							adau1761.SetVol(oldVerPer)
							//VolChange(oldVerPer)
							log.Println(log.WarningLog, "cur perVol:", oldVerPer)
						case <-outVol:
							log.Println(log.WarningLog, "trig out")
							goto Out_Vol
						}
					}
				Out_Vol:
					hasProcessVol = false
					select {
					case <-againVol:

					default:

					}
					select {
					case <-outVol:

					default:

					}
					volPerFinish, _ := GetSystemVolPer()
					log.Println(log.WarningLog, "out dlna vol process:", volPerFinish)
				}()

			}
		}
	} else {
		log.Println(log.ErrorLog, err.Error())
		return
	}
}

func ClearProjectorVolProcess() {
	log.Println(log.DebugLog, "ClearProjectorVolProcess lock")
	volProcessLock.Lock()
	defer func() {
		volProcessLock.Unlock()
		log.Println(log.DebugLog, "ClearProjectorVolProcess unLock")
	}()
	if hasProcessVol {
		outVol <- true
	}
}

func SetPlayUserListsFlag(st bool) {
	playUserLists = st
}

func ttsExecVuiStart() {
	vui.IconLed_iconMusic004.Show()
	tmp := schedule.DirectLed{EffectId: atservice.GUIDE_EFFECT_ID_EXEC_CMD, Action: atservice.GUIDE_ACTION_START}
	led_mcu.DirectLedControl(tmp.EffectId, tmp.Action, tmp.StartLampNum, []byte{uint8(adau1761.MustGetVol())})
}

func ttsExecVuiStop() {
	tmp := schedule.DirectLed{EffectId: atservice.GUIDE_EFFECT_ID_EXEC_CMD, Action: atservice.GUIDE_ACTION_STOP}
	led_mcu.DirectLedControl(tmp.EffectId, tmp.Action, tmp.StartLampNum, []byte{})
	vui.IconLed_Clear.Show()
}

func mscToastFile(file string, uuid string, needRepeat bool, keep bool, configChange func(params *schedule.NoticePlayerParams)) {
	tmpconfig := GetDefaultNoticePlayerParams()
	tmpconfig.NeedRepeat = needRepeat
	tmpconfig.Keep = keep
	tmpconfig.Level = TTS_LEVEL_1
	tmpconfig.SrcType = AdSrcType_FILE
	tmpconfig.Tts = file
	tmpconfig.Uuid = uuid
	tmpconfig.IconIn = vui.IconLed_DoNothing
	tmpconfig.IconOut = vui.IconLed_DoNothing

	tmpconfig.BeforeSpecialFunc = func() {
		if IsMicClose == false {
			ttsExecVuiStart()
		}
	}

	tmpconfig.SpecialFunc = func(arg schedule.TtsPlayerArg) {
		if IsMicClose == false {
			ttsExecVuiStop()
		}

		//检查是否网络缓存播放失败
		if arg.StopReason == schedule.NoticeStopReason_Error {
			go mscToastFile(vui.SoundNotice_Song_Get_Timeout.GetPath(), CONST_AUDIO_ID, true, false, nil)
		}
	}

	if configChange != nil {
		configChange(&tmpconfig)
	}

	NoticePlayerSendSCmd2(schedule.SCMD_SuperTask_Tts_Ctrl_Play, tmpconfig)
}

func mscToastTts(tts string, RespId string, needRepeat bool, keep bool, configChange func(params *schedule.NoticePlayerParams)) {
	tmpconfig := GetDefaultNoticePlayerParams()
	tmpconfig.Keep = keep
	tmpconfig.NeedRepeat = needRepeat
	tmpconfig.Level = TTS_LEVEL_1
	tmpconfig.SrcType = AdSrcType_TTS
	tmpconfig.Tts = tts
	tmpconfig.Uuid = RespId
	tmpconfig.IconIn = vui.IconLed_DoNothing
	tmpconfig.IconOut = vui.IconLed_DoNothing

	tmpconfig.BeforeSpecialFunc = func() {
		if IsMicClose == false {
			ttsExecVuiStart()
		}
	}

	tmpconfig.SpecialFunc = func(arg schedule.TtsPlayerArg) {
		if IsMicClose == false {
			ttsExecVuiStop()
		}

		//检查是否网络缓存播放失败
		if arg.StopReason == schedule.NoticeStopReason_Error {
			go mscToastFile(vui.SoundNotice_Song_Get_Timeout.GetPath(), CONST_AUDIO_ID, false, false, nil)
		}
	}

	if configChange != nil {
		configChange(&tmpconfig)
	}

	NoticePlayerSendSCmd2(schedule.SCMD_SuperTask_Tts_Ctrl_Play, tmpconfig)
}

func getRokidUrlSrc(tts string, resid string) (ret string) {
	defer log.Println(log.DebugLog, ret)

	var id string
	if len(resid) == 0 {
		id = getResId()
	} else {
		id = resid
	}
	return fmt.Sprintf(ROKID_SERVER_URL, cserveraddr.GetTtsServerAddr(), id, Uuid, "1001", tts)
}

func getResId() string {
	if rokidUrlId < 0xffff {
		rokidUrlId++
	} else {
		rokidUrlId = 0
	}
	return strconv.Itoa(int(rokidUrlId))
}
