package schedule

import (
	"errors"
	"netease_control_center/globals"
	"netease_control_center/interfaces/eventPoint"
	"netease_control_center/interfaces/log"
	"netease_control_center/modules/hardware/misc"
	"strings"
	"sync"
)

var (
	playermap = make(map[uint32]*SPlayer)
	lk        sync.Mutex
	playerid  uint32 = uint32(globals.PlayerType_Other)

	statusRec PlayerDetail
)

func InitPlayerFramework() {
	lockCore()
	defer unlockCore()

	if curRunningPlayer.IsPlaying() {
		curRunningPlayer.Pause(false, "")
	}

	if curRunningPlayer.GetPlayerId() != uint32(globals.PlayerType_Wifi) {
		curRunningPlayer.ActiveStatus = false
		curRunningPlayer.SetInterruptStatus(false)
		if player := playermap[uint32(globals.PlayerType_Wifi)]; player != nil {
			curRunningPlayer = player
		}
	}

	curRunningPlayer.playerDetail = PlayerDetail{Status: globals.MUSIC_ST_IDLE}
	curRunningPlayer.PlayAfterResume = false
	curRunningPlayer.HasRequestPlayWhenInterrupt = false
}

func GetAllPlayerStatus() string {
	var ret string
	for _, v := range playermap {
		ret += v.String() + "\n\n"
	}
	return ret
}

func SetPlayerArgs(playertype globals.PlayerType, args ...interface{}) bool {
	if player, ok := playermap[uint32(playertype)]; ok && player != nil {
		return player.SetArg(args...)
	}

	return false
}

func FindPlayerBySCmd(cmd SCMD) *SPlayer {
	for _, splayer := range playermap {
		if cmdarr := splayer.Player.GetSupportedSCmd(); len(cmdarr) != 0 {
			for _, supportedcmd := range cmdarr {
				if cmd == supportedcmd {
					return splayer
				}
			}
		}
	}

	return nil
}

func RegistePlayer(player RegistedPlayer, playertype globals.PlayerType) (error, PlayerHd) {
	lk.Lock()
	defer lk.Unlock()

	if player.Pause == nil || player.Play == nil || player.Stop == nil || player.IsOnline == nil || player.IfForcePause == nil || player.PrepareNext == nil {
		log.Println(log.DebugLog, "registe player fail, interface nil!!")
		return errors.New("Interface is nil, not allow!"), nil
	}

	if playertype < globals.PlayerType_Other {
		if _, ok := playermap[uint32(playertype)]; ok == true {
			return errors.New("The type has been registed!!"), nil
		}
	}

	var tmp SPlayer
	tmp.ClearPlayStatus()
	tmp.Player = player
	tmp.playerDetail.Status = globals.MUSIC_ST_IDLE
	tmp.playerDetail.CurPos = 0
	tmp.playerDetail.Duration = 0
	tmp.SetInterruptStatus(false)

	if playertype == globals.PlayerType_Other {
		tmp.Id = playerid
		playerid++
	} else {
		tmp.Id = uint32(playertype)
	}

	if playertype == globals.PlayerType_Wifi {
		tmp.ActiveStatus = true
	}

	playermap[tmp.Id] = &tmp
	log.Println(log.DebugLog, "Has registed player,", tmp.Id, tmp.playerDetail.Status)

	if playertype == globals.PlayerType_Wifi {
		curRunningPlayer = &tmp
		curRunningPlayer.SetActiveStatus(true)
	}

	return nil, &tmp
}

func UnRegistePlayer(id uint32) error {
	lk.Lock()
	defer lk.Unlock()
	if id < uint32(globals.PlayerType_Other) {
		log.Println(log.DebugLog, "Cur player not allow delete!", id)
		return errors.New("Cur player not allow delete!")
	}

	delete(playermap, id)
	return nil
}

func SetPlayerPrepareNext(playerid uint32) {
	if player, ok := playermap[playerid]; ok == true && player != nil && player.Player.PrepareNext != nil {
		lockCore()
		if player.IsActive() == true {
			player.Player.PrepareNext(nil)
		}
		unlockCore()
	}
}

