package jukebox

import (
	"errors"
	"io/ioutil"
	"os"
	"sync"
	"time"

	gg "github.com/faiface/pixel-examples/community/amidakuji/glossary"

	"github.com/faiface/beep"
	"github.com/faiface/beep/speaker"
	"github.com/faiface/beep/vorbis"
)

// -------------------------------------------------------------------------

const nMusics = 2

var (
	mutex     sync.Mutex
	isPlaying bool
	musics    [nMusics]*_Music
)

// -------------------------------------------------------------------------

// singleton
func init() {
	// city pop favorites
	musics[0] = _NewMusicFromAsset("nighttempo-purepresent1", "karaoke/kikuchimomoko-nightcruising.ogg")
	musics[1] = _NewMusicFromAsset("nighttempo-purepresent2", "karaoke/takeuchimariya-plasticlove.ogg")

	// speaker on
	speaker.Init(musics[0].format.SampleRate, musics[0].format.SampleRate.N(time.Second))
	speaker.Play(beep.Iterate(func() (soundtrack beep.Streamer) {
		musics[0].stream.Seek(0)
		musics[1].stream.Seek(0)
		return beep.Seq(musics[0].stream, musics[1].stream)
	}))
	speaker.Lock()
}

// IsPlaying determines whether the soundtrack is currently playing or not.
func IsPlaying() bool {
	return isPlaying
}

// Play unlocks the speaker.
func Play() {
	mutex.Lock()
	defer mutex.Unlock()
	if !isPlaying {
		isPlaying = true
		speaker.Unlock()
	}
	return
}

// Pause locks the speaker.
func Pause() {
	mutex.Lock()
	defer mutex.Unlock()
	if isPlaying {
		isPlaying = false
		speaker.Lock()
	}
	return
}

// Finalize should be called on program exit.
// This function deletes the temporary music file its package generates.
func Finalize() error {
	errs := ""
	for _, music := range musics {
		music.Close()
		err := music._Destory()
		if err != nil {
			errs += " " + err.Error()
		}
	}
	if errs != "" {
		return errors.New(errs)
	}
	return nil
}

// -------------------------------------------------------------------------

// NewMusicFromAsset is a constructor.
func _NewMusicFromAsset(nameMusic, nameAsset string) *_Music {
	asset, err := gg.Asset(nameAsset)
	if err != nil {
		// log.Fatal(err) //
	}
	return _NewMusic(nameMusic, asset)
}

// Music is a temporary file to play a single background music. It should be destroyed on program exit.
type _Music struct {
	os.File
	stream beep.StreamSeekCloser
	format beep.Format
}

// NewMusic creates an instance of Music, a temporary file from which the speaker plays a music.
// speaker.Lock() to pause.
// speaker.Unlock() to resume/play.
func _NewMusic(name string, asset []byte) *_Music {
	tmpfile, err := ioutil.TempFile("", name)
	if err != nil {
		// log.Fatal(err) //
	}
	// log.Println(tmpfile.Name()) //
	_, err = tmpfile.Write(asset)
	if err != nil {
		// log.Fatal(err) //
	}
	stream, format, err := vorbis.Decode(tmpfile)
	if err != nil {
		// log.Fatal(err) //
	}
	return &_Music{*tmpfile, stream, format}
}

// Destory deletes the temporary music file.
func (music *_Music) _Destory() error {
	return os.Remove(music.Name())
}
