package schedule

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"netease_control_center/globals"
	"netease_control_center/interfaces/log"
	"netease_control_center/modules/hardware/misc"
	"sync"
	"time"
)

type NoticeStopReason uint8

func (this NoticeStopReason) IsStart() bool {
	if this == NoticeStart_Play {
		return true
	} else {
		return false
	}
}

func (this NoticeStopReason) IsStop() bool {
	return !this.IsStart()
}

const (
	NoticeStopReason_Interrupt NoticeStopReason = iota + 1
	NoticeStopReason_Cancel
	NoticeStopReason_Complete
	NoticeStopReason_Error
	NoticeStart_Play

	NoticeStopReason_Max
)

var NoticeStopReasons = [...]string{
	NoticeStopReason_Interrupt: "Interrupt",
	NoticeStopReason_Cancel:    "Cancel",
	NoticeStopReason_Complete:  "Complete",
	NoticeStopReason_Error:     "Error",
}

func GetNoticeStopErr(reason uint8) (err string) {
	defer func() {
		if err := recover(); err != nil {
			err = fmt.Sprintln("Unknow err:", reason)
		}
	}()
	err = fmt.Sprintln("Code:", reason, " Desc:", NoticeStopReasons[reason])
	return
}

type StartPlayNotice func(id uint32, srcType globals.AdSrcType, src string, uuid string)
type StopPlayNotice func(id uint32)
type PlayNoticeCallBack func(id uint32, reason NoticeStopReason)

const audioPlayIdStart = uint32(globals.PlayerType_Other) //id = curplayid ++, 即从PlayerType_Other + 1 开始
var (
	curplayid  uint32 = audioPlayIdStart
	noticeChan chan uint8

	noticePlayerLock sync.Mutex

	startPlayNotice StartPlayNotice
	stopPlayNotice  StopPlayNotice
	noticePlaying   bool = false
)

func RegisteNoticePlayer(start StartPlayNotice, stop StopPlayNotice) {
	startPlayNotice = start
	stopPlayNotice = stop
}

func SafeCloseChan(ch chan uint8) (reterr error) {
	defer func() {
		if err := recover(); err != nil {
			buf := new(bytes.Buffer)
			fmt.Fprintln(buf, err)
			reterr = errors.New(buf.String())
		}
	}()
	close(ch)
	return nil
}

func TriggerNoticePlayerComplete(channum uint8) (reterr error) {
	defer func() {
		if err := recover(); err != nil {
			buf := new(bytes.Buffer)
			fmt.Fprintln(buf, err)
			reterr = errors.New(buf.String())
		}
	}()

	select {
	case noticeChan <- channum:
	default:
		return errors.New("chan can not write!")
	}

	return nil
}

func SetPlayDetailForNotice(detail PlayerDetail) {
	noticePlayerLock.Lock()
	defer noticePlayerLock.Unlock()

	if detail.PlayerId == curplayid {
		switch detail.Status {
		case globals.AUDIO_ST_FINISH:
			log.Println(log.DebugLog, "Return playerId:", detail.PlayerId, " status: Complete")
			noticePlaying = false
			TryMicAdcIdle()
			TriggerNoticePlayerComplete(uint8(NoticeStopReason_Complete))
		case globals.AUDIO_ST_INTERRUPT:
			log.Println(log.DebugLog, "Return playerId:", detail.PlayerId, " status: interrupt")
			noticePlaying = false
			TryMicAdcIdle()
			TriggerNoticePlayerComplete(uint8(NoticeStopReason_Interrupt))
		case globals.AUDIO_ST_ERR:
			log.Println(log.DebugLog, "Return playerId:", detail.PlayerId, " status: error")
			noticePlaying = false
			TryMicAdcIdle()
			TriggerNoticePlayerComplete(uint8(NoticeStopReason_Error))
		case globals.AUDIO_ST_START:
			log.Println(log.DebugLog, "Return playerId:", detail.PlayerId, " status: begin")
			//notice在播放
			noticePlaying = true
			TryMicAdcSet()
			TriggerNoticePlayerComplete(uint8(NoticeStart_Play))
		}
	}
}

