// +build !rm_basic_commands allcommands tabcompletion

package main

import (
	"fmt"
	"regexp"
	"strings"

	"samhofi.us/x/keybase"
)

var (
	tabSlice     []string
	commandSlice []string
)

// This defines the handleTab function thats called by key bindind tab for the input control.
func handleTab(viewName string) error {
	inputString, err := getInputString(viewName)
	if err != nil {
		return err
	}
	// if you successfully get an input string, grab the last word from the string
	ss := regexp.MustCompile(`[ #]`).Split(inputString, -1)
	s := ss[len(ss)-1]
	// create a variable in which to store the result
	var resultSlice []string
	// if the word starts with a : its an emoji lookup
	if strings.HasPrefix(s, ":") {
		resultSlice = getEmojiTabCompletionSlice(s)
	} else if strings.HasPrefix(s, "/") {
		generateCommandTabCompletionSlice()
		s = strings.Replace(s, "/", "", 1)
		resultSlice = getCommandTabCompletionSlice(s)
	} else {
		if strings.HasPrefix(s, "@") {
			// now in case the word (s) is a mention @something, lets remove it to normalize
			s = strings.Replace(s, "@", "", 1)
		}
		// now call get the list of all possible cantidates that have that as a prefix
		resultSlice = getChannelTabCompletionSlice(s)
	}
	rLen := len(resultSlice)
	lcp := longestCommonPrefix(resultSlice)
	if lcp != "" {
		originalViewTitle := getViewTitle("Input")
		newViewTitle := ""
		if rLen >= 1 && originalViewTitle != "" {
			if rLen == 1 {
				newViewTitle = originalViewTitle
			} else if rLen <= 5 {
				newViewTitle = fmt.Sprintf("%s|| %s", originalViewTitle, strings.Join(resultSlice, " "))
			} else if rLen > 5 {
				newViewTitle = fmt.Sprintf("%s|| %s +%d more", originalViewTitle, strings.Join(resultSlice[:6], " "), rLen-5)
			}
			setViewTitle(viewName, newViewTitle)
			remainder := stringRemainder(s, lcp)
			writeToView(viewName, remainder)
		}
	}

	return nil
}

// Main tab completion functions
func getEmojiTabCompletionSlice(inputWord string) []string {
	// use the emojiSlice from emojiList.go and filter it for the input word
	//resultSlice := filterStringSlice(emojiSlice, inputWord)
	resultSlice := filterEmojiMap(emojiMap, inputWord)
	return resultSlice
}
func getChannelTabCompletionSlice(inputWord string) []string {
	// use the tabSlice from above and filter it for the input word
	resultSlice := filterStringSlice(tabSlice, inputWord)
	return resultSlice
}
func getCommandTabCompletionSlice(inputWord string) []string {
	// use the commandSlice from above and filter it for the input word
	resultSlice := filterStringSlice(commandSlice, inputWord)
	return resultSlice
}

//Generator Functions (should be called externally when chat/list/join changes
func generateChannelTabCompletionSlice() {
	// fetch all members of the current channel and add them to the slice
	channelSlice := getCurrentChannelMembership()
	for _, m := range channelSlice {
		tabSlice = appendIfNotInSlice(tabSlice, m)
	}
}
func generateCommandTabCompletionSlice() {
	// get the maps of all built commands - this should only need to be done on startup
	// removing typeCommands for now, since they aren't actually commands you can type - contrary to the naming
	/*for commandString1 := range typeCommands {
		commandSlice = appendIfNotInSlice(commandSlice, commandString1)
	}*/
	for commandString2 := range commands {
		commandSlice = appendIfNotInSlice(commandSlice, commandString2)
	}
	for _, commandString3 := range baseCommands {
		commandSlice = appendIfNotInSlice(commandSlice, commandString3)
	}
}
func generateRecentTabCompletionSlice() {
	var recentSlice []string
	for _, s := range channels {
		if s.MembersType == keybase.TEAM {
			// its a team so add the topic name and channel name
			recentSlice = appendIfNotInSlice(recentSlice, s.TopicName)
			recentSlice = appendIfNotInSlice(recentSlice, s.Name)
		} else {
			//its a user, so clean the name and append
			recentSlice = appendIfNotInSlice(recentSlice, cleanChannelName(s.Name))
		}
	}
	for _, s := range recentSlice {
		tabSlice = appendIfNotInSlice(tabSlice, s)
	}
}

// Helper functions
func getCurrentChannelMembership() []string {
	var rs []string
	if channel.Name != "" {
		t := k.NewTeam(channel.Name)
		testVar, err := t.MemberList()
		if err != nil {
			return rs // then this isn't a team, its a PM or there was an error in the API call
		}
		for _, m := range testVar.Result.Members.Owners {
			rs = append(rs, fmt.Sprintf("%+v", m.Username))
		}
		for _, m := range testVar.Result.Members.Admins {
			rs = append(rs, fmt.Sprintf("%+v", m.Username))
		}
		for _, m := range testVar.Result.Members.Writers {
			rs = append(rs, fmt.Sprintf("%+v", m.Username))
		}
		for _, m := range testVar.Result.Members.Readers {
			rs = append(rs, fmt.Sprintf("%+v", m.Username))
		}

	}
	return rs
}
func filterStringSlice(ss []string, fv string) []string {
	var rs []string
	for _, s := range ss {
		if strings.HasPrefix(s, fv) {
			rs = append(rs, s)
		}
	}
	return rs
}
func filterEmojiMap(eMap map[string]emojiData, fv string) []string {
	var rs []string
	for k, _ := range eMap {
		if strings.HasPrefix(k, fv) {
			rs = append(rs, k)
		}
	}
	return rs
}
func longestCommonPrefix(ss []string) string {
	// cover the case where the slice has no or one members
	switch len(ss) {
	case 0:
		return ""
	case 1:
		return ss[0]
	}
	// all strings are compared by bytes here forward (TBD unicode normalization?)
	// establish min, max lenth members of the slice by iterating over the members
	min, max := ss[0], ss[0]
	for _, s := range ss[1:] {
		switch {
		case s < min:
			min = s
		case s > max:
			max = s
		}
	}
	// then iterate over the characters from min to max, as soon as chars don't match return
	for i := 0; i < len(min) && i < len(max); i++ {
		if min[i] != max[i] {
			return min[:i]
		}
	}
	// to cover the case where all members are equal, just return one
	return min
}
func stringRemainder(aStr, bStr string) string {
	var long, short string
	//figure out which string is longer
	switch {
	case len(aStr) < len(bStr):
		short = aStr
		long = bStr
	default:
		short = bStr
		long = aStr
	}
	// iterate over the strings using an external iterator so we don't lose the value
	i := 0
	for i < len(short) && i < len(long) {
		if short[i] != long[i] {
			// the strings aren't equal so don't return anything
			return ""
		}
		i++
	}
	// return whatever's left of the longer string
	return long[i:]
}
func appendIfNotInSlice(ss []string, s string) []string {
	for _, element := range ss {
		if element == s {
			return ss
		}
	}
	return append(ss, s)
}