package core

import (
	
	
	
	
	

	
	
	
	
)

// Tokenizer is a method used by a (line) type to split itself according to
// different rules (split between spaces, punctuation, brackets, quotes, etc.).
type Tokenizer func(cursorPos int) (split []string, index int, newPos int)

// Line is an input line buffer.
// Contains methods to search and modify its contents,
// split itself with tokenizers, and displaying itself.
type Line []rune

// Set replaces the line contents altogether with a new slice of characters.
// If no characters are passed, the line is thus made empty.
func ( *Line) ( ...rune) {
	* = 
}

// Insert inserts one or more runes at the given position.
// If the position is either negative or greater than the
// length of the line, nothing is inserted.
func ( *Line) ( int,  ...rune) {
	for {
		// I don't really understand why `0` is creeping in at the
		// end of the array but it only happens with unicode characters.
		if len() > 1 && [len()-1] == 0 {
			 = [:len()-1]
			continue
		}

		break
	}

	// Invalid position cancels the insertion
	if  < 0 ||  > .Len() {
		return
	}

	switch {
	case .Len() == 0:
		* = 
	case  < .Len():
		 := string((*)[:])
		 := string(append((*)[:], ...))
		 += 
		* = []rune()
	case  == .Len():
		* = append(*, ...)
	}
}

// InsertBetween inserts one or more runes into the line, between the specified
// begin and end position, effectively deleting everything in between those.
// If either or these positions is equal to -1, the selection content
// is inserted at the other position. If both are -1, nothing is done.
func ( *Line) (,  int,  ...rune) {
	, ,  := .checkRange(, )
	if ! {
		return
	}

	switch {
	case  == -1:
		.Insert(, ...)
	case  == .Len():
		 := string((*)[:]) + string()
		* = []rune()
	default:
		 := string((*)[:])
		 := string(append((*)[:], ...))
		 += 
		* = []rune()
	}
}

// Cut deletes a slice of runes between a beginning and end position on the line.
// If the begin/end pos is negative/greater than the line, all runes located on
// valid indexes in the given range are removed.
func ( *Line) (,  int) {
	, ,  := .checkRange(, )
	if ! {
		return
	}

	switch  {
	case -1:
		 := string((*)[:])
		* = []rune()
	default:
		 := string((*)[:])
		 := string((*)[:])
		 += 
		* = []rune()
	}
}

// CutRune deletes a rune at the given position in the line.
// If the position is out of bounds, nothing is deleted.
func ( *Line) ( int) {
	if  < 0 ||  > .Len() || .Len() == 0 {
		return
	}

	switch {
	case  == 0:
		* = (*)[1:]
	case  == .Len():
		* = (*)[:-1]
	default:
		 := string((*)[+1:])
		 := string((*)[:])
		 += 
		* = []rune()
	}
}

// Len returns the length of the line, as given by ut8.RuneCount.
// This should NOT confused with the length of the line in terms of
// how many terminal columns its printed representation will take.
func ( *Line) () int {
	return utf8.RuneCountInString(string(*))
}

// SelectWord returns the begin and end index positions of a word
// (separated by punctuation or spaces) around the specified position.
func ( *Line) ( int) (,  int) {
	if .Len() == 0 {
		return , 
	}

	 = .checkPosRange()
	if  == .Len() {
		--
	}

	 := regexp.MustCompile("[0-9a-zA-Z_]")
	,  = , 

	if  := .MatchString(string((*)[])); ! {
		 = regexp.MustCompile(`\s`)
	}

	// To first space found backward
	for ;  >= 0; -- {
		if  := .MatchString(string((*)[])); ! {
			break
		}
	}

	// And to first space found forward
	for ;  < .Len(); ++ {
		if  := .MatchString(string((*)[])); ! {
			break
		}
	}

	++

	// Ending position must be greater than 0
	if  > 0 {
		--
	}

	return , 
}

// SelectBlankWord returns the begin and end index positions
// of a full bigword (blank word) around the specified position.
func ( *Line) ( int) (,  int) {
	if .Len() == 0 {
		return , 
	}

	 = .checkPosRange()
	if  == .Len() {
		--
	}

	 := regexp.MustCompile(`[^\s]`)

	,  = , 

	if  := .MatchString(string((*)[])); ! {
		 = regexp.MustCompile(`\s`)
	}

	// To first space found backward
	for ;  >= 0; -- {
		 :=  > 0 && (*)[-1] == '\\'
		if  := .MatchString(string((*)[])); ! && ! {
			break
		}
	}

	// And to first space found forward
	for ;  < .Len(); ++ {
		 :=  > 0 && (*)[-1] == '\\'
		if  := .MatchString(string((*)[])); ! && ! {
			break
		}
	}

	++

	// Ending position must be greater than 0
	if  > 0 {
		--
	}

	return , 
}