func PlayNoticeAsync(srcType globals.AdSrcType, src string, uuid string, needRepeat bool, ctx context.Context, cb PlayNoticeCallBack) (id uint32) {
	noticePlayerLock.Lock()
	defer noticePlayerLock.Unlock()

	SafeCloseChan(noticeChan)

	curplayid++
	if curplayid == 0 {
		curplayid = audioPlayIdStart + 1
	}
	if len(src) == 0 {
		if cb != nil {
			go cb(curplayid, NoticeStopReason_Complete)
		}
		log.Println(log.ErrorLog, "Send tts notice of content is null!")

		return curplayid
	}

	// set after end
	//if needRepeat {
	//	ttsInfo := GetNoticePlayerParams(src, uuid, srcType, globals.TTS_LEVEL_1, needRepeat, false)
	//	SetTtsRepeatInfo(ttsInfo)
	//}

	if cb != nil || needRepeat {
		noticeChan = make(chan uint8, 1)
		go func() {
			tmpplayerid := curplayid
			tmpnoticeChan := noticeChan
		waitEnd:
			if ctx != nil {
				select {
				case <-ctx.Done():
					log.Println(log.DebugLog, "tts playerId:", tmpplayerid, ", TTS end because of ctx done! src:", src)
					if stopPlayNotice != nil && tmpplayerid == curplayid {
						stopPlayNotice(tmpplayerid)
					}

					if nil != cb {
						cb(tmpplayerid, NoticeStopReason_Cancel)
					}

				case no := <-tmpnoticeChan:
					if no == uint8(NoticeStart_Play) {
						if needRepeat {
							ttsInfo := GetNoticePlayerParams(src, uuid, srcType, globals.TTS_LEVEL_1, needRepeat, false)
							SetTtsRepeatInfo(ttsInfo)
						}

						if nil != cb {
							cb(tmpplayerid, NoticeStopReason(no))
						}
						log.Println(log.DebugLog, "tts playerId = ", tmpplayerid, ", TTS begin:", src)
						goto waitEnd
					}
					log.Println(log.DebugLog, "tts playerId = ", tmpplayerid, ", TTS end because of: ", GetNoticeStopErr(no), src)

					if nil != cb {
						if no < uint8(NoticeStopReason_Max) && no >= uint8(NoticeStopReason_Interrupt) {
							cb(tmpplayerid, NoticeStopReason(no))
						} else {
							cb(tmpplayerid, NoticeStopReason_Interrupt)
						}
					}
				}
			} else {
				select {
				case no := <-tmpnoticeChan:
					if no == uint8(NoticeStart_Play) {
						if needRepeat {
							ttsInfo := GetNoticePlayerParams(src, uuid, srcType, globals.TTS_LEVEL_1, needRepeat, false)
							SetTtsRepeatInfo(ttsInfo)
						}

						if nil != cb {
							cb(tmpplayerid, NoticeStopReason(no))
						}
						log.Println(log.DebugLog, "tts playerId = ", tmpplayerid, ", TTS begin:", src)

						goto waitEnd
					}

					log.Println(log.DebugLog, "tts playerId = ", tmpplayerid, ", TTS end because of: ", GetNoticeStopErr(no), src)

					if nil != cb {
						if no < uint8(NoticeStopReason_Max) && no >= uint8(NoticeStopReason_Interrupt) {
							cb(tmpplayerid, NoticeStopReason(no))
						} else {
							cb(tmpplayerid, NoticeStopReason_Interrupt)
						}
					}
				}
			}
			log.Println(log.DebugLog, "Tts schedule player(playerId = ", tmpplayerid, ") stop play:", src)
		}()
	}

	log.Println(log.DebugLog, "Tts schedule player(playerId = ", curplayid, ") play:", src)
	if startPlayNotice != nil {
		if srcType == globals.AdSrcType_TTS {
			globals.SetEq(globals.ADAU1761_EQ_TTS)
		} else {
			globals.SetEq(globals.ADAU1761_EQ_NORMAL)
		}

		startPlayNotice(curplayid, srcType, src, uuid)
		misc.VolTrigeMicAdc()
	}

	return curplayid
}

func PlayNoticeSync(srcType globals.AdSrcType, src string, uuid string, needRepeat bool, ctx context.Context) {
	ch := make(chan uint8, 1)
	cb := func(id uint32, reason NoticeStopReason) {
		//  todo: 子蛟,这里等待播放结束再退出,对你的其他逻辑是否有影响
		switch reason {
		case NoticeStopReason_Complete,
			NoticeStopReason_Interrupt,
			NoticeStopReason_Cancel,
			NoticeStopReason_Error:
			ch <- 1
		}
	}

	PlayNoticeAsync(srcType, src, uuid, needRepeat, ctx, cb)
	if ctx != nil {
		select {
		case <-ctx.Done():
		case <-ch:
		}
	} else {
		select {
		case <-ch:
		}
	}
}

func PlayNoticeSyncWithCb(srcType globals.AdSrcType, src string, uuid string, needRepeat bool, ctx context.Context, callback func(id uint32, reason NoticeStopReason)) {
	ch := make(chan uint8, 1)
	cb := func(id uint32, reason NoticeStopReason) {
		//  todo: 子蛟,这里等待播放结束再退出,对你的其他逻辑是否有影响
		if callback != nil {
			callback(id, reason)
		}
		switch reason {
		case NoticeStopReason_Complete,
			NoticeStopReason_Interrupt,
			NoticeStopReason_Cancel,
			NoticeStopReason_Error:
			ch <- 1
		}
	}

	PlayNoticeAsync(srcType, src, uuid, needRepeat, ctx, cb)
	if ctx != nil {
		select {
		case <-ctx.Done():
		case <-ch:
		}
	} else {
		select {
		case <-ch:
		}
	}
}

func PlayNoticeSyncWithTimeout(srcType globals.AdSrcType, src string, uuid string, needRepeat bool, ctx context.Context, timeout time.Duration) {
	var newctx context.Context
	if ctx != nil {
		newctx, _ = context.WithTimeout(ctx, timeout)
	} else {
		newctx, _ = context.WithTimeout(context.Background(), timeout)
	}

	PlayNoticeSync(srcType, src, uuid, needRepeat, newctx)
}

func PlayNoticeSyncTimeout(srcType globals.AdSrcType, src string, uuid string, needRepeat bool, ctx context.Context, timeout_sec uint32) {
	ch := make(chan uint8, 1)
	cb := func(id uint32, reason NoticeStopReason) {
		ch <- 1
	}

	PlayNoticeAsync(srcType, src, uuid, needRepeat, ctx, cb)
	if ctx != nil {
		select {
		case <-ctx.Done():
		case <-ch:
		case <-time.After(time.Duration(timeout_sec) * time.Second):
		}
	} else {
		select {
		case <-ch:
		case <-time.After(time.Duration(timeout_sec) * time.Second):
		}
	}
}

func NoticeIsPlaying() bool {
	return noticePlaying
}