func SetPlayerStatus(status PlayerDetail) {
	if status.Status >= globals.AUDIO_ST_IDLE && status.Status <= globals.AUDIO_ST_MAX {
		//notice
		SetPlayDetailForNotice(status)
	} else {
		if GetCurPlayerId() == globals.PlayerType(status.PlayerId) && ((GetCurPlayerId() >= globals.PlayerType_Wifi &&
			GetCurPlayerId() <= globals.PlayerType_Other) || scenePlayerHd.GetPlayerId() == status.PlayerId) {
			globals.CurPlayerSt.Duration = status.Duration
			globals.CurPlayerSt.CurPlayingPos = status.CurPos
			globals.CurPlayerSt.PlayerId = globals.PlayerType(status.PlayerId)
			globals.CurPlayerSt.TaskField = globals.FIELD_MUSIC
			globals.CurPlayerSt.PageSize = globals.PAGE_SIZE
			globals.CurPlayerSt.PlayMode = globals.CurPlayingMode

			if GetCurPlayerId() == globals.PlayerType_Wifi {
				globals.CurPlayerSt.MusicListId = globals.PlayListSrcRec.MusicListId
				globals.CurPlayerSt.OfTag = globals.PlayListSrcRec.TagName
				globals.CurPlayerSt.CloudId = &globals.PlayListSrcRec.CloudId

				var curSongInfo *globals.SongInfo
				curSongInfo, _, _ = globals.LocalMusicList[globals.PLAYSONG_MUSIC_LIST].Search(&status.Uuid, false)
				if nil == curSongInfo {
					if eVal, err := globals.GetCurWifiSongInfo(); nil == err {
						curSongInfo = eVal
					} else {
						log.Println(log.DebugLog, err.Error())
					}
				}

				if nil != curSongInfo {
					if curSongInfo.Single {
						globals.CurPlayerSt.Type = globals.SINGLE_SONG
					} else if globals.TOP3_RCMD_SONG == curSongInfo.SongType {
						globals.CurPlayerSt.Type = curSongInfo.MusicSrc
						// 覆盖 listid tagname tag
						globals.CurPlayerSt.MusicListId = &curSongInfo.MusicListId
						globals.CurPlayerSt.OfTag = &curSongInfo.TagName
					} else {
						globals.CurPlayerSt.Type = globals.PlayListSrcRec.MusicSrc
					}

					globals.CurPlayerSt.ExtraData = globals.PlayListSrcRec.ExtraData
					globals.CurPlayerSt.BizType = globals.PlayListSrcRec.BizType
					globals.CurPlayerSt.Order = globals.PlayListSrcRec.Order

					globals.CurPlayerSt.Tag = uint8(curSongInfo.SongType)
					globals.CurPlayerSt.MusicId = &curSongInfo.Id
					globals.CurPlayerSt.ArtistId = &curSongInfo.ArtistId
					globals.CurPlayerSt.ArtistName = &curSongInfo.ArtistName
					globals.CurPlayerSt.AlbumId = &curSongInfo.AlbumId
					globals.CurPlayerSt.AlbumName = &curSongInfo.AlbumName
				} else {
					if nil == globals.LocalMusicList[globals.CUR_MUSIC_LIST].List ||
						0 == globals.LocalMusicList[globals.CUR_MUSIC_LIST].List.Len() {
						// 如果是wifi，但是当前播放列表空，不广播播放状态
						goto _setPlayerStatusToFramework
					}

					globals.CurPlayerSt.MusicId = &status.Uuid
					globals.CurPlayerSt.Type = globals.PlayListSrcRec.MusicSrc
				}

				if nil != globals.NormalListMap[globals.NORMAL_LIST_PAGE].CurPos {
					globals.CurPlayerSt.Page = globals.NormalListMap[globals.NORMAL_LIST_PAGE].CurPos.Value.(*globals.PageNode).PageIndex
				}

				epRecordEvent(curSongInfo, status)
			} else if scenePlayerHd.GetPlayerId() == status.PlayerId {
				globals.CurPlayerSt.MusicListId = globals.ScenesPlayerListSrc.MusicListId
				globals.CurPlayerSt.OfTag = globals.ScenesPlayerListSrc.TagName
				globals.CurPlayerSt.CloudId = &globals.ScenesPlayerListSrc.CloudId

				var curSongInfo *globals.SongInfo
				curSongInfo, _, _ = globals.LocalMusicList[globals.PLAYSONG_MUSIC_LIST].Search(&status.Uuid, false)
				if nil == curSongInfo {
					if eVal, err := globals.GetScenesSongInfo(); nil == err {
						curSongInfo = eVal
					} else {
						log.Println(log.DebugLog, err.Error())
					}
				}

				if nil != curSongInfo {
					if curSongInfo.Single {
						globals.CurPlayerSt.Type = globals.SINGLE_SONG
					} else if globals.TOP3_RCMD_SONG == curSongInfo.SongType {
						globals.CurPlayerSt.Type = curSongInfo.MusicSrc
						// 覆盖 listid tagname tag
						globals.CurPlayerSt.MusicListId = &curSongInfo.MusicListId
						globals.CurPlayerSt.OfTag = &curSongInfo.TagName
					} else {
						globals.CurPlayerSt.Type = globals.ScenesPlayerListSrc.MusicSrc
					}

					globals.CurPlayerSt.Tag = uint8(curSongInfo.SongType)
					globals.CurPlayerSt.MusicId = &curSongInfo.Id
					globals.CurPlayerSt.ArtistId = &curSongInfo.ArtistId
					globals.CurPlayerSt.ArtistName = &curSongInfo.ArtistName
					globals.CurPlayerSt.AlbumId = &curSongInfo.AlbumId
					globals.CurPlayerSt.AlbumName = &curSongInfo.AlbumName
				} else {
					globals.CurPlayerSt.MusicId = &status.Uuid
					globals.CurPlayerSt.Type = globals.ScenesPlayerListSrc.MusicSrc
				}

				if nil != globals.NormalListMap[globals.SCENES_LIST_PAGE].CurPos {
					globals.CurPlayerSt.Page = globals.NormalListMap[globals.SCENES_LIST_PAGE].CurPos.Value.(*globals.PageNode).PageIndex
				}

				globals.CurCurrentStatus = globals.SCENE_FIELD_ST

				epRecordEvent(curSongInfo, status)
			} else {
				globals.CurPlayerSt.Page = 0
				globals.CurPlayerSt.Type = 0
				globals.CurPlayerSt.MusicId = &status.Uuid
				globals.CurPlayerSt.CloudId = nil
			}

			if needBroadcast(status) {

				globals.CurPlayerSt.ContinuousTaskSt = status.Status

				if nextSong(status) {
					if nil != globals.MusicActionNew {
						globals.CurPlayerSt.Action = *globals.MusicActionNew
						globals.MusicActionNew = nil
					} else {
						globals.CurPlayerSt.Action = ""
					}
					if nil != globals.MusicActionSrcNew {
						globals.CurPlayerSt.ActionSrc = *globals.MusicActionSrcNew
						globals.MusicActionSrcNew = nil
					} else {
						globals.CurPlayerSt.ActionSrc = ""
					}
				} else {
					/* action 开始播放的时候广播，后续清空 */
					globals.CurPlayerSt.Action = ""
					globals.CurPlayerSt.ActionSrc = ""
				}

				var st globals.VboxBroadcastSt = globals.VboxBroadcastSt{
					//PlayerSt:  globals.CurPlayerSt,
					//VboxVolSt: globals.CurVboxVolSt,
					//ScenesSt:  globals.CurScenesSt,
					ModeId:        globals.PlayListSrcRec.ModeId,
					PlayerStPtr:   &globals.CurPlayerSt,
					VboxVolStPtr:  &globals.CurVboxVolSt,
					ScenesStPtr:   GetSceneStatus(),
					CurrentStatus: GetMusicField(),
				}
				if st.CurrentStatus == globals.SCENE_FIELD_ST && len(GetCurScenePlayerDetail().Uuid) == 0 {
					st.PlayerStPtr = nil
				}
				st.VboxVolStPtr.Action = globals.VOL_NULL_ACTION
				globals.TrigBroadcastStatus(st)
			}
		}

	_setPlayerStatusToFramework:
		if player, ok := playermap[status.PlayerId]; ok == true {
			if checkWifiStatus(status) {
				if nil != globals.CurPlayerSt.MusicId && len(*globals.CurPlayerSt.MusicId) > 0 {
					log.Println(log.WarningLog, "status info err, but reverse, oldId:", status.Uuid, " newId:", *globals.CurPlayerSt.MusicId)
					status.Uuid = *globals.CurPlayerSt.MusicId
				} else {
					log.Printf(log.ErrorLog, "status info err,and can not reverse\n")
				}
			}
			player.playerDetail = status

			if GetCurPlayerId() == globals.PlayerType(status.PlayerId) {
				if player.Player.PlayerStatusChange != nil {
					player.Player.PlayerStatusChange(status)
				}
			}

			//not cur player
			if globals.MUSIC_ST_PAUSED != status.Status && globals.MUSIC_ST_IDLE != status.Status {
				if (GetCurPlayerId() != globals.PlayerType(status.PlayerId) || player.InterruptStatus == true) && player.IsPlaying() {
					//if not cur player should not play, framework ensure only one player is playing
					log.Printf(log.DebugLog, "Here maybe some issue, Player(%d) not the curPlayer(%d) or player been interruppted (%+v) but in playing!#PV1-521\n", player.GetPlayerId(), GetCurPlayerId(), player.InterruptStatus)
					player.Pause(false, "Framework excepttion control")
					log.Printf(log.DebugLog, ">>>>>>>> playerId:%d st:%d\n", status.PlayerId, status.Status)
				}
			}
		} else {
			log.Println(log.ErrorLog, "Set player status, but not find the id:", status.PlayerId)
		}
	}
}

