package schedule

import (
	"fmt"
	. "netease_control_center/globals"
	"netease_control_center/interfaces/eventPoint"
	"netease_control_center/interfaces/log"
	"netease_control_center/interfaces/vui"
	"netease_control_center/modules/hardware/misc"
	"time"
)

const (
	REQUEST_PLAY_PARAMS = -1
)

type PlayerArg struct {
	GloablePause bool
	NotShowIcon  bool
	TtsNextPre   *string

	ServerPlayerBase
}

type PlayerCategory uint8

const (
	PlayerCategory_CommonMusic PlayerCategory = iota
	PlayerCategory_Scene
)

type PlayerStartType uint8

const (
	PlayerStartType_New          PlayerStartType = iota
	PlayerStartType_Continue
	PlayerStartType_PlayerChange
)

type RegistedPlayer struct {
	Pause    func()
	Play     func(start int, arg SCmd_Args) (isSuccess bool)
	Stop     func()
	Seek     func(pos uint32)
	IsOnline func() bool
	//if higher task running, if force pause
	IfForcePause func() bool

	PlayNext func(arg interface{})
	PlayPre  func(arg interface{})

	PrepareNext func(arg interface{})

	GetSupportedSCmd func() []SCMD

	SetArg func(arg ...interface{}) bool

	GetPlayerName func() string

	PlayerStatusChange func(status PlayerDetail)

	SetVolume func(vol uint16)

	Debug func() string

	PlayerChangeCb func(isJumpIn bool)

	//Just for scene player, Right Now!
	InterruptPlayer func()
	ResumePlayer    func()
	PlayOrPause     func() (isPaused bool)
	EnterScene      func(canExec bool, sceneidOrstruct interface{}) (isSuccess bool)

	GetPlayerCategory func() PlayerCategory
	GetScheduleDomain ScheduleDomainGetFunc
}

type PlayerDetail struct {
	PlayerId uint32         `json:"playerId"`
	Uuid     string         `json:"musicUuid"`
	Status   PlayerStatuses `json:"plySt"`
	CurPos   uint32         `json:"curPos"`
	Duration uint32         `json:"duration"`
}

type PlayerDetailType uint8

const (
	PlayerDetailType_Notice PlayerDetailType = iota
	PlayerDetailType_Music
)

type PlayerPauseReason uint8

const (
	PlayerPauseReason_VoiceWakeUP PlayerPauseReason = iota
	PlayerPauseReason_ByUser
	PlayerPauseReason_Other
)

type PlayerHd interface {
	GetPlayerId() uint32
	IsActive() bool
	GetPlayerStatus() PlayerDetail
	SetPlayerStatus(status PlayerDetail)
	SetSPlayerPlayStatus(detail PlayerDetail, whyChange string)
	RequestPlay_Async(arg interface{})
	RequestPlay_Sync(arg interface{}) (isCanPlay bool)
	RequestPlay_Async_WithCb(arg interface{}, cb func(SCmdStructExecCb))
	ClearPlayStatus()
	IsPlayerChange(ClearStatus bool) bool
	GetLastPasusedTime() time.Time
	ReportOffline()
	IsPlayerWillPlay() bool
	IsPlaying() bool
	IsInterrupt() bool
	SetPauseReason(reason PlayerPauseReason)

	GetGlobalPauseStatus() bool
}

type SPlayer struct {
	Id uint32

	Player         RegistedPlayer
	playerDetail   PlayerDetail
	LastPausedTime time.Time
	ActiveStatus   bool

	InterruptStatus bool
	InterruptTime   time.Time

	PlayAfterResume             bool
	HasRequestPlayWhenInterrupt bool
	isPlayerChange              bool

	pauseReason PlayerPauseReason

	OemArg SCmd_Args
}

func (this *SPlayer) GetPauseReason() PlayerPauseReason {
	return this.pauseReason
}

func (this *SPlayer) SetPauseReason(reason PlayerPauseReason) {
	this.pauseReason = reason
}

func (this *SPlayer) GetPlayerPlayStatus() PlayerDetail {
	return this.playerDetail
}