// Find returns the index position of a target rune, or -1 if not found.
func ( *Line) ( rune,  int,  bool) int {
	if .Len() == 0 {
		return -1
	}

	 = .checkPosRange()

	for {
		if  {
			++
			if  > .Len()-1 {
				break
			}
		} else {
			--
			if  < 0 {
				break
			}
		}

		// Check if character matches
		if (*)[] ==  {
			return 
		}
	}

	// The rune was not found.
	return -1
}

// FindSurround returns the beginning and end positions of an enclosing rune (either
// matching signs -brackets- or the rune itself -quotes/letters-) and the enclosing chars.
func ( *Line) ( rune,  int) (,  int, ,  rune) {
	,  = strutil.MatchSurround()

	 = .Find(, +1, false)
	 = .Find(, -1, true)

	return
}

// SurroundQuotes returns the index positions of enclosing quotes around the given cursor
// position, provided that these quotes are really enclosing the inner selection (that is,
// that each of those quotes is not paired with another, outer quote).
// bpos or epos can be -1 if no quotes have been forward/backward found.
func ( *Line) ( bool,  int) (,  int) {
	var ,  rune

	if  {
		,  = '\'', '\''
	} else {
		,  = '"', '"'
	}

	// How many occurrences before and after cursor.
	var ,  int

	 = .Find(, +1, false)
	 = .Find(, , true)

	,  := , 

	for {
		if  != -1 {
			++
		}

		if  != -1 {
			++
		}

		// If one of the searches failed, we're done.
		if  == -1 ||  == -1 {
			break
		}

		// Or we use a new forward/backward reference pos.
		 = .Find(, , false)
		 = .Find(, , true)
	}

	// If there is an equal number of signs (like quotes) on each side,
	// that means we are not pointing at a word/phrase within quotes.
	if %2 == 0 && %2 == 0 {
		return -1, -1
	}

	// Or we possibly are (but not mandatorily: bpos/epos can be -1)
	return , 
}

// DisplayLine prints the line to stdout, starting at the current terminal
// cursor position, assuming it is at the end of the shell prompt string.
// Params:
// @indent -    Used to align all lines (except the first) together on a single column.
func ( *Line,  int) {
	 := strings.Split(string(*), "\n")

	if strings.HasSuffix(string(*), "\n") {
		 = append(, "")
	}

	for ,  := range  {
		// Don't let any visual selection go further than length.
		 += color.BgDefault

		// Clear everything before each line, except the first.
		if  > 0 {
			term.MoveCursorForwards()
			 = term.ClearLineBefore + 
		}

		// Clear everything after each line, except the last.
		if  < len()-1 {
			if len()+ < term.GetWidth() {
				 += term.ClearLineAfter
			}

			 += term.NewlineReturn
		}

		fmt.Print()
	}
}

// CoordinatesLine returns the number of real terminal lines on which the input line spans, considering
// any contained newlines, any overflowing line, and the indent passed as parameter. The values also
// take into account an eventual suggestion added to the line before printing.
// Params:
// @indent - Coordinates to align all lines (except the first) together on a single column.
// Returns:
// @x - The number of columns, starting from the terminal left, to the end of the last line.
// @y - The number of actual lines on which the line spans, accounting for line wrap.
func ( *Line,  int) (,  int) {
	 := string(*)
	 := strings.Split(, "\n")
	,  := 0, 0

	for ,  := range  {
		,  := strutil.LineSpan([]rune(), , )
		 += 
		 = 
	}

	return , 
}

// Lines returns the number of real lines in the input buffer.
// If there are no newlines, the result is 0, otherwise it's
// the number of lines - 1.
func ( *Line) () int {
	 := string(*)
	 := regexp.MustCompile(string(inputrc.Newline))
	 := .FindAllStringIndex(, -1)

	return len()
}

// Forward returns the offset to the beginning of the next
// (forward) token determined by the tokenizer function.
func ( *Line) ( Tokenizer,  int) ( int) {
	, ,  := ()

	switch {
	case len() == 0:
		return
	case +1 == len():
		 = .Len() - 
	default:
		 = len([]) - 
	}

	return
}

// ForwardEnd returns the offset to the end of the next
// (forward) token determined by the tokenizer function.
func ( *Line) ( Tokenizer,  int) ( int) {
	, ,  := ()
	if len() == 0 {
		return
	}

	 := strings.TrimRightFunc([], unicode.IsSpace)

	switch {
	case  == len()-1 &&  >= len()-1:
		return
	case  >= len()-1:
		 = strings.TrimRightFunc([+1], unicode.IsSpace)
		 = len([]) - 
		 += len() - 1
	default:
		 = len() -  - 1
	}

	return
}

// Backward returns the offset to the beginning position of the previous
// (backward) token determined by the tokenizer function.
func ( *Line) ( Tokenizer,  int) ( int) {
	, ,  := ()

	switch {
	case len() == 0:
		return
	case  == 0 &&  == 0:
		return
	case  == 0:
		 = len([-1])
	default:
		 = 
	}

	return  * -1
}