func GetCurPlayerId() globals.PlayerType {
	return globals.PlayerType(curRunningPlayer.GetPlayerId())
}

func GetCurPlayer() *SPlayer {
	return curRunningPlayer
}

func GetScenePlayerId() globals.PlayerType {
	return globals.PlayerType(scenePlayerHd.GetPlayerId())
}

func IsCurPlayerActive() bool {
	if curRunningPlayer == nil {
		return false
	} else {
		return (curRunningPlayer.IsActive() || !curRunningPlayer.InterruptStatus)
	}

	return false
}

func IsCurPlayerInterrupt() bool {
	if curRunningPlayer == nil {
		return true
	} else {
		return curRunningPlayer.InterruptStatus
	}
}

func IsCurPlayerPlaying() bool {
	if curRunningPlayer == nil {
		return false
	} else {
		return curRunningPlayer.IsPlaying()
	}
}

func needBroadcast(status PlayerDetail) bool {
	if globals.MUSIC_ST_PREPARING == status.Status {
		log.Println(log.ErrorLog, "player.playerDetail.Status:", globals.CurPlayerSt.ContinuousTaskSt)

		if isPlaying(globals.CurPlayerSt.ContinuousTaskSt) {
			return false
		}
	}

	switch status.Status {
	case globals.MUSIC_ST_PREPARED,
		globals.MUSIC_ST_FOR_NEXT:
		return false
	default:
		return true
	}
}