func (this *SPlayer) ChangePlayStatus(status PlayerStatuses, whyChange string) {
	this.playerDetail.Status = status
	log.Printf(log.DebugLog, "Player status be changed to:%+v, reason:%s\n", this.playerDetail, whyChange)
}

func (this *SPlayer) SetSPlayerPlayStatus(detail PlayerDetail, whyChange string) {
	this.playerDetail = detail
	log.Printf(log.DebugLog, "Player status be changed to:%+v, reason:%s\n", this.playerDetail, whyChange)
}

func (this *SPlayer) String() string {
	var ret string
	ret += fmt.Sprintf("Id:%d ", this.Id)
	ret += fmt.Sprintln(" Name:", this.Player.GetPlayerName())
	ret += fmt.Sprintln(" Online:", this.Player.IsOnline())
	ret += fmt.Sprintln(" LastInterrupt:", this.InterruptTime)
	ret += fmt.Sprintln(" LastPaused:", this.LastPausedTime)
	ret += fmt.Sprintln(" PauseReason:", this.pauseReason)
	ret += fmt.Sprintln(" InterruptStatus:", this.InterruptStatus)
	ret += fmt.Sprintln(" ActiveStatus:", this.ActiveStatus)
	ret += fmt.Sprintln(" PlayAfterResume:", this.PlayAfterResume)
	ret += fmt.Sprintln(" IsPlayerChange", this.isPlayerChange)
	ret += fmt.Sprintln(" HasRequestPlayWhenInterrupt:", this.HasRequestPlayWhenInterrupt)
	if curRunningPlayer != nil && curRunningPlayer.Player.GetPlayerName != nil {
		ret += fmt.Sprintln(" CurRunning player:", curRunningPlayer.Player.GetPlayerName())
	}
	ret += fmt.Sprintf(" PlayDetail:%+v", this.playerDetail)

	if this.Player.Debug != nil {
		ret += " Debug:" + this.Player.Debug() + "\n"
	}

	return ret
}

func (this *SPlayer) GetPlayerCategory() PlayerCategory {
	if this.Player.GetPlayerCategory == nil {
		return PlayerCategory_CommonMusic
	} else {
		return this.Player.GetPlayerCategory()
	}
}

func (this *SPlayer) IsPlaying() bool {
	switch this.playerDetail.Status {
	case MUSIC_ST_PREPARING, MUSIC_ST_PLAYING, MUSIC_ST_PREPARED, MUSIC_ST_SEEKING, MUSIC_ST_FOR_NEXT,
		MUSIC_ST_REV_LIST_OK, MUSIC_ST_COMPLETED:
		return true
	default:
		return false
	}
}

func (this *SPlayer) IsInterrupt() bool {
	return this.InterruptStatus
}
func (this *SPlayer) GetLastPasusedTime() time.Time {
	return this.LastPausedTime
}
func (this *SPlayer) UpdatePausedTime() {
	this.LastPausedTime = time.Now().Local()
}
func (this *SPlayer) UpdateInterruptTime() {
	this.InterruptTime = time.Now().Local()
}
func (this *SPlayer) GetPlayerId() uint32 {
	return this.Id
}
func (this *SPlayer) IsActive() bool {
	return this.ActiveStatus
}

func (this *SPlayer) SetInterruptStatus(st bool) {
	this.InterruptStatus = st
}

func (this *SPlayer) SetActiveStatus(active bool) {
	this.ActiveStatus = active
}

func (this *SPlayer) GetPlayerStatus() PlayerDetail {
	return this.playerDetail
}

func (this *SPlayer) SetPlayerStatus(status PlayerDetail) {
	this.SetSPlayerPlayStatus(status, "By player hd!")
}

func (this *SPlayer) PlayOrPause(showIcon bool, bywho string) (isPaused bool) {
	this.SetActiveStatus(true)
	if this.InterruptStatus == false {
		if this.IsPlaying() {
			this.Pause(showIcon, bywho)
			eventPoint.PlayPauseEvent(eventPoint.ModelValPause, curRunningPlayer.GetPlayerStatus().Uuid)
			return true
		} else {
			this.ForceContinuePlay()
			eventPoint.PlayPauseEvent(eventPoint.ModelValPlay, curRunningPlayer.GetPlayerStatus().Uuid)
			return false
		}
	} else {
		this.ForceContinuePlay()
		eventPoint.PlayPauseEvent(eventPoint.ModelValPlay, curRunningPlayer.GetPlayerStatus().Uuid)
		return false
	}
	this.SetInterruptStatus(false)
	return false
}