// Tokenize splits the line on each word, that is, split on every punctuation or space.
func ( *Line) ( int) ([]string, int, int) {
	 := *

	if .Len() == 0 {
		return nil, 0, 0
	}

	 = .checkPosRange()

	var ,  int
	var  bool

	 := make([]string, 1)

	for ,  := range  {
		switch {
		case unicode.IsPunct():
			if  > 0 && [-1] !=  {
				 = append(, "")
			}

			[len()-1] += string()
			 = true

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

		case  == '\n':
			// Newlines are a word of their own only
			// when the last rune of the previous word
			// is one as well.
			if  > 0 && [-1] ==  {
				 = append(, "")
			}

			[len()-1] += string()
			 = true

		default:
			if  {
				 = append(, "")
			}

			[len()-1] += string()
			 = false
		}

		// Not caught when we are appending to the end
		// of the line, where rl.pos = linePos + 1, so...
		if  ==  {
			 = len() - 1
			 = len([]) - 1
		}
	}

	// ... so we adjust here for this case.
	if  == len() {
		 = len() - 1
		 = len([])
	}

	return , , 
}

// TokenizeSpace splits the line on each WORD (blank word), that is, split on every space.
func ( *Line) ( int) ([]string, int, int) {
	 := *

	if .Len() == 0 {
		return nil, 0, 0
	}

	 = .checkPosRange()

	var ,  int
	 := make([]string, 1)
	var  bool

	for ,  := range  {
		switch  {
		case ' ', '\t':
			[len()-1] += string()
			 = false

		case '\n':
			// Newlines are a word of their own only
			// when the last rune of the previous word
			// is one as well.
			if  > 0 && [-1] ==  {
				 = append(, "")
			}

			[len()-1] += string()
			 = true

		default:
			if ( > 0 && ([-1] == ' ' || [-1] == '\t')) ||  {
				 = append(, "")
			}

			 = false
			[len()-1] += string()
		}

		// Not caught when we are appending to the end
		// of the line, where rl.pos = linePos + 1, so...
		if  ==  {
			 = len() - 1
			 = len([]) - 1
		}
	}

	// ... so we adjust here for this case.
	if  == len() {
		 = len() - 1
		 = len([])
	}

	return , , 
}

// TokenizeBlock splits the line into arguments delimited either by
// brackets, braces and parenthesis, and/or single and double quotes.
func ( *Line) ( int) ([]string, int, int) {
	 := *

	if .Len() == 0 {
		return nil, 0, 0
	}

	 = .checkPosRange()
	if  == .Len() {
		--
	}

	var (
		,  rune
		          []string
		          int
		            = make(map[int]int)
		          int
		,  bool
	)

	switch [] {
	case '(', ')', '{', '[', '}', ']':
		,  = strutil.MatchSurround([])

	default:
		return nil, 0, 0
	}

	for  := range  {
		switch [] {
		case '\'':
			if ! {
				 = !
			}

		case '"':
			if ! {
				 = !
			}

		case :
			if ! && ! {
				, ,  = openToken(, , , , , , )
			} else if  ==  {
				return nil, 0, 0
			}

		case :
			if ! && ! {
				,  = closeToken(, , , , , , )

				if  ==  {
					return , 1, 0
				} else if  ==  {
					return , 1, len([1])
				}
			} else if  ==  {
				return nil, 0, 0
			}
		}
	}

	return nil, 0, 0
}

// add a new block token to the list of split tokens.
func openToken(, , ,  int,  map[int]int,  []rune,  []string) (int, int, []string) {
	++

	[] = 

	if  !=  {
		return , , 
	}

	// Important: don't index a negative below.
	if  == 0 {
		++
	}

	 = 
	 = []string{string([:-1])}

	return , , 
}

// close the current block token if any.
func closeToken(, , ,  int,  map[int]int,  []rune,  []string) (int, []string) {
	if  ==  {
		 = append(, string([[]:]))
		return , 
	}

	if  ==  {
		 := []
		if  == 0 {
			++
		}

		 = []string{
			string([:-1]),
			string([[]:]),
		}

		return , 
	}

	--

	return , 
}

// newlines gives the indexes of all newline characters in the line.
func ( *Line) () [][]int {
	 := string(*)
	 += string(inputrc.Newline)
	 := regexp.MustCompile(string(inputrc.Newline))

	return .FindAllStringIndex(, -1)
}

// returns bpos, epos ordered and true if either is valid.
func ( *Line) (,  int) (int, int, bool) {
	if  == -1 &&  == -1 {
		return -1, -1, false
	}

	// Check positions out of bound
	if  > .Len() {
		 = .Len()
	}

	if  < 0 {
		 = 0
	}

	// Order begin and end pos
	if  > -1 &&  <  {
		,  = , 
	}

	return , , true
}

// similar to checkPos, but won't fail: will bring
// the position back onto a valid index on the line.
func ( *Line) ( int) int {
	if  < 0 {
		return 0
	}

	if  > .Len() {
		return .Len()
	}

	return 
}