package readline

import "strings"

// tokeniser - The input line must be splitted according to different rules (split between spaces, brackets, etc ?).
type tokeniser func(line []rune, cursorPos int) (split []string, index int, newPos int)

func tokeniseLine(line []rune, linePos int) ([]string, int, int) {
	if len(line) == 0 {
		return nil, 0, 0
	}

	var index, pos int
	var punc bool

	split := make([]string, 1)

	for i, r := range line {
		switch {
		case (r >= 33 && 47 >= r) ||
			(r >= 58 && 64 >= r) ||
			(r >= 91 && 94 >= r) ||
			r == 96 ||
			(r >= 123 && 126 >= r):

			if i > 0 && line[i-1] != r {
				split = append(split, "")
			}
			split[len(split)-1] += string(r)
			punc = true

		case r == ' ' || r == '\t':
			split[len(split)-1] += string(r)
			punc = true

		default:
			if punc {
				split = append(split, "")
			}
			split[len(split)-1] += string(r)
			punc = false
		}

		if i == linePos {
			index = len(split) - 1
			pos = len(split[index]) - 1
		}

	}

	// Hackish: if we are at the end of the line,
	// currently appending to it, we return the pos
	// as we would do when matching linePos
	if linePos == len(line) {
		if index == 0 {
			index = len(split) - 1
		}
		if pos == 0 {
			pos = len(split[index])
		}
	}

	return split, index, pos
}

func tokeniseSplitSpaces(line []rune, linePos int) ([]string, int, int) {
	if len(line) == 0 {
		return nil, 0, 0
	}

	var index, pos int
	split := make([]string, 1)

	for i, r := range line {
		switch {
		case r == ' ' || r == '\t':
			split[len(split)-1] += string(r)

		default:
			if i > 0 && (line[i-1] == ' ' || line[i-1] == '\t') {
				split = append(split, "")
			}
			split[len(split)-1] += string(r)
		}

		if i == linePos {
			index = len(split) - 1
			pos = len(split[index]) - 1
		}
	}

	return split, index, pos
}

func tokeniseBrackets(line []rune, linePos int) ([]string, int, int) {
	var (
		open, close    rune
		split          []string
		count          int
		pos            = make(map[int]int)
		match          int
		single, double bool
	)

	switch line[linePos] {
	case '(', ')':
		open = '('
		close = ')'

	case '{', '[':
		open = line[linePos]
		close = line[linePos] + 2

	case '}', ']':
		open = line[linePos] - 2
		close = line[linePos]

	default:
		return nil, 0, 0
	}

	for i := range line {
		switch line[i] {
		case '\'':
			if !single {
				double = !double
			}

		case '"':
			if !double {
				single = !single
			}

		case open:
			if !single && !double {
				count++
				pos[count] = i
				if i == linePos {
					match = count
					split = []string{string(line[:i-1])}
				}

			} else if i == linePos {
				return nil, 0, 0
			}

		case close:
			if !single && !double {
				if match == count {
					split = append(split, string(line[pos[count]:i]))
					return split, 1, 0
				}
				if i == linePos {
					split = []string{
						string(line[:pos[count]-1]),
						string(line[pos[count]:i]),
					}
					return split, 1, len(split[1])
				}
				count--

			} else if i == linePos {
				return nil, 0, 0
			}
		}
	}

	return nil, 0, 0
}

func rTrimWhiteSpace(oldString string) (newString string) {
	return strings.TrimRight(oldString, " ")
	// TODO: support tab chars
	/*defer fmt.Println(">" + oldString + "<" + newString + ">")
	newString = oldString
	for len(oldString) > 0 {
		if newString[len(newString)-1] == ' ' || newString[len(newString)-1] == '\t' {
			newString = newString[:len(newString)-1]
		} else {
			break
		}
	}
	return*/
}