package tracker

type (
	Bool struct {
		BoolData
	}

	BoolData interface {
		Value() bool
		Enabled() bool
		setValue(bool)
	}

	Panic           Model
	IsRecording     Model
	Playing         Model
	InstrEnlarged   Model
	Effect          Model
	TrackMidiIn     Model
	CommentExpanded Model
	Follow          Model
	UnitSearching   Model
	UnitDisabled    Model
	LoopToggle      Model
	UniquePatterns  Model
	Mute            Model
	Solo            Model
	LinkInstrTrack  Model
)

func (v Bool) Toggle() {
	v.Set(!v.Value())
}

func (v Bool) Set(value bool) {
	if v.Enabled() && v.Value() != value {
		v.setValue(value)
	}
}

// Model methods

func (m *Model) Panic() *Panic                     { return (*Panic)(m) }
func (m *Model) IsRecording() *IsRecording         { return (*IsRecording)(m) }
func (m *Model) Playing() *Playing                 { return (*Playing)(m) }
func (m *Model) InstrEnlarged() *InstrEnlarged     { return (*InstrEnlarged)(m) }
func (m *Model) Effect() *Effect                   { return (*Effect)(m) }
func (m *Model) TrackMidiIn() *TrackMidiIn         { return (*TrackMidiIn)(m) }
func (m *Model) CommentExpanded() *CommentExpanded { return (*CommentExpanded)(m) }
func (m *Model) Follow() *Follow                   { return (*Follow)(m) }
func (m *Model) UnitSearching() *UnitSearching     { return (*UnitSearching)(m) }
func (m *Model) UnitDisabled() *UnitDisabled       { return (*UnitDisabled)(m) }
func (m *Model) LoopToggle() *LoopToggle           { return (*LoopToggle)(m) }
func (m *Model) UniquePatterns() *UniquePatterns   { return (*UniquePatterns)(m) }
func (m *Model) Mute() *Mute                       { return (*Mute)(m) }
func (m *Model) Solo() *Solo                       { return (*Solo)(m) }
func (m *Model) LinkInstrTrack() *LinkInstrTrack   { return (*LinkInstrTrack)(m) }

// Panic methods

func (m *Panic) Bool() Bool  { return Bool{m} }
func (m *Panic) Value() bool { return m.panic }
func (m *Panic) setValue(val bool) {
	(*Model)(m).setPanic(val)
}
func (m *Panic) Enabled() bool { return true }

// IsRecording methods

func (m *IsRecording) Bool() Bool  { return Bool{m} }
func (m *IsRecording) Value() bool { return (*Model)(m).recording }
func (m *IsRecording) setValue(val bool) {
	m.recording = val
	m.instrEnlarged = val
	trySend(m.broker.ToPlayer, any(RecordingMsg{val}))
}
func (m *IsRecording) Enabled() bool { return true }

// Playing methods

func (m *Playing) Bool() Bool  { return Bool{m} }
func (m *Playing) Value() bool { return m.playing }
func (m *Playing) setValue(val bool) {
	m.playing = val
	if m.playing {
		(*Model)(m).setPanic(false)
		trySend(m.broker.ToPlayer, any(StartPlayMsg{m.d.Cursor.SongPos}))
	} else {
		trySend(m.broker.ToPlayer, any(IsPlayingMsg{val}))
	}
}
func (m *Playing) Enabled() bool { return m.playing || !m.instrEnlarged }

// InstrEnlarged methods

func (m *InstrEnlarged) Bool() Bool        { return Bool{m} }
func (m *InstrEnlarged) Value() bool       { return m.instrEnlarged }
func (m *InstrEnlarged) setValue(val bool) { m.instrEnlarged = val }
func (m *InstrEnlarged) Enabled() bool     { return true }

// CommentExpanded methods

func (m *CommentExpanded) Bool() Bool        { return Bool{m} }
func (m *CommentExpanded) Value() bool       { return m.commentExpanded }
func (m *CommentExpanded) setValue(val bool) { m.commentExpanded = val }
func (m *CommentExpanded) Enabled() bool     { return true }

// Follow methods

func (m *Follow) Bool() Bool        { return Bool{m} }
func (m *Follow) Value() bool       { return m.follow }
func (m *Follow) setValue(val bool) { m.follow = val }
func (m *Follow) Enabled() bool     { return true }

// TrackMidiIn (Midi Input for notes in the tracks)

func (m *TrackMidiIn) Bool() Bool        { return Bool{m} }
func (m *TrackMidiIn) Value() bool       { return m.trackMidiIn }
func (m *TrackMidiIn) setValue(val bool) { m.trackMidiIn = val }
func (m *TrackMidiIn) Enabled() bool     { return m.MIDI.HasDeviceOpen() }

// Effect methods

func (m *Effect) Bool() Bool { return Bool{m} }
func (m *Effect) Value() bool {
	if m.d.Cursor.Track < 0 || m.d.Cursor.Track >= len(m.d.Song.Score.Tracks) {
		return false
	}
	return m.d.Song.Score.Tracks[m.d.Cursor.Track].Effect
}
func (m *Effect) setValue(val bool) {
	if m.d.Cursor.Track < 0 || m.d.Cursor.Track >= len(m.d.Song.Score.Tracks) {
		return
	}
	m.d.Song.Score.Tracks[m.d.Cursor.Track].Effect = val
}
func (m *Effect) Enabled() bool { return true }