func (this *SPlayer) ForceContinuePlay() (isSuccess bool) {
	if this.InterruptStatus == true && this.HasRequestPlayWhenInterrupt == true {
		this.HasRequestPlayWhenInterrupt = false
		return this.StartPlay(REQUEST_PLAY_PARAMS)
	} else {
		if this.IsPlaying() == true {
			return true
		}

		difftime := time.Now().Local().Sub(this.LastPausedTime)
		log.Println(log.DebugLog, "ForceContinuePlay difftime = ", difftime, ", lastpausedTime = ", this.LastPausedTime,
			", T1 = ", GetT1Duration(), ", CurPos:", this.playerDetail.CurPos)
		switch {
		case difftime <= GetT1Duration():
			this.playerDetail.Status = MUSIC_ST_PLAYING
			return this.StartPlay(int(this.playerDetail.CurPos))
		case difftime > GetT1Duration():
			return this.StartPlay(0)
		}
	}

	return false
}

func (this *SPlayer) ContinuePlay() (isSuccess bool) {
	this.SetActiveStatus(true)
	this.SetInterruptStatus(false)
	log.Println(log.DebugLog, "isPlaying:", this.IsPlaying())
	if this.IsPlaying() == true {
		return true
	}

	difftime := time.Now().Local().Sub(this.LastPausedTime)
	log.Println(log.DebugLog, "ContinuePlay difftime = ", difftime, ", lastpausedTime = ", this.LastPausedTime,
		", T1 = ", GetT1Duration(), ", T2 = ", GetT2Duration(), ", CurPos:", this.playerDetail.CurPos)
	switch {
	case difftime <= GetT1Duration():
		this.playerDetail.Status = MUSIC_ST_PLAYING
		return this.StartPlay(int(this.playerDetail.CurPos))
	case difftime > GetT1Duration() && difftime <= GetT2Duration():
		return this.StartPlay(0)
	case difftime > GetT2Duration():
		this.Pause(true, "")
		return true
	}

	return false
}

func (this *SPlayer) Stop() {
	this.SetInterruptStatus(false)
	this.Player.Stop()
}

func (this *SPlayer) Pause(showPausedIcon bool, byWho string) {
	switch this.GetPlayerCategory() {
	case PlayerCategory_CommonMusic:
		if showPausedIcon == true {
			vui.IconLed_IconChaozuo001.Show()
		}

		if this.IsPlaying() {
			switch byWho {
			case ModuleName_VoiceWakeUp:
				this.SetPauseReason(PlayerPauseReason_VoiceWakeUP)
			case ModuleName_UserTrig:
				this.SetPauseReason(PlayerPauseReason_ByUser)
			default:
				this.SetPauseReason(PlayerPauseReason_Other)
			}

			this.UpdatePausedTime()
			this.Player.Pause()
			this.playerDetail.Status = MUSIC_ST_PAUSED
		} else {
			//maybe interrupted
			this.PlayAfterResume = false
		}

	case PlayerCategory_Scene:
		this.Player.Pause()
	}
}

func (this *SPlayer) StartPlay(start int) (isSuccess bool) {
	log.Println(log.DebugLog, this.Player.GetPlayerName(), " begins to play, pos:", start)
	isGlobalPause = false
	IconLedMusicPlayMode.ForceCallCb()
	this.playerDetail.Status = MUSIC_ST_PLAYING
	this.SetActiveStatus(true)
	this.SetInterruptStatus(false)
	this.OemArg.StartPos = start

	SetEq(ADAU1761_EQ_NORMAL)
	misc.VolTrigeMicAdc()

	isSuccess = this.Player.Play(start, this.OemArg)
	if isSuccess == false {
		this.playerDetail.Status = MUSIC_ST_PAUSED
	}
	this.OemArg.OemArg = nil
	vui.ShowIconMusic()

	return
}

func (this *SPlayer) SetPlayerChange(st bool) {
	this.isPlayerChange = st
}