func epRecordEvent(songInfo *globals.SongInfo, statusCur PlayerDetail) {
	if nil != songInfo {
		var src string
		if globals.TOP3_RCMD_SONG == songInfo.SongType {
			src = eventPoint.SrcValRcmd
		} else {
			src = eventPoint.SrcValVoice
		}
		var srcOs eventPoint.EpOs
		switch eventPoint.EpOs(songInfo.SourceOs) {
		case eventPoint.EpOs_App, eventPoint.EpOs_Device:
			srcOs = eventPoint.EpOs(songInfo.SourceOs)
		default:
			srcOs = eventPoint.EpOs_Server
		}

		if globals.MUSIC_ST_PLAYING == statusCur.Status && statusCur.Duration > 0 {
			if statusCur.Uuid != statusRec.Uuid || statusCur.CurPos < statusRec.CurPos {
				if globals.MUSIC_ST_PLAYING == statusRec.Status && "" != statusRec.Uuid {
					oldSonfInfo, _, _ := globals.LocalMusicList[globals.PLAYSONG_MUSIC_LIST].Search(&statusRec.Uuid, false)
					if nil != oldSonfInfo {
						if globals.TOP3_RCMD_SONG == oldSonfInfo.SongType {
							src = eventPoint.SrcValRcmd
						} else {
							src = eventPoint.SrcValVoice
						}
						switch eventPoint.EpOs(oldSonfInfo.SourceOs) {
						case eventPoint.EpOs_App, eventPoint.EpOs_Device:
							srcOs = eventPoint.EpOs(oldSonfInfo.SourceOs)
						default:
							srcOs = eventPoint.EpOs_Server
						}

						if statusRec.Duration-statusRec.CurPos < globals.MUSIC_PLAY_END_THRESHOLD {
							// playend
							eventPoint.MusicEndEvent(statusRec.Uuid, src, eventPoint.EndValPlayOver, srcOs)
						} else {
							// interrupt
							eventPoint.MusicEndEvent(statusRec.Uuid, src, eventPoint.EndValInterrupt, srcOs)
						}
					}
				}

				if nil != globals.RspId {
					eventPoint.MusicStartEvent(statusCur.Uuid, src, *globals.RspId, eventPoint.EventPlay, srcOs)
					globals.RspId = nil
				} else {
					eventPoint.MusicStartEvent(statusCur.Uuid, src, "", eventPoint.EventPlay, srcOs)
				}

			} else if !isPlaying(globals.CurPlayerSt.ContinuousTaskSt) && globals.MUSIC_ST_PLAYING == statusCur.Status {
				if nil != globals.RspId {
					eventPoint.MusicPauseToPlayEvent(statusCur.Uuid, src, *globals.RspId, eventPoint.EventPlay, srcOs)
					globals.RspId = nil
				} else {
					eventPoint.MusicPauseToPlayEvent(statusCur.Uuid, src, "", eventPoint.EventPlay, srcOs)
				}
			}
			//log.Printf(log.ErrorLog, "rec:%+v cur:%+v\n", statusRec, statusCur)
			statusRec = statusCur
		} else if globals.MUSIC_ST_PREPARING == statusCur.Status {

			if nil != globals.RspId {
				eventPoint.MusicStartEvent(statusCur.Uuid, src, *globals.RspId, eventPoint.EventPrepare, srcOs)
			} else {
				eventPoint.MusicStartEvent(statusCur.Uuid, src, "", eventPoint.EventPrepare, srcOs)
			}
		}
	}
}