// UnitSearching methods

func (m *UnitSearching) Bool() Bool  { return Bool{m} }
func (m *UnitSearching) Value() bool { return m.d.UnitSearching }
func (m *UnitSearching) setValue(val bool) {
	m.d.UnitSearching = val
	if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
		m.d.UnitSearchString = ""
		return
	}
	if m.d.UnitIndex < 0 || m.d.UnitIndex >= len(m.d.Song.Patch[m.d.InstrIndex].Units) {
		m.d.UnitSearchString = ""
		return
	}
	m.d.UnitSearchString = m.d.Song.Patch[m.d.InstrIndex].Units[m.d.UnitIndex].Type
}
func (m *UnitSearching) Enabled() bool { return true }

// UnitDisabled methods

func (m *UnitDisabled) Bool() Bool { return Bool{m} }
func (m *UnitDisabled) Value() bool {
	if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
		return false
	}
	if m.d.UnitIndex < 0 || m.d.UnitIndex >= len(m.d.Song.Patch[m.d.InstrIndex].Units) {
		return false
	}
	return m.d.Song.Patch[m.d.InstrIndex].Units[m.d.UnitIndex].Disabled
}
func (m *UnitDisabled) setValue(val bool) {
	if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
		return
	}
	l := ((*Model)(m)).Units().List()
	r := l.listRange()
	defer (*Model)(m).change("UnitDisabledSet", PatchChange, MajorChange)()
	for i := r.Start; i < r.End; i++ {
		m.d.Song.Patch[m.d.InstrIndex].Units[i].Disabled = val
	}
}
func (m *UnitDisabled) Enabled() bool {
	if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
		return false
	}
	if len(m.d.Song.Patch[m.d.InstrIndex].Units) == 0 {
		return false
	}
	return true
}

// LoopToggle methods

func (m *LoopToggle) Bool() Bool  { return Bool{m} }
func (m *LoopToggle) Value() bool { return m.loop.Length > 0 }
func (t *LoopToggle) setValue(val bool) {
	m := (*Model)(t)
	newLoop := Loop{}
	if val {
		l := m.OrderRows().List()
		r := l.listRange()
		newLoop = Loop{r.Start, r.End - r.Start}
	}
	m.setLoop(newLoop)
}
func (m *LoopToggle) Enabled() bool { return true }

// UniquePatterns methods

func (m *UniquePatterns) Bool() Bool        { return Bool{m} }
func (m *UniquePatterns) Value() bool       { return m.uniquePatterns }
func (m *UniquePatterns) setValue(val bool) { m.uniquePatterns = val }
func (m *UniquePatterns) Enabled() bool     { return true }

// Mute methods

func (m *Mute) Bool() Bool { return Bool{m} }
func (m *Mute) Value() bool {
	if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
		return false
	}
	return m.d.Song.Patch[m.d.InstrIndex].Mute
}
func (m *Mute) setValue(val bool) {
	if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
		return
	}
	defer (*Model)(m).change("Mute", PatchChange, MinorChange)()
	a, b := min(m.d.InstrIndex, m.d.InstrIndex2), max(m.d.InstrIndex, m.d.InstrIndex2)
	for i := a; i <= b; i++ {
		if i < 0 || i >= len(m.d.Song.Patch) {
			continue
		}
		m.d.Song.Patch[i].Mute = val
	}
}
func (m *Mute) Enabled() bool { return m.d.InstrIndex >= 0 && m.d.InstrIndex < len(m.d.Song.Patch) }

// Solo methods

func (m *Solo) Bool() Bool { return Bool{m} }
func (m *Solo) Value() bool {
	a, b := min(m.d.InstrIndex, m.d.InstrIndex2), max(m.d.InstrIndex, m.d.InstrIndex2)
	for i := range m.d.Song.Patch {
		if i < 0 || i >= len(m.d.Song.Patch) {
			continue
		}
		if (i >= a && i <= b) == m.d.Song.Patch[i].Mute {
			return false
		}
	}
	return true
}
func (m *Solo) setValue(val bool) {
	defer (*Model)(m).change("Solo", PatchChange, MinorChange)()
	a, b := min(m.d.InstrIndex, m.d.InstrIndex2), max(m.d.InstrIndex, m.d.InstrIndex2)
	for i := range m.d.Song.Patch {
		if i < 0 || i >= len(m.d.Song.Patch) {
			continue
		}
		m.d.Song.Patch[i].Mute = !(i >= a && i <= b) && val
	}
}
func (m *Solo) Enabled() bool { return m.d.InstrIndex >= 0 && m.d.InstrIndex < len(m.d.Song.Patch) }

// LinkInstrTrack methods

func (m *LinkInstrTrack) Bool() Bool        { return Bool{m} }
func (m *LinkInstrTrack) Value() bool       { return m.linkInstrTrack }
func (m *LinkInstrTrack) setValue(val bool) { m.linkInstrTrack = val }
func (m *LinkInstrTrack) Enabled() bool     { return true }