func (this *SPlayer) IsPlayerChange(ClearStatus bool) bool {
	defer func() {
		if ClearStatus == true {
			this.isPlayerChange = false
		}
	}()

	return this.isPlayerChange
}

func (this *SPlayer) Seek(pos uint32) {
	this.SetInterruptStatus(false)
	this.SetActiveStatus(true)
	if this.Player.Seek != nil {
		this.Player.Seek(pos)
	}
}

func (this *SPlayer) PlayNext(arg interface{}) {
	this.SetInterruptStatus(false)
	this.SetActiveStatus(true)
	if this.Player.PlayNext != nil {
		this.Player.PlayNext(arg)
	}

}

func (this *SPlayer) PlayPre(arg interface{}) {
	this.SetInterruptStatus(false)
	this.SetActiveStatus(true)
	if this.Player.PlayPre != nil {
		this.Player.PlayPre(arg)
	}
}

func (this *SPlayer) Interrupt(byWho string) {
	switch this.GetPlayerCategory() {
	case PlayerCategory_CommonMusic:
		if this.InterruptStatus == true {
			return
		}

		this.SetInterruptStatus(true)
		this.HasRequestPlayWhenInterrupt = false
		this.UpdateInterruptTime()
		this.SetActiveStatus(false)

		log.Println(log.DebugLog, this.Player.GetPlayerName()+" is interrupted, cur playing:", this.IsPlaying(), " status:", this.playerDetail.Status)
		//todo check which player status mean in play
		if this.IsPlaying() == true {
			this.Pause(false, byWho)
			this.PlayAfterResume = true
		} else {
			this.PlayAfterResume = false
		}

	case PlayerCategory_Scene:
		this.SetActiveStatus(false)
		this.Player.InterruptPlayer()

		vui.IconLed_Clear.Show()
	}
}

func (this *SPlayer) Resume() (isSuccess bool) {
	switch this.GetPlayerCategory() {
	case PlayerCategory_CommonMusic:
		if this.InterruptStatus == false {
			return false
		}
		this.SetInterruptStatus(false)
		this.ActiveStatus = true

		if this.PlayAfterResume == true {
			log.Println(log.DebugLog, "resume player:", this.GetPlayerId())
			if this.HasRequestPlayWhenInterrupt == true {
				this.HasRequestPlayWhenInterrupt = false
				return this.StartPlay(REQUEST_PLAY_PARAMS)
			} else {
				return this.ContinuePlay()
			}
		}
		return true

	case PlayerCategory_Scene:
		this.SetActiveStatus(true)
		this.Player.ResumePlayer()
		return true
	}

	return false
}

func (this *SPlayer) SetArg(arg ...interface{}) bool {
	if this.Player.SetArg != nil {
		return this.Player.SetArg(arg...)
	}
	return true
}

func (this *SPlayer) SetVolume(vol uint16) {
	if this.Player.SetVolume != nil {
		this.Player.SetVolume(vol)
	}
}

func (this *SPlayer) ClearPlayStatus() {
	this.playerDetail = PlayerDetail{Status: MUSIC_ST_PAUSED}
}

func (this *SPlayer) RequestPlay_Async(arg interface{}) {
	SendSCmd(SCMD_MusicPlayer_RequestPlay, this.GetPlayerId(), arg)
}

func (this *SPlayer) RequestPlay_Sync(arg interface{}) (isCanPlay bool) {
	return playerRequestPlay(uint32(this.GetPlayerId()), arg)
}

func (this *SPlayer) RequestPlay_Async_WithCb(arg interface{}, cb func(SCmdStructExecCb)) {
	SendSCmdStruct(SCmdStruct{SCmd: SCMD_MusicPlayer_RequestPlay, ExecCb: cb, Arg: []interface{}{this.GetPlayerId(), arg}})
}

func (this *SPlayer) ReportOffline() {
	SendSCmd(SCMD_MusicPlayer_Offline, this.GetPlayerId())
}

func (this *SPlayer) IsPlayerWillPlay() bool {
	if this.PlayAfterResume || this.IsPlaying() {
		return true
	}
	return false
}

func (this *SPlayer) GetGlobalPauseStatus() bool {
	return isGlobalPause
}
