package model

import (
	"sort"
)

// SuggestionListener listens for suggestions.
type SuggestionListener interface {
	BuffWatcher

	// SuggestionChanged notifies suggestion changes.
	SuggestionChanged(text, sugg string)
}

// SuggestionFunc produces suggestions.
type SuggestionFunc func(text string) sort.StringSlice

// FishBuff represents a suggestion buffer.
type FishBuff struct {
	*CmdBuff

	suggestionFn    SuggestionFunc
	suggestions     []string
	suggestionIndex int
}

// NewFishBuff returns a new command buffer.
func NewFishBuff(key rune, kind BufferKind) *FishBuff {
	return &FishBuff{
		CmdBuff:         NewCmdBuff(key, kind),
		suggestionIndex: -1,
	}
}

// PrevSuggestion returns the prev suggestion.
func (f *FishBuff) PrevSuggestion() (string, bool) {
	if len(f.suggestions) == 0 {
		return "", false
	}

	if f.suggestionIndex < 0 {
		f.suggestionIndex = 0
	} else {
		f.suggestionIndex--
	}
	if f.suggestionIndex < 0 {
		f.suggestionIndex = len(f.suggestions) - 1
	}

	return f.suggestions[f.suggestionIndex], true
}

// NextSuggestion returns the next suggestion.
func (f *FishBuff) NextSuggestion() (string, bool) {
	if len(f.suggestions) == 0 {
		return "", false
	}

	if f.suggestionIndex < 0 {
		f.suggestionIndex = 0
	} else {
		f.suggestionIndex++
	}
	if f.suggestionIndex >= len(f.suggestions) {
		f.suggestionIndex = 0
	}

	return f.suggestions[f.suggestionIndex], true
}

// ClearSuggestions clear out all suggestions.
func (f *FishBuff) ClearSuggestions() {
	f.suggestionIndex = -1
}

// CurrentSuggestion returns the current suggestion.
func (f *FishBuff) CurrentSuggestion() (string, bool) {
	if len(f.suggestions) == 0 || f.suggestionIndex < 0 || f.suggestionIndex >= len(f.suggestions) {
		return "", false
	}

	return f.suggestions[f.suggestionIndex], true
}

// AutoSuggests returns true if model implements auto suggestions.
func (f *FishBuff) AutoSuggests() bool {
	return true
}

// Suggestions returns suggestions.
func (f *FishBuff) Suggestions() []string {
	if f.suggestionFn != nil {
		return f.suggestionFn(string(f.buff))
	}
	return nil
}

// SetSuggestionFn sets up suggestions.
func (f *FishBuff) SetSuggestionFn(fn SuggestionFunc) {
	f.suggestionFn = fn
}

// Notify publish suggestions to all listeners.
func (f *FishBuff) Notify() {
	if f.suggestionFn == nil {
		return
	}
	f.fireSuggestionChanged(f.suggestionFn(string(f.buff)))
}

// Add adds a new charater to the buffer.
func (f *FishBuff) Add(r rune) {
	f.CmdBuff.Add(r)
	f.Notify()
}

// Delete removes the last character from the buffer.
func (f *FishBuff) Delete() {
	f.CmdBuff.Delete()
	f.Notify()
}

func (f *FishBuff) fireSuggestionChanged(ss []string) {
	f.suggestions, f.suggestionIndex = ss, 0
	if len(ss) == 0 {
		f.suggestionIndex = -1
		return
	}

	for _, l := range f.listeners {
		if listener, ok := l.(SuggestionListener); ok {
			listener.SuggestionChanged(f.GetText(), ss[f.suggestionIndex])
		}
	}
}