func UrlSrc(src string) bool {
	return strings.Contains(src, "http://") || strings.Contains(src, "https://") ||
		strings.Contains(src, "ftp://")
}

func checkWifiStatus(status PlayerDetail) bool {
	if uint32(globals.PlayerType_Wifi) == status.PlayerId {
		if status.Uuid == globals.AirPlayUuid || status.Uuid == globals.BtUuid || UrlSrc(status.Uuid) {
			return true
		}
		return false
	}
	return false
}

func nextSong(status PlayerDetail) bool {
	if nil == globals.CurPlayerSt.MusicId ||
		*globals.CurPlayerSt.MusicId != status.Uuid ||
		1 == status.CurPos /* 表示开始播放时候, 没有获取到pos,置为1 */ {

		return true
	} else {
		return false
	}
}

func isPlaying(st globals.PlayerStatuses) bool {
	switch st {
	case globals.MUSIC_ST_PREPARING, globals.MUSIC_ST_PLAYING, globals.MUSIC_ST_PREPARED,
		globals.MUSIC_ST_SEEKING, globals.MUSIC_ST_FOR_NEXT,
		globals.MUSIC_ST_COMPLETED:
		return true
	default:
		return false
	}
}

//音箱是否在出声，兼顾curPlayer、notice和se
func SpeakerIsPlaying() bool {
	if isPlaying(globals.CurPlayerSt.ContinuousTaskSt) ||
		SeIsPlaying() == true ||
		NoticeIsPlaying() == true ||
		globals.BtAvkDataRecving == true {
		return true
	} else {
		return false
	}
}

func TryMicAdcSet() {
	misc.VolTrigeMicAdc()
}

func TryMicAdcIdle() {
	if !SpeakerIsPlaying() {
		misc.IdleMicAdc()
	}
}
